开发者

SQL Server - use Exists clause in Where and Select

开发者 https://www.devze.com 2023-02-03 19:30 出处:网络
I have a view that\'s something like CREATE VIEW OrdersView WITH SCHEMABINDING AS SELECT o.Id, o.OrderDate, o.LastName, o.TotalPrice, s.Status

I have a view that's something like

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)

The intent is to fetch all orders that have at least one line item of type 1 along with their current status.

We're in the process of adding a second type of line item, and I have modified the view such that it will include orders that have at least one line item of type 1 or type 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))

Easy enough, but I've just had a requirement added to show whether an order contains line items of type 1 or type 2 (or both) in the grid where these results are displayed:

Order ID | T1 | T2 | Last name | Price    | Status
============================================================
12345    | x  |    | Smith     | $100.00  | In Production
12346    | x  | x  | Jones     | $147.23  | Part Dispatched
开发者_如何学C12347    |    | x  | Atwood    | $12.50   | Dispatched

The only way I can think of is to do:

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

But this smells wrong with the duplication of the EXISTS clauses. Is there a better qway to write it? Can I make it perform better?


you can LEFT JOIN on OrderLineItemType1 and OrderLineItemType2 and then filter out rows where both of those columns are NULL in the WHERE clause.


One change that may be worth profiling (but not directly related to your specific question).

The following two lines:

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

It may be better to write this as:

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

I usually find that this performs better (but it's one of those that it's worth profiling both ways).

The LEFT JOIN tries to find later rows that apply to the same order, then the WHERE clause rejects any potential result rows where such a match occurred - so the only matching row from s must be the latest one for this order.


You don't need EXISTS here at all:

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

精彩评论

暂无评论...
验证码 换一张
取 消