我在使用左连接和过滤器的 select 查询中做错了什么?

What am I doing wrong in this select query with left join and filters?

使用 Actian PSQL v13:

我正在尝试连接两个 table 并按一些条件过滤结果集。一个 table 是客户销售历史记录。另一个 table 将客户与购买组相关联。并非所有客户都在购买组中,因此我正在执行左连接以获得所有可能的销售历史结果。我不希望结果包括购买组 'SALREP' 中的任何人,但我确实希望看到不在任何购买组中的客户。此外,所谓的部分 'FREIGHT' 正在从结果中过滤掉。

我遇到的问题是,当我过滤掉 'SALREP' 组时,只有购​​买组中的客户被 selected;所有不在购买组中的客户都丢失了。

我通过注释掉 'SALREP' 的筛选器对此进行了测试,结果集确实包括不在购买组中的客户。我还尝试使用不同的过滤器方法,以防 Actian PSQL 挑剔。例如,我尝试了"not equals"的不同方法,如!=not()<>;问题仍然存在。另外,我用 left joinleft outer join 遇到了同样的问题。

这里是查询:

select T2.GROUP_CUST, T1.DATE_INVOICE, T1.SALESPERSON, T1.CUSTOMER, T1.PRODUCT_LINE, T1.PART, T1.DESCRIPTION, T1.QTY_SHIPPED, T1.EXTENSION 
from ORDER_HIST_LINE T1 
left join BUYING_GROUP T2 on T1.CUSTOMER=T2.CUSTOMER 
where DATE_INVOICE > '2019-06-13' and PART != 'FREIGHT'
and T2.GROUP_CUST != 'SALREP' 
and T1.CUSTOMER in ('ABC', 'DEF', 'GHI')
order by T1.CUSTOMER;

预期结果集应包括 2019 年 6 月 13 日之后开具发票的所有内容,不包括名为 'FREIGHT' 的部分,并且在名为 'SALREP' 的组中没有客户。但是,实际的结果集是不完整的。例如:

CUSTOMER | GROUP_CUST | DATE_INVOICE  | PART   | etc.
-----------------------------------------------------
ABC      |  A12       |  2019-06-14   | WIDGET
DEF      |  A12       |  2019-06-14   | GEAR

基本上,所有不属于任何购买组的客户都被排除在外。

注释掉and T2.GROUP_CUST != 'SALREP'部分,得到预期结果。例如:

CUSTOMER | GROUP_CUST | DATE_INVOICE  | PART   | etc.
-----------------------------------------------------
ABC      |  A12       |  2019-06-14   | WIDGET
DEF      |  A12       |  2019-06-14   | GEAR
GHI      |            |  2019-06-15   | WIDGET

我正在考虑创建到 select 查询的左连接,该查询首先从购买组中删除 'SALREP',但这不允许结果集识别和删除该组中的任何人。例如:left join (select * from BUYING_GROUP where GROUP_CUST != 'SALREP') T2

8/7/19 进一步尝试: 我在 MySQL v5.0.12 上发现了同样的困境。我可以 left join tables 产生不匹配的结果。我可以在那些不匹配的结果上过滤左边的 table 而不会意外丢失任何东西。然而,如果不让所有不匹配的行都消失,我无法对那些不匹配的结果进行正确的 table 过滤。

需要在ON子句中的secondtable加上条件。唯一明显的参考是 T2.GROUP_CUST,但它可能也适用于 DATE_INVOICEPART

from ORDER_HIST_LINE T1 left join
     BUYING_GROUP T2
     on T1.CUSTOMER = T2.CUSTOMER and
        T2.GROUP_CUST <> 'SALREP'
where DATE_INVOICE > '2019-06-13' and
      PART <> 'FREIGHT' and
      T1.CUSTOMER in ('ABC', 'DEF', 'GHI')
order by T1.CUSTOMER;

感谢 https://mode.com/resources/sql-tutorial/sql-joins-where-vs-on/。文章提到,"filtering in the WHERE clause can also filter null values, so we added an extra line to make sure to include the nulls."

这让我测试并意识到,当针对右侧 table 进行过滤时,有必要明确声明要包括该过滤列的 NULL 值(不匹配的行),否则那些不匹配的行从结果集中删除。我还发现有必要将过滤器对放在括号或结果集 "explodes" 中。我测试并发现只有在对右侧 table 进行过滤时才需要这样做;过滤左侧 table 没有这个问题(很明显)。

最终答案:

select T2.GROUP_CUST, T1.DATE_INVOICE, T1.SALESPERSON, T1.CUSTOMER, T1.PRODUCT_LINE,             
T1.PART, T1.DESCRIPTION, T1.QTY_SHIPPED, T1.EXTENSION 
from ORDER_HIST_LINE T1 
left join BUYING_GROUP T2 on T1.CUSTOMER=T2.CUSTOMER 
where DATE_INVOICE > '2019-06-13' and PART != 'FREIGHT' 
and (T2.GROUP_CUST != 'SALREP' or T2.GROUP_CUST is null) 
and T1.CUSTOMER in ('ABC', 'DEF', 'GHI') 
order by T1.CUSTOMER;

注意行 and (T2.GROUP_CUST != 'SALREP' or T2.GROUP_CUST is null).