Trying to write a query to specify if a customer has completed two types of a specific column

Viewed 63

I have this table (resedtl) below...

It contains information about a customer and the resType and the status of it. I am trying to determine if a customer has completed (status = done) both types of "RES" or only one or only two.

enter image description here

CREATE TABLE [dbo].[resedtl](
[CustId] [int] NOT NULL,
[RESType] [nchar](10) NULL,
[note] [varchar](50) NULL,
[status] [varchar](50) NULL
) ON [PRIMARY]
GO
INSERT [dbo].[resedtl] ([CustId], [RESType], [note], [status]) VALUES (123, N'1         ', N'test', N'done')
GO
INSERT [dbo].[resedtl] ([CustId], [RESType], [note], [status]) VALUES (123, N'2         ', N'test2', N'done')
GO
INSERT [dbo].[resedtl] ([CustId], [RESType], [note], [status]) VALUES (124, N'1         ', N'test', N'done')
GO
INSERT [dbo].[resedtl] ([CustId], [RESType], [note], [status]) VALUES (124, N'2         ', N'tests', N'no')
GO
INSERT [dbo].[resedtl] ([CustId], [RESType], [note], [status]) VALUES (125, N'1         ', N'test', N'done')
GO
INSERT [dbo].[resedtl] ([CustId], [RESType], [note], [status]) VALUES (126, N'2         ', N'test', N'done')
GO

I want to return something like this, where if custId is done for both resType 1 and 2 then I want to return both. else just one or just two.

enter image description here

So far, I've the query written as follows...

  select distinct  t.CustId, case when (
 select top 1 rd.CustId from resedtl rd
 where rd.CustId = t.CustId
 and rd.CustId in (
   select rd.CustId from resedtl rd
  where rd.status ='done'
  and rd.RESType = 1
  and rd.CustId = t.CustId
 )
 and rd.CustId in (
    select rd.CustId from resedtl rd
  where rd.status ='done'
  and rd.RESType = 2
  and rd.CustId = t.CustId
 )
 ) = t.CustId then 'both'
 when

 (
  select top 1 rd.CustId from resedtl rd
 where rd.CustId = t.CustId
 and rd.CustId in (
   select rd.CustId from resedtl rd
  where rd.status ='done'
  and rd.RESType = 1
  and rd.CustId = t.CustId
 )
 and rd.CustId not in (
    select rd.CustId from resedtl rd
  where rd.status ='done'
  and rd.RESType = 2
  and rd.CustId = t.CustId
 )
 ) = t.CustId then 'just one'

 when

 (
  select top 1 rd.CustId from resedtl rd
 where rd.CustId = t.CustId
 and rd.CustId not in (
   select rd.CustId from resedtl rd
  where rd.status ='done'
  and rd.RESType = 1
  and rd.CustId = t.CustId
 )
 and rd.CustId  in (
    select rd.CustId from resedtl rd
  where rd.status ='done'
  and rd.RESType = 2
  and rd.CustId = t.CustId
 )
 ) = t.CustId then 'just two'
 else 'None'
 end as result from resedtl t
 where t.CustId in (123,124,125,126)

I'm wondering if this is the best way to do this or if there is a better solution to this..

The query I have seem to be over complicated.

4 Answers
select   Custld
        ,case when min(chk) <> max(chk) then 'both' 
              when min(chk) =  1        then 'just one' 
              when min(chk) =  2        then 'just two'
                                        else 'none' end as "result"
        
from     (
          select  *
                  ,case status when 'done' then RESType end as chk
          from    t
         ) t
group by Custld
Custld result
123 both
124 just one
125 just one
126 just two

Fiddle

Join to itself to detect if there is a done status:

select
  t.custid,
  case
    when t1.custid is null then 'just two'
    when t2.custid is null then 'just one'
    else 'both'
  end as result
from (select distinct custid from resedtl where restype in (1, 2) and status = 'done') t
left join resedtl t1 on t1.custid = t.custid
  and t1.restype = 1 and t1.status = 'done'
left join resedtl t2 on t2.custid = t.custid
  and t2.restype = 2 and t2.status = 'done'

If you want a "none" entry, a small tweak is needed:

select
  t.custid,
  case
    when t1.custid is null and t2.custid is null then 'none'
    when t1.custid is null then 'just two'
    when t2.custid is null then 'just one'
    else 'both'
  end as result
from (select distinct custid from resedtl) t
left join resedtl t1 on t1.custid = t.custid
  and t1.restype = 1 and t1.status = 'done'
left join resedtl t2 on t2.custid = t.custid
  and t2.restype = 2 and t2.status = 'done'

I would think something like this would work. Sry, I don't have sql server installed on this machine, so it is not tested

SELECT
  ISNULL(res1.CustId, res2.CustId) AS CustId,
  CASE
    WHEN Res1.CustId IS NOT NULL AND Res2.CustId IS NOT NULL
        THEN 'both'
      WHEN Res1.CustId IS NOT NULL
        THEN 'just one'
      ELSE 'just two'
  END AS result
FROM resedtl Res1
FULL OUTER JOIN resedtl res2
  ON res1.CustId = res2.CustId
  AND res2.ResType = 2
  AND res2.status = 'done'
WHERE 
  Res1.ResType = 1
  AND res1.status = 'done'

I took a slightly different approach than the other answers. I chose to LEFT JOIN back onto the base table using the RESType so I could compare the columns side by side using a CASE EXPRESSION.

I ended up choosing to GROUP BY the r.CustId in the main original table to obtain the unique CustIds... I could have used a CTE and performed a DISTINCT or done this some other way as seen in other answers. My solution might not be the least amount of code to produce the desired result set however I feel it is intuitive and easy to understand. Regardless it is just another way to solve for this problem.

Last, in order for me to test for none where both RESType status were set to 'no' I inserted an additional record into the resedtl table.

INSERT [dbo].[resedtl] ([CustId], [RESType], [note], [status]) VALUES (127, N'2', N'test', N'no')
SELECT r.CustId
    , CASE WHEN res1.status = 'done' THEN
        
        CASE WHEN res2.status = 'done' THEN 'just both' ELSE 'just one' END 
        
      ELSE CASE WHEN res2.status = 'done' THEN 'just two' ELSE 'none' END
    
      END AS result

    /* Audit Fields below.  Comment back in for testing. */
    --, STRING_AGG(r.RESType, ', ') AS RESTTypes, STRING_AGG(r.note, ', ') AS notes, STRING_AGG(r.status, ', ') AS statusList
    --, res1.RESType AS RESType1, res1.note AS note1, res1.status AS status1
    --, res2.RESType AS RESType2, res2.note AS note2, res2.status AS status2


FROM resedtl r
    LEFT JOIN resedtl res1 ON res1.CustId = r.CustId AND res1.RESType = 1
    LEFT JOIN resedtl res2 ON res2.CustId = r.CustId AND res2.RESType = 2
GROUP BY r.CustId
    , res1.RESType, res1.note, res1.status
    , res2.RESType, res2.note, res2.status
;

RESType Query Results Example

Related