嵌套选择不为空的SQL

时间:2019-01-25 12:02:11

标签: sql sql-server

我有一个Customers表,其中有CustomerIDCustomerName

然后我有一个Orders表,其中包含CustomerIDdatetime OrderPlaceddatetime OrderDelivered

考虑到并非所有客户都下订单,我想获得CustomerNameOrderPlacedOrderDelivered的列表,但仅针对已下订单且其订单的客户已交付,每个客户只有最新的OrderPlaced

我从开始做起(完全意识到这还没有实现OrderDelivered的限制,但是还没有做我想做的事):

SELECT CustomerID,
    (SELECT TOP 1 OrderDelivered 
        FROM Orders ORDER BY OrderDelivered DESC) AS OrderDelivered
FROM Customer
WHERE OrderDelivered IS NOT NULL

但是MS SQL已经不喜欢这样,它说它不知道OrderDelivered子句中的WHERE是什么。

我该怎么做?

3 个答案:

答案 0 :(得分:2)

个人而言,我会将您的子查询移至FROM并使用CROSS APPLY。然后,您可以更轻松地参考该列:

SELECT C.CustomerID,
       O.OrderDelivered
FROM Customer C
     CROSS APPLY (SELECT TOP 1 OrderDelivered 
                  FROM Orders oa
                  WHERE oa.CustomerID = C.CustomerID --Guess column name for orders
                    AND O.OrderDelivered IS NOT NULL
                  ORDER BY O.OrderDelivered DESC) O;

但是,由于它是CROSS APPLY,因此结果已经被过滤;因此不需要WHERE

答案 1 :(得分:1)

如果您要获取最近交付的订单,则一种方法使用apply

select c.*, o.OrderPlaced, o.OrderDelivered
from customer c cross apply
     (select top (1) o.*
      from orders o
      where o.CustomerID = c.CustomerID and 
            o.OrderDelivered is not null
      order by o.OrderPlaced desc
     ) o;

答案 2 :(得分:0)

您可以使用OVER子句(https://docs.microsoft.com/en-us/sql/t-sql/queries/select-over-clause-transact-sql)来实现。

DECLARE @customers TABLE (CustomerId INT, CustomerName NVARCHAR(20))
DECLARE @orders TABLE (CustomerId INT, OrderPlaced DATETIME, OrderDelivered DATETIME)

INSERT INTO @customers VALUES
    (1, 'a'),
    (2, 'b')

INSERT INTO @orders VALUES
    (1, '2019-01-01', null),
    (2, '2019-01-03', '2019-02-01'),
    (2, '2019-01-05', null)

SELECT
    c.CustomerName,
     -- Latest OrderPlaced
    FIRST_VALUE(o.OrderPlaced)
        OVER(PARTITION BY c.CustomerId ORDER BY o.OrderPlaced DESC) AS OrderPlaced,
    -- The matching OrderDelivered
    FIRST_VALUE(o.OrderDelivered)
        OVER(PARTITION BY c.CustomerId ORDER BY o.OrderPlaced DESC) AS OrderDelivered
FROM @customers c
INNER JOIN @orders o ON o.CustomerId = c.CustomerId
WHERE o.OrderDelivered IS NOT NULL