Что я делаю неправильно в этом запросе выбора с левым соединением и фильтрами?

С Actian PSQL v13:

Я пытаюсь объединить две таблицы и отфильтровать набор результатов по нескольким критериям. Одна таблица - история продаж клиентов. Другая таблица связывает клиентов с группами покупок. Не все клиенты входят в группу покупателей, поэтому я делаю объединение влево, чтобы получить все возможные результаты по истории продаж. Я НЕ хочу, чтобы результаты включали кого-либо в группу покупок 'SALREP', но я хочу видеть клиентов, которые не входят ни в одну из групп покупок. Кроме того, так называемая часть "FREIGHT" отфильтровывается из результатов.

У меня проблема в том, что когда я отфильтровываю группу "SALREP", выбираются только те клиенты, которые входят в группу покупок; все клиенты, не входящие в группу, отсутствуют.

Я проверил это, закомментировав фильтр для "SALREP", и набор результатов действительно включает клиентов, которые не входят в группу покупателей. Я также пытался использовать другой подход фильтра, на случай, если Actian PSQL требователен. Например, я пробовал разные методы "не равных", такие как !=, not() а также <>; проблема сохраняется. Кроме того, я использовал left join а также left 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-06-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

Я думал о создании левого соединения с запросом выбора, который в первую очередь удаляет SALREP из групп покупок, но не позволяет результирующему набору идентифицировать и удалить кого-либо из этой группы. Напр.: left join (select * from BUYING_GROUP where GROUP_CUST != 'SALREP') T2

7/19/19 Дальнейшие попытки: я нахожу ту же дилемму на MySQL v5.0.12. Я могу покинуть таблицы соединений, чтобы получить несоответствующие результаты. Я могу отфильтровать левую таблицу по этим несоответствующим результатам, ничего не потеряв. Тем не менее, я не могу отфильтровать правильную таблицу по этим несовпадающим результатам без исчезновения всех несовпадающих строк.

2 ответа

Решение

Спасибо https://mode.com/resources/sql-tutorial/sql-joins-where-vs-on/. В статье упоминается, что "фильтрация в предложении WHERE также может фильтровать нулевые значения, поэтому мы добавили дополнительную строку, чтобы убедиться, что в нее включены нулевые значения".

Это привело меня к тестированию и осознанию того, что при фильтрации по правой таблице необходимо явно указать, что значения NULL (несопоставленные строки) для этого отфильтрованного столбца должны быть включены, в противном случае эти несопоставленные строки удаляются из результирующего набора. Я также обнаружил, что необходимо заключить пару фильтров в круглые скобки или результирующий набор "взрывается". Я проверил и обнаружил, что это необходимо только при фильтрации по правой таблице; Фильтрация по левой таблице не имеет этой проблемы (очевидно).

Окончательный ответ:

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),

Вы должны поставить условия на втором столе в ON пункт. Единственная очевидная ссылка T2.GROUP_CUST, но это может относиться к DATE_INVOICE а также PART также:

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;
Другие вопросы по тегам