查询从3个表中检索数据

时间:2013-03-22 16:20:57

标签: sql oracle plsql

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

我有三张桌子:

  1. 具有字段cons_id_no,key_id
  2. 的消费者
  3. bm_bill,其中包含字段key_id,bill_id_no,amt_payable,bill_date (它将包含所有账单金额和消费者的日期)
  4. mreceipt,其中包含字段key_id,receipt_no,amt_paid,fine,pay_date(It 将包含消费者的所有付款细节)
  5. 消费者表与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的信息。最重要的是,在我的解决方案中,我还有一个这样的查询。

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

4 个答案:

答案 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_NUMBERRANK这样的分析函数而哭泣,但两个外连接复杂化了。 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有相关的行
  4. 在每种情况下它都有效,但如果像这样彻底改写,你会想要测试更多。

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