LEFT Join PSQL where AND parameter is on another table

LEFT Join PSQL where the AND parameter is on another table

我有一个 PSQL 查询,其中左侧的 AND 参数加入另一个 table 检查下面的 table 示例。

发票

 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

还有另一个 table 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' table,其中 'product' 字段是相似的)当天(在 INVOICES table 上找到)。但是当我 运行 这个查询时,它列出了所有 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 |           

您收到大量 NULL 值,根据您的查询,这似乎是预期的结果。

这似乎是 "INNER" 连接类型的问题,而不是 "left" 连接问题。左连接将保留结果集中没有产品发票的所有实例。

你也可以将关于 "DATE" 的整个查询移动到 having 子句中(个人而言,我更喜欢 WHERE/Having 中的子查询,因为我觉得逻辑更清晰):

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;

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

像这样:

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; 

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

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

解决方法:使用case语句判断invoice项是否匹配table:

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;