SQL Server - 在Where和Select中使用Exists子句

时间:2011-01-11 09:19:38

标签: sql sql-server sql-server-2008

我有一个类似

的观点
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条款的重复有些不一致。写一个更好的qway吗?我可以让它表现更好吗?

3 个答案:

答案 0 :(得分:3)

您可以在OrderLineItemType1和OrderLineItemType2上LEFT JOIN,然后在WHERE子句中过滤掉这两列都为NULL的行。

答案 1 :(得分:0)

可能值得分析的一个变化(但与您的具体问题没有直接关系)。

以下两行:

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中唯一匹配的行必须是此订单的最新行。 / p>

答案 2 :(得分:0)

您根本不需要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