Почему эти два запроса с «EXISTS» ведут себя по-разному? - PullRequest
1 голос
/ 07 мая 2019

Есть две таблицы. Customers с custid в качестве PK и Orders с custid в качестве FK.

Customers таблица содержит столбцы custid, companyname

Orders таблица содержит столбцы custid, orderid, orderdate

Я хочу вернуть клиентов, которые заказывали в 2007 году, а не в «2008». Я хочу вернуть custid и companyname в конечном результате.

У меня есть query1, который выбирает правильный результат с 7 различными custid в общей сложности У меня есть query2, который дает мне более четкие строки, т.е. 86 строк в конечном результате

Query1

SELECT custid, companyname
FROM customers c
WHER EXISTS
 (SELECT custid
  FROM orders o
  WHERE YEAR(orderdate) = '2007'AND o.custid = c.custid)
AND NOT EXISTS
 (SELECT custid
  FROM orders o
  WHERE YEAR(orderdate) = '2008'AND o.custid=c.custid)

Query2

SELECT DISTINCT custid, companyname
FROM customers c
WHERE EXISTS
 (SELECT custid
  FROM orders o
  WHERE YEAR(orderdate) = '2007'
        AND YEAR(orderdate) <> '2008'
        AND o.custid=c.custid)

Я не понимаю проблемы с query2 и почему он не может дать правильные результаты?

Ответы [ 4 ]

2 голосов
/ 07 мая 2019

Правильный запрос: первый .

Как отметил пользователь 2722968, использование: WHERE YEAR(orderdate) = '2007' AND YEAR(orderdate) <> '2008' работает для каждой строки.Таким образом, если у custid есть и orderid в 2007 году, и другой в 2008 году, вышеупомянутый WHERE действительно вернет строку 2007 года, так как он действительно YEAR(orderdate) = '2007' AND YEAR(orderdate) <> '2008'.

Наоборот, разные коды в(NOT) EXISTS выполнить операцию (semijoi) не над строками, а над наборами результатов.Это то, что вам нужно.

Предложение: Рекомендуется не использовать функции, если вы можете их избегать, потому что, когда вы применяете функции к полю, если на нем есть индекс, он не можетбыть использованы для ускорения расчетов.Таким образом, вместо YEAR (orderdate) = 2007 лучше использовать:

orderdate>='20070101' and orderdate<'20080101'

С учетом этого запрос становится:

SELECT custid, companyname
FROM customers c
WHERE EXISTS
 (SELECT custid
  FROM orders o
  WHERE orderdate>='20070101' and orderdate<'20080101' AND o.custid = c.custid)
AND NOT EXISTS
 (SELECT custid
  FROM orders o
  WHERE orderdate>='20080101' and orderdate<'20090101' AND o.custid=c.custid)
1 голос
/ 14 мая 2019

Есть один более простой способ решить этот вопрос с помощью EXCEPT

select c.custid, c.companyname
from Customers c
join
Orders o
on o.custid=c.custid
where orderdate>='20070101' and orderdate<'20080101'
except
select c.custid, c.companyname
from [TSQL2012].Sales.Customers c
join
[TSQL2012].Sales.Orders o
on o.custid=c.custid
where orderdate>='20080101' and orderdate<'20090101'
1 голос
/ 07 мая 2019

Хорошо, давайте получим таблицу customers с одним клиентом (клиент 1) и следующую таблицу orders

custid   orderid  orderdate
---------------------------
  1         1       1.1.2007
  1         2       1.1.2008       

Ваш второй запрос интерпретирует подзапрос для клиента

SELECT custid
FROM orders o
WHERE YEAR(orderdate) = 2007 AND YEAR(orderdate) <> 2008 AND o.custid = 1

и возвращает первый ряд.Следовательно, exists оценивается для true для клиента, потому что есть строка с year(orderdate) = 2007 and year(orderdate) <> 2008 (первая строка).ОДНАКО, это не означает, что не существует другой строки с 2008 годом!

Ясно, что первый запрос не возвращает результата, поскольку клиент 1 не удовлетворяет предикату not exists.Если мы выражаем первый запрос в реляционной алгебре, то это соответствует разнице между двумя наборами, однако второй запрос является простым соединением с условием.

1 голос
/ 07 мая 2019

Почему эти два запроса с 'EXISTS' ведут себя по-разному?

Потому что:

  • Первый запрос вернет клиента, если у него есть заказ в2007 и не имеет (другого) заказа в 2008 году.
  • Но второй запрос вернет клиента, если у него есть заказ в 2007 году, но тот же заказ не в 2008 году (и так как заказ ужев 2007 году, то не в 2008 году, поэтому условие <> 2008 является избыточным).

Похоже, что первый запрос имеет больше смысла.

...