我是SQL的新手,我对查询有疑问。
我有三张桌子:
消费者表与bm_bill和mreceipt有一对多的关系。 我想根据他的cons_id_no创建消费者的分类帐信息。这应该 包含他的cons_id_no,key_id,bill_id_no(最新),bill_date(最新),amt_payable(最新),receipt_no(最新),amt_paid(最新),罚款(最新),pay_date(最新) 为此,我创建了以下查询
SELECT
c.key_id,
c.cons_id_no consumerid,
b.bill_id_no billno,
TO_CHAR(b.bill_date,'dd-Mon-YYYY') billdate,
b.amt_payable,
m.receipt_no receiptno,
TO_CHAR(m.pay_date,'dd-Mon-YYYY') paydate,
m.amt_paid+m.fine amountpaid
FROM
consumer c
LEFT OUTER JOIN (SELECT key_id, MAX(bill_date) AS maxDate FROM bm_bill GROUP BY key_id) maxBillDate
ON (maxBillDate.key_id = c.key_id)
LEFT OUTER JOIN bm_bill b
ON (b.key_id = c.key_id AND b.bill_date = maxBillDate.maxDate)
LEFT OUTER JOIN (SELECT key_id, MAX(pay_date) AS maxPayDate FROM mreceipt GROUP BY key_id) maxMReceipt
ON (maxMReceipt.key_id = c.key_id)
LEFT OUTER JOIN mreceipt m
ON (m.key_id = c.key_id AND m.pay_date = maxMReceipt.maxPayDate)
WHERE
c.cons_id_no='?';
我执行了查询,它给了我想要的结果。然后我注意到查询太慢了,发现在我的解决方案中我有:
SELECT key_id, max(bill_date) AS maxDate FROM bm_bill GROUP BY key_id
从bm_bill中检索所有key_ids和bill_dates,我只需要特定key_id的信息。最重要的是,在我的解决方案中,我还有一个这样的查询。
因此我的问题是:有没有更好的方法来做到这一点?
答案 0 :(得分:2)
您将连接两个表(bm_bill和mreceipt)中的每一个。我要尝试的第一件事是更改您的查询以避免双连接,看看它是否有所不同,如:
SELECT
c.key_id,
c.cons_id_no consumerid,
b.bill_id_no billno,
TO_CHAR(b.bill_date,'dd-Mon-YYYY') billdate,
b.amt_payable,
m.receipt_no receiptno,
TO_CHAR(m.pay_date,'dd-Mon-YYYY') paydate,
m.amt_paid+m.fine amountpaid
FROM
consumer c
LEFT JOIN (SELECT key_id,
bill_id_no, bill_date,amt_payable,receipt_no receiptno ,
ROW_NUMBER() OVER (PARTITION BY key_id ORDER BY bill_date DESC) as rn
FROM bm_bill)b ON (b.key_id = c.key_id and b.rn =1)
LEFT JOIN (SELECT key_id,
pay_date , amt_paid, amt_paid, fine,
ROW_NUMBER() OVER (PARTITION BY key_id ORDER BY pay_date DESC) as rn
FROM mreceipt) m ON (m.key_id = c.key_id and m.rn =1)
WHERE
c.cons_id_no='?';
如果效果不好,您可以使用Oracle“替代”SQLServer OUTER APPLY
- 创建2个分别返回MAX(bill_date)
和MAX(pay_date)
的函数,并加入它们。
答案 1 :(得分:0)
1-而不是使用MAX
我会建议您使用ORDER
DESC
使用GROUP
使用TOP
函数而不是MAX
LEFT JOIN
{1}};而不是WHERE X IN (...)
我会使用{{1}}。
2-我不知道您的数据有多大,但如果您的数据是数百万行,那么将分组结果存储在临时表和索引表中可能会缩短您的处理时间。
3-此外,您需要多长时间运行此查询(新鲜程度)是相关的:如果您不需要过去几分钟的最新结果(而是当天的结果),那么一定要使用临时存储,您可以将它用于当天的所有查询。
答案 2 :(得分:0)
你说查询as-is会给你想要的结果。如果是这样,请考虑以下事项:
您正在加入派生表(maxBillDate),目的是什么?您没有将联接用作过滤器或表中的任何字段。所以...摆脱它,重新执行你的查询,你应该看到没有它你会得到相同的结果。
...同一家商店你的派生表:maxReceipt
从那里开始,摆脱不必要的行李,看看你的想法。
答案 3 :(得分:0)
好问题!这是一个艰难的因为它为ROW_NUMBER
或RANK
这样的分析函数而哭泣,但两个外连接复杂化了。 DENSE_RANK
虽然做到了这一点。这就是我想出的:
select * from (
select
c.key_id,
c.cons_id_no consumerid,
to_char(m.pay_date, 'dd-Mon-YYYY') paydate,
m.receipt_no receiptno,
m.amt_paid+m.fine amountpaid,
dense_rank() over
(order by m.pay_date desc nulls last) as paydaterank,
to_char(b.bill_date, 'dd-Mon-YYYY') billdate,
b.bill_id_no billno,
b.amt_payable,
dense_rank() over
(order by b.bill_date desc nulls last) as billdaterank
from customer c
left outer join mreceipt m on c.key_id = m.key_id
left outer join bm_bill b on c.key_id = b.key_id
where c.cons_id_no = '?'
)
where paydaterank = 1 and billdaterank = 1
我在以下条件下对此进行了测试:
在每种情况下它都有效,但如果像这样彻底改写,你会想要测试更多。
另请注意,我专注于日期和ID来解决这个问题,然后再添加所有其他列。检查列表中的拼写错误或遗漏 - 我在输入所有内容后进行了快速检查,但我可能错过了一些内容。