LEFT加入PSQL,其中AND参数在另一个表上

时间:2016-08-09 12:58:14

标签: sql postgresql left-join

我有一个PSQL查询,左边的AND参数在下面的另一个表检查表示例中加入。

发票

 id  | account |      invoice_date       | reference | total_amount | status 
-----+---------+-------------------------+-----------+--------------+--------
 164 |     100 | 2016-08-03 03:05:08.996 |       161 |      2000.00 |       
 165 |     100 | 2016-08-03 21:42:07.865 |       164 |            0 |       
 167 |     100 | 2016-08-03 22:56:41.731 |       166 |       100.00 |       
 168 |     100 | 1970-01-01 00:33:20     |       161 |          200 |       
 169 |     100 | 2016-08-08 00:00:00     |       161 |          200 |  

Invoice_items

 id  | invoice | invoice_item_type | product | quantity | unit_price | reference | amount  
-----+---------+-------------------+---------+----------+------------+-----------+---------
 143 |     164 |                 1 |       6 |          |            |       161 | 2000.00
 144 |     165 |                 1 |      11 |          |            |       164 |       0
 145 |     167 |                 1 |       8 |          |            |       166 |  100.00

还有另一个表PRODUCTS,但唯一相关的字段是id

这是我的查询

select products.id, sum(invoice_items.amount) as total_revenue
from products
    left join invoice_items on invoice_items.product = products.id
    left join invoices on invoice_items.invoice = invoices.id
                      and invoices.invoice_date= current_date
group by products.id;

我需要查询的内容是列出所有产品ID,并在total_revenue列中,列出产品的销售总额(在invoice_items表中添加'amount',其中'product'字段类似)为当天(在INVOICES表中找到)。但是当我运行此查询时,它会列出产品的所有total_amounts。我错过了什么?

示例输出。 8,6和11必须为空

id  | total_revenue 
-----+---------------
 125 |              
 154 |              
 119 |              
 129 |              
   8 |        100.00
 112 |              
   5 |              
 132 |              
 104 |              
 113 |              
 143 |              
 152 |              
 121 |              
 127 |              
 165 |              
 139 |              
 146 |              
  15 |              
   2 |              
 147 |              
 149 |              
 166 |              
 169 |              
  13 |              
 106 |              
 122 |              
   9 |              
  11 |             0
 110 |              
 120 |              
 130 |              
 155 |              
 134 |              
 136 |              
 101 |              
 168 |              
 131 |              
 157 |              
 161 |              
 103 |              
 150 |              
 159 |              
 107 |              
 108 |              
 145 |              
   4 |              
  12 |              
 158 |              
 167 |              
 138 |              
 162 |              
 100 |              
 156 |              
 163 |              
 124 |              
 123 |              
 109 |              
 153 |              
 102 |              
 105 |              
 151 |              
 116 |              
 133 |              
 140 |              
 160 |              
 148 |              
 126 |              
 141 |              
   7 |              
 118 |              
  10 |              
 164 |              
 128 |              
  14 |              
 144 |              
 135 |              
   1 |              
   6 |       2000.00
   3 |              
 137 |              
 117 |              
 142 |              
 111 |           

3 个答案:

答案 0 :(得分:0)

您将获得大量NULL值,这似乎是基于您的查询所预期的。

这似乎是一个“内部”连接类型的问题而不是“左”连接。左连接将保留结果集中没有产品发票的所有实例。

您还可以将关于“DATE”的整个查询移动到having子句中(就个人而言,我更喜欢WHERE /中的子查询,因为我发现逻辑更清晰):

SELECT products.id, SUM(invoice_items.amount) AS total_revenue
FROM products
    INNER JOIN invoice_items ON invoice_items.product = products.id
HAVING EXISTS (SELECT 1 FROM invoices WHERE invoice_items.invoice = invoices.id
                      AND invoices.invoice_date= current_date)
GROUP BY products.id;

答案 1 :(得分:0)

日期约束只过滤掉发票表中的记录,而您还需要它来过滤出invoice_items表中的记录 - 但它没有这样做,因为它们都是左连接。派生表将轻松解决此问题,并提供您想要的结果。我还添加了一些表别名,以简洁和可读。

像这样:

SELECT 
    p.id, SUM(inv.amount) AS total_revenue

FROM 
    products p  LEFT JOIN 

    (SELECT 
        ii.product, i.invoice_date, ii.amount 
     FROM 
        invoice_items ii JOIN
        invoices i ON 
            ii.invoice = i.id) inv ON 
            inv.product = p.id AND
            inv.invoice_date= current_date

GROUP BY p.id; 

答案 2 :(得分:0)

我明白了。具有所需结果的较小样本数据将有所帮助。

问题解释起来有点复杂。无论日期如何,left join都会保留所有产品和发票。最终加入invoices会引入仅在当前日期匹配的发票。但是,因为您在第二个表上进行求和,所以每个匹配的行都会出现一次(即使当天没有发票项目),它也会出现在您的结果中。

解决方案:使用case语句确定invoice项目表中是否存在匹配项:

select p.id,
       sum(ii.amount * (case when i.id is not null then 1 end)) as total_revenue
from products p left join
     invoice_items ii
     on ii.product = p.id left join
     invoices i
     on ii.invoice = i.id and i.invoice_date = current_date
group by p.id;

我还怀疑该日期的正确条件是:

     on ii.invoice = i.id and i.invoice_date >= current_date and
        i.invoice_date < current_date + interval '1 day'

此外,将此作为子查询编写可以保存外部聚合,还应该解决问题:

select p.*,
       (select sum(ii.amount)
        from invoice_items ii join
             invoices i
             on ii.invoice = i.id and i.invoice_date >= current_date and
                i.invoice_date < current_date + interval '1 day'
        where ii.product = p.id
       ) as total_revenue
from products p;