SQL Server - используйте предложение Exists в Where и Select - PullRequest
6 голосов
/ 11 января 2011

У меня есть вид, похожий на

CREATE VIEW OrdersView WITH SCHEMABINDING AS
SELECT o.Id, o.OrderDate, o.LastName, o.TotalPrice, s.Status
FROM dbo.Orders o INNER JOIN dbo.OrderStatus s on o.Id = s.OrderId
WHERE NOT EXISTS (SELECT NULL from dbo.OrderStatus where OrderId = s.OrderId and StatusDate > s.StatusDate
AND EXISTS (SELECT NULL FROM dbo.OrderLineItemType1 WHERE OrderId = o.Id)

Цель состоит в том, чтобы получить все заказы, которые имеют хотя бы одну позицию типа 1, вместе с их текущим статусом.В процессе добавления позиции второго типа, и я изменил представление так, чтобы оно включало заказы, в которых есть хотя бы одна позиция типа 1 или типа 2:

CREATE VIEW OrdersView WITH SCHEMABINDING AS
SELECT o.Id, o.OrderDate, o.LastName, o.TotalPrice, s.Status
FROM dbo.Orders o INNER JOIN dbo.OrderStatus s on o.Id = s.OrderId
WHERE NOT EXISTS (SELECT NULL from dbo.OrderStatus where OrderId = s.OrderId and StatusDate > s.StatusDate
AND (EXISTS (SELECT NULL FROM dbo.OrderLineItemType1 WHERE OrderId = o.Id)
  OR EXISTS (SELECT NULL FROM dbo.OrderLineItemType2 WHERE OrderId = o.Id))

Достаточно просто, но я только что добавил требование, чтобы показать, содержит ли заказ позиции типа 1 или типа 2 (или оба) в сетке, где отображаются эти результаты:

Order ID | T1 | T2 | Last name | Price    | Status
============================================================
12345    | x  |    | Smith     | $100.00  | In Production
12346    | x  | x  | Jones     | $147.23  | Part Dispatched
12347    |    | x  | Atwood    | $12.50   | Dispatched

Единственный способ, которым яМожно подумать, что делать:

CREATE VIEW OrdersView WITH SCHEMABINDING AS
SELECT o.Id, 
       CASE WHEN EXISTS (SELECT NULL FROM dbo.OrderLineItemType1 WHERE OrderID = o.Id) THEN 1 ELSE 0 END AS HasType1,
       CASE WHEN EXISTS (SELECT NULL FROM dbo.OrderLineItemType2 WHERE OrderId = o.ID) THEN 1 ELSE 0 END AS HasType2,
       o.OrderDate, o.LastName, o.TotalPrice, s.Status
FROM dbo.Orders o INNER JOIN dbo.OrderStatus s on o.Id = s.OrderId
WHERE NOT EXISTS (SELECT NULL from dbo.OrderStatus where OrderId = s.OrderId and StatusDate > s.StatusDate
AND (EXISTS (SELECT NULL FROM dbo.OrderLineItemType1 WHERE OrderId = o.Id)
  OR EXISTS (SELECT NULL FROM dbo.OrderLineItemType2 WHERE OrderId = o.Id))

Но это плохо пахнет с дублированием пунктов EXISTS.Есть ли лучший способ написать это?Могу ли я заставить его работать лучше?

Ответы [ 3 ]

3 голосов
/ 11 января 2011

вы можете LEFT JOIN на OrderLineItemType1 и OrderLineItemType2 и затем отфильтровывать строки, в которых оба этих столбца имеют значение NULL в предложении WHERE.

0 голосов
/ 11 января 2011

Вам вообще не нужно EXISTS здесь:

SELECT  o.Id, HasType1, HasType2, o.OrderDate, o.LastName, o.TotalPrice, s.Status
FROM    dbo.Orders o
CROSS APPLY
        (
        SELECT  TOP 1 s.*
        FROM    dbo.OrderStatus
        WHERE   OrderId = o.Id
        ORDER BY
                StatusDate DESC
        ) s
OUTER APPLY
        (
        SELECT  TOP 1 1 AS HasType1
        FROM    dbo.OrderLineItemType1
        WHERE   OrderID = o.Id
        ) olt1
OUTER APPLY
        (
        SELECT  TOP 1 1 AS HasType2
        FROM    dbo.OrderLineItemType2
        WHERE   OrderID = o.Id
        ) olt2
0 голосов
/ 11 января 2011

Одно изменение, которое может стоить профилирования (но не имеет прямого отношения к вашему конкретному вопросу).

Следующие две строки:

FROM dbo.Orders o INNER JOIN dbo.OrderStatus s on o.Id = s.OrderId
WHERE NOT EXISTS (SELECT NULL from dbo.OrderStatus where OrderId = s.OrderId and StatusDate > s.StatusDate

Может быть, лучше написать это как:

FROM dbo.Orders o INNER JOIN dbo.OrderStatus s on o.Id = s.OrderId
        LEFT JOIN dbo.OrderStatus s_later on o.Id  = s_later.OrderId and s_later.StatusDate > s.StatusDate
WHERE s_later.OrderId is null

Я обычно нахожу, что это работает лучше (но это один из тех, которые стоит профилировать в обоих направлениях).

LEFT JOIN пытается найти более поздние строки, которые применяются к тому же порядку, затем предложение WHERE отклоняет любые потенциальные строки результата, где произошло такое совпадение, поэтому единственная совпадающая строка из s должна быть самой последней для этого порядка. 1011 *

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...