如何区分左连接中来自不匹配记录的空值和来自不存在记录的空值?

How to differentiate between a null from a non matched record and a null from a non existing record in a left join?

下面的语句创建了 tables、数据和有问题的 select。

目标是当custid 1没有订单时,我想显示'Did not find any orders',如果custid 1有订单记录但没有电视,我想显示'Found order but not TV order' .在这两种情况下,t.description 都将为 NULL,但我需要区分这两种 NULL 情况。如何区分它们以便前两个 WHEN 语句可以相应地处理每个?

CREATE TABLE [dbo].[Customer](
    [CustId] [int] NOT NULL,
    [CustomerName] [varchar](50) NOT NULL
)

CREATE TABLE [dbo].[Order](
    [OrderId] [int] NOT NULL,
    [CustId] [int] NOT NULL,
    [Description] [varchar](50) NOT NULL
)



INSERT INTO customer (CustId, CustomerName) VALUES
(1, 'John'),
(2, 'Tom')
GO

INSERT INTO [order] (OrderId, CustId, Description) VALUES
(1, 2, 'TV')
go

IF OBJECT_ID('tempdb..#temp') IS NOT NULL
    DROP TABLE #temp;

SELECT

    'TV' Description INTO #temp

SELECT c.CustId
    ,t.Description
   ,o.Description
   ,CASE
        WHEN t.Description IS NULL THEN 'Did not find any orders'
        WHEN t.Description IS NULL THEN 'Found order but not TV order'
        ELSE 'Found TV Order' 
    END Status

FROM Customer c
LEFT JOIN [Order] o
    ON o.CustId = c.CustId
LEFT JOIN #temp t
    ON t.Description = o.Description
WHERE c.CustId = 1

更新:

#temp table 具有搜索条件。在这种情况下 'TV'.

///你可以试试这个。

 select a.custid,a.CustomerName,orderid=isnull(b.orderId,0),
     description=iif(b.description is null,'Did not Find Any Order',b.description) 
     from Customer a left join [Order] b on a.custid=b.Custid

这可能对你有帮助,不确定。

案例陈述:

,case when  t.Description = o.Description  then 'Found TV Order'
         when  isnull(t.Description,'No') = isnull(o.Description,'No') then 'Did not find any orders'
         when  isnull(t.Description,'No') <> isnull(o.Description,'No') then 'Found order but not TV order'
   end Status

修改后的查询:

SELECT c.CustId
    ,t.Description tdesc
   ,o.Description odesc

   ,case when  t.Description = o.Description  then 'Found TV Order'
         when  isnull(t.Description,'No') = isnull(o.Description,'No') then 'Did not find any orders'
         when  isnull(t.Description,'No') <> isnull(o.Description,'No') then 'Found order but not TV order'
   end Status
  /*  ,CASE
        WHEN t.Description IS NULL THEN 'Did not find any orders'
        WHEN t.Description IS NULL THEN 'Found order but not TV order'
        ELSE 'Found TV Order' 
    END Status  */

FROM Customer c
LEFT JOIN [Order] o ON o.CustId = c.CustId
LEFT JOIN #temp t   ON t.Description = o.Description

--------------------------- ** Old code is below **---------------------------------------------

 select CustId,descpt 
    from 
    (
    SELECT c.CustId
        , case when o.Description is null then 'Did not find any orders' end   descpt 
    FROM Customer c
    LEFT JOIN [Order] o ON o.CustId = c.CustId  ) no_orders
    where no_orders.descpt is not null

    union 
    select CustId,descpt 
    from 
    (
    SELECT c.CustId
        , case when o.Description ='TV' then 'Found order for TV ' end   descpt 
    FROM Customer c
    LEFT JOIN [Order] o ON o.CustId = c.CustId  ) no_orders
    where no_orders.descpt is not null

    union
    select CustId,descpt 
    from 
    (
    SELECT c.CustId
        , case when o.Description <> 'TV' then 'Found order but not TV order' end   descpt 
    FROM Customer c
    LEFT JOIN [Order] o ON o.CustId = c.CustId  ) no_orders
    where no_orders.descpt is not null

从问题中还不完全清楚,但我假设临时 table #temp 是您要在订单中查找的描述列表。

所以我可以想到几种不同的方法,例如您可以使用 OUTER APPLY:

select
    c.CustId,
    t.Description,
    case
        when o.Description is null then
            'Did not find any orders'
        when o.Description <> t.Description then
            concat('Found order but not ',t.Description,' order')
        else
            concat('Found ',t.Description,' order')
    end as status
