Sql 用于从3个表检索数据的查询
我是SQL的新手,我对查询有疑问 我有三张桌子:Sql 用于从3个表检索数据的查询,sql,oracle,plsql,Sql,Oracle,Plsql,我是SQL的新手,我对查询有疑问 我有三张桌子: 具有cons\u id\u no、key\u id字段的消费者 bm_bill,包含关键字id、票据id号、应付金额、票据日期字段 (它将包含消费者的所有账单金额和日期) mreceipt,其中包含字段key\u id、收据号、支付金额、罚款、支付日期(It) 将包含消费者的所有付款详细信息) consumer表与bm_bill和mreceipt具有一对多关系。 我想根据消费者的cons_id_no创建消费者的分类账信息。它应该 包含其证件号、钥
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_账单中检索所有密钥id和账单日期,在bm_账单中,我只需要特定密钥id的信息。最重要的是,我的解决方案中还有一个类似的查询
因此,我的问题是:有没有更好的方法来做到这一点?1-我建议您使用
命令使用DESC
,并使用TOP
函数而不是MAX
,对数据进行分组;另外,我将使用中的X(…)
,而不是左连接
2-我不知道你的数据有多大,但如果你有上千万行,那么将分组结果存储在临时索引表中可能会缩短处理时间
3-您需要多长时间运行此查询(新鲜度级别)也是相关的:如果您不需要过去几分钟的最新结果(而是当天的结果),那么一定要使用临时存储,您可以将其用于当天的所有查询。您说过,按原样查询会提供所需的结果。如果是这样的话,请考虑以下内容:
您正在加入一个派生表(maxBillDate),其目的是什么?您没有将联接用作筛选器或表中的任何字段。所以…去掉它,重新执行查询,你应该看到没有它你会得到相同的结果
…与派生表相同的存储:maxReceipt
从这里开始,扔掉不必要的行李,看看你是怎么想的。你要加入两个表格(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“alternative”替代SQLServerOUTER APPLY
——创建两个函数,分别返回MAX(账单日期)
和MAX(付款日期)
,然后加入它们。好问题!这是一个棘手的问题,因为它需要像行数
或秩
这样的分析函数,但这两个外部连接使事情变得复杂<代码>密级
成功了。以下是我的想法:
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
我在以下条件下进行了测试:
bm_bill有多个相关行,mreceipt没有相关行
mreceipt有多个相关行,bm_bill没有行
bm_bill和mreceipt有相关行
在每一种情况下,它都是有效的,但是像这样彻底的重写,您将需要进行更多的测试
还要注意的是,为了解决这个问题,我重点关注了日期和ID,然后在后面添加了所有其他列。检查列列表中是否有键入错误或遗漏-我在键入所有内容后进行了快速检查,但可能遗漏了一些内容。@@hadinbe Oracle中没有TOP函数-检查问题标记。订购通常会降低性能…@Art:谢谢您的反馈!我不知道Oracle没有此功能。我可能不正确,但我在consumer和bm_bill表之间有一对多关系,在mreceipt表中也有一对多关系。我需要基于max(账单日期)和max(付款日期)的数据,这就是我添加它的原因。@kaushik——好的,但我的意思是,你没有使用它。因此,如果您需要它,那么您需要将其中的字段添加到SELECT子句中。您不是从bm_bill和mreceipt中检索所有数据吗?@kaushik:是的,但每个数据只检索一次(而不是原始查询中的两次)。今天,我学习了left EXTER join和partition by。使用这些东西,我也得到了你提出的同样的问题。我们可以在派生表中使用“c.key\u id”吗?然后我可以将检索到的行数减少到最小。@kaushik:我相信您不能直接这样做(据我所知,Oracle不直接支持SQLServerOUTER APPLY
),但您可以使left join
返回对象类型的函数的每个结果都与之相关,使用你的提示。