Объяснение необходимо для пропущенных строк с левым соединением и счетчиком () - PullRequest
11 голосов
/ 04 июня 2011

Может ли кто-нибудь помочь мне разобраться в следующем поведении, которое происходит, когда я добавляю предложение WHERE в запрос, в котором есть LEFT JOIN с COUNT (*)?

У меня есть две таблицы:

TABLE 1: customers
customer_id | name
------------------
1 | Bob
2 | James
3 | Fred

TABLE 2: orders
order_id | customer_id | order_timestamp
----------------------------------------
1000 | 1 | 2011-01-01 00:00
1001 | 1 | 2011-01-05 00:00
1002 | 2 | 2011-01-10 00:00

Теперь следующий запрос сообщает мне, сколько заказов сделал каждый клиент:

select c.customer_id, count(o.order_id)
from customers c
left join orders o using (customer_id)
group by 1

customer_id | count
-------------------
1 | 2
2 | 1
3 | 0

Это прекрасно работает, НО, если я добавлю в запрос предложение WHERE, запрос больше не будетвыводит количество нулей для клиентов, которые не размещали никаких заказов, хотя я делаю ЛЕВНОЕ СОЕДИНЕНИЕ:

select c.customer_id, count(o.order_id)
from customers c
left join orders o using (customer_id)
where o.order_timestamp >= '2011-01-05'
group by 1

customer_id | count
-------------------
1 | 1
2 | 1

Теперь, если я переместлю условие ГДЕ как часть ЛЕВОГО СОЕДИНЕНИЯ, как показано ниже, я получуназад мои нулевые счета для клиентов, которые не размещали заказы:

select c.customer_id, count(o.order_id)
from customers c
left join orders o on (c.customer_id = o.customer_id) and (o.order_timestamp >= '2011-01-05')
group by 1

Я не понимаю, почему второй запрос не работает, а третий -?Может ли кто-нибудь дать мне объяснение?Также не уверен, что это имеет значение, но я использую postgres.Спасибо!

Ответы [ 3 ]

7 голосов
/ 04 июня 2011

Это потому, что NULL не больше или равен чему-либо;Если вы измените предложение WHERE на where o.order_timestamp is null or o.order_timestamp >= '2011-01-05', то получите то же поведение, что и ограничение предложения объединения.

Обратите внимание: я бы рекомендовал подход предложения объединения, так как он более точно соответствует тому, что вы пытаетесьделать.Кроме того, изменение в предложении WHERE, которое я упомянул выше, будет работать только в том случае, если столбец order_timestamp не имеет значения NULL - если это так, то для проверки нуля следует использовать другой столбец (например, where o.primarykey is null or o.order_timestamp >= '2011-01-05').

5 голосов
/ 04 июня 2011

Размещение критериев фильтра имеет значение при работе с внешними соединениями (ВПРАВО, ВЛЕВО). Критерии в предложении ON OUTER JOIN применяются перед JOIN; критерии в предложении WHERE применяются после JOIN - применяются к результирующему набору, который использует JOIN.

   SELECT c.customer_id, 
          COUNT(o.order_id)
     FROM CUSTOMERS c
LEFT JOIN ORDERS o ON o.customer_id - c.customer_id
                  AND o.order_timestamp >= '2011-01-05'
 GROUP BY c.customer_id

ординалы

Порядковые значения, означающие использование числового значения, которое относится к числовому положению столбцов в предложении SELECT, не рекомендуется. Если кто-то изменит запрос, скажем, добавить столбец, это может существенно повлиять на ваш запрос.

0 голосов
/ 04 июня 2011

Чирс прав, ноль не больше или равен чему-либо. Поэтому, когда вы включаете ваше условие в условие where, оно применяется к окончательному представлению (таблице) результатов, генерируемых левым соединением, в этом результате ваше условие удаляет строку с отметкой времени ноль.

Однако, когда вы применяете то же условие во время выполнения соединения, условие применяется только к таблице заказов, а затем выполняется левое соединение. Таким образом, он не удаляет строки с отметкой времени ноль.

Таким образом, в третьем условии запроса, примененном до генерации финальной таблицы, и во втором условии запроса, примененном после генерации финальной таблицы

...