0

我是 SQL 的新手,我对查询有疑问。

我有三张桌子:

  1. 具有字段 cons_id_no、key_id 的消费者
  2. bm_bill 具有字段 key_id、bill_id_no、amt_payable、bill_date(它将包含消费者的所有账单金额和日期)
  3. mreceipt 具有字段 key_id、receipt_no、amt_paid、fine、pay_date(它将包含消费者的所有付款详细信息)

消费者表与 bm_bill 和 mreceipt 是一对多的关系。我想根据消费者的 cons_id_no 创建分类帐信息。它应该包含他的 cons_id_no、key_id、bill_id_no(最新)、bill_date(最新)、amt_payable(最新)、receipt_no(最新)、amt_paid(最新)、fine(最新)、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 的信息。最重要的是,我的解决方案中还有一个这样的查询。

因此我的问题是:有没有更好的方法来做到这一点?

4

4 回答 4

2

您将加入 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 来解决它- 您创建 2 个分别返回和的OUTER APPLY函数,然后加入它们。MAX(bill_date)MAX(pay_date)

于 2013-03-22T16:56:19.320 回答
0

1-而不是使用MAX我会建议你ORDER使用你的数据DESC,并GROUP使用TOP函数而不是MAX; 也代替 aLEFT JOIN我会使用 a WHERE X IN (...)

2-我不知道您的数据有多大,但如果您的数据有数十万行,那么将分组结果存储在临时索引表中可能会缩短您的处理时间。

3-此外,您需要多久运行一次此查询(新鲜度)是相关的:如果您不需要过去几分钟的最新结果(而是当天的结果),那么一定要使用临时存储,您可以将它用于一天中的所有查询。

于 2013-03-22T16:38:20.657 回答
0

好问题!这是一个硬汉,因为它需要像ROW_NUMBERor这样的分析函数,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

我在以下条件下对此进行了测试:

  1. bm_bill 有多个相关行,而 mreceipt 没有相关行
  2. mreceipt 有多个相关行,而 bm_bill 没有行
  3. bm_bill 和 mreceipt 有相关的行

在每种情况下,它都有效,但是通过像这样的彻底重写,您将需要进行更多测试。

另请注意,我专注于日期和 ID 来解决这个问题,然后添加了所有其他列。检查列列表中是否存在拼写错误或遗漏 - 我在输入所有内容后进行了快速检查,但我可能遗漏了一些东西。

于 2013-03-22T17:42:31.727 回答
0

你说查询原样给你想要的结果。如果是这样,那么请考虑以下几点:

您要加入派生表 (maxBillDate),目的是什么?您没有将联接用作过滤器或表中的任何字段。所以......摆脱它,重新执行你的查询,你应该看到没有它你会得到相同的结果。

...与您的派生表相同的商店:maxReceipt

从那里开始,摆脱不必要的包袱,看看你的想法。

于 2013-03-22T16:49:34.533 回答