from dbo.Customer as c
    cross join (
        select 'TV' as Description
    ) as t
    outer apply (
        select top 1 tt.Description
        from dbo.[Order] as tt
        where
            tt.CustId = c.CustId
        order by
            case when tt.Description = t.Description then 0 else 1 end
    ) as o

sql fiddle demo

或者您可以使用左连接和外部应用:

select
    c.CustId,
    t.Description,
    case
        when o1.Description is not null then
            concat('Found ',t.Description,' order')
        when o2.OrderId is not null then
            concat('Found order but not ',t.Description,' order')
        else
            'Did not find any orders'
    end as status
from dbo.Customer as c
    cross join (
        select 'TV' as Description
    ) as t
    left join dbo.[Order] as o1 on
        o1.CustId = c.CustId and
        o1.Description = t.Description
    outer apply (
        select top 1 tt.OrderId
        from dbo.[Order] as tt
        where
            tt.CustId = c.CustId and
            tt.Description <> t.Description
    ) as o2

sql fiddle demo

首先,我假设订单中的每个客户可以并且很可能会有多个记录 table。由于您需要知道是否没有任何订单,但显然需要一个结果集,因此您无法根据所需的描述进行过滤……但是如果您按客户(和描述)分组并使用计数,则可以功能,这还允许您在订单 table.

中可能有很多条记录时 return 一条记录

此外,由于您最终要尝试根据两个结果集(电视订单和任何订单)回答两个问题,因此您将需要两个查询的某个版本。多种选择,但子查询是最简单的。

这是 SQL(我为此永久设置了临时 table 并添加了更多记录):

CREATE TABLE [dbo].[Customer](
    [CustId] [int] NOT NULL,
    [CustomerName] [varchar](50) NOT NULL
)
CREATE TABLE [dbo].[Order](
    [OrderId] [int] NOT NULL,
    [CustId] [int] NOT NULL,
    [Description] [varchar](50) NOT NULL
)
CREATE TABLE [dbo].[FakeTemp](
    [Description] [varchar](50) NOT NULL
)
INSERT INTO FakeTemp (Description) VALUES
('TV')
GO
INSERT INTO customer (CustId, CustomerName) VALUES
(1, 'John'),
(2, 'Tom'),
(3, 'Steve')
GO
INSERT INTO [order] (OrderId, CustId, Description) VALUES
(1, 1, 'TV')
go
INSERT INTO [order] (OrderId, CustId, Description) VALUES
(2, 2, 'VCR')
go
INSERT INTO [order] (OrderId, CustId, Description) VALUES
(3, 1, 'VCR')
go


select c.CustomerName, count(OrderId) as TV_Orders
, (select count(OrderID) from [order] where CustId = 1) as All_Orders
from customer c
left outer join [order] o on c.CustId = o.CustId
inner join FakeTemp t on o.Description = t.Description
where c.CustID = 1
group by c.CustomerName, t.Description

这里是 link 到 fiddle(感谢 Roman Pekar 让我开始): http://sqlfiddle.com/#!18/eadc3/16/0

我没有做 case 语句,因为我认为您可以轻松地将其转换为输出文本所需的 case 语句,并且这样显示的结果更好一些。

对于无法完成案例陈述的同学,这里是完整完成的作业问题:

select
  CASE
      WHEN (select count(OrderID) from orders where CustId = 1) = 0 THEN 'Did not find any orders'
      WHEN count(OrderID) = 0 THEN 'Found order but not TV order'
      ELSE 'Found TV Order'
  End as Status

from customer c
left outer join [orders] o on c.CustId = o.CustId
inner join FakeTemp t on o.Description = t.Description
where c.CustID = 1
group by c.CustomerName, t.Description

如果您只想显示有关客户的信息,请使用聚合:

SELECT c.CustId,
       (CASE WHEN COUNT(o.CustId) = 0
             THEN 'Did not find any orders'
             WHEN SUM(CASE WHEN o.Description = 'TV' THEN 1 ELSE 0 END) = 0
            THEN 'Found order but not TV order'
            ELSE 'Found TV Order' 
       END) as Status
FROM Customer c LEFT JOIN 
     [Order] o
     ON o.CustId = c.CustId
WHERE c.CustId = 1
GROUP BY c.CustId;

请注意,我不知道 #temp 的用途。

另外,如果您需要客户级别的信息,您不应该在 SELECT 中包含订单级别的信息。