Search code examples
sqlsql-servercaseinner-join

CASE in INNER join not returning desired results


For the following dataset:

CREATE TABLE ServiceOrder (
  ServiceOrderId INTEGER ,
  EventStatus VARCHAR(100),
  Date DATE
);

INSERT INTO ServiceOrder VALUES (0001, 'status1A', CONVERT(date,'01.01.2022',104));
INSERT INTO ServiceOrder VALUES (0001, 'status1B', CONVERT(date,'12.01.2022',104));
INSERT INTO ServiceOrder VALUES (0001, 'status2', CONVERT(date,'01.02.2022',104));
INSERT INTO ServiceOrder VALUES (0001, 'status2', CONVERT(date,'22.02.2022',104));
INSERT INTO ServiceOrder VALUES (0001, 'status3', CONVERT(date,'01.03.2022',104));
INSERT INTO ServiceOrder VALUES (0001, 'status4', CONVERT(date,'01.04.2022',104));

INSERT INTO ServiceOrder VALUES (0002, 'status1A', CONVERT(date,'12.01.2022',104));
INSERT INTO ServiceOrder VALUES (0002, 'status1B', CONVERT(date,'01.01.2022',104));
INSERT INTO ServiceOrder VALUES (0002, 'status2', CONVERT(date,'01.02.2022',104));
INSERT INTO ServiceOrder VALUES (0002, 'status2', CONVERT(date,'22.02.2022',104));
INSERT INTO ServiceOrder VALUES (0002, 'status3', CONVERT(date,'01.03.2022',104));
INSERT INTO ServiceOrder VALUES (0002, 'status4', CONVERT(date,'12.01.2022',104));

I need the smallest date for each status by ServiceOrderId. (in the following result, you can see status2 only show once by ServiceOrderId) Like this:

SELECT ServiceOrderId, EventStatus, MIN(Date) 
FROM ServiceOrder
GROUP BY ServiceOrderId, EventStatus ;

tableA, RESULT:
    ServiceOrderId  EventStatus MIN(Date)
    1   status1A    2022-01-01
    1   status1B    2022-01-12 
    1   status2     2022-02-01
    1   status3     2022-03-01
    1   status4     2022-04-01
    2   status1A    2022-01-12 
    2   status1B    2022-01-01
    2   status2     2022-02-01
    2   status3     2022-03-01
    2   status4     2022-04-01

and, if the ServiceOrderId contain both EventStatus status1A and status1B I need the one with the smallest date.

The following works in MySQL:

SELECT tableC.ServiceOrderId, tableC.EventStatus, MIN(tableC.Date) as tableCDate
FROM ServiceOrder as tableC, 
    (SELECT ServiceOrderId, MAX(tableADate) as tableBDate
    FROM (SELECT ServiceOrderId, EventStatus, MIN(Date) as tableADate
        FROM ServiceOrder
        GROUP BY ServiceOrderId, EventStatus
        ) as tableA
    WHERE EventStatus IN ('status1A', 'status1B')
    GROUP BY ServiceOrderId
    ) as tableB
where (CASE WHEN EventStatus IN ('status1A', 'status1B')
        THEN tableC.ServiceOrderId <> tableB.ServiceOrderId 
             and tableC.Date <> tableB.tableBDate 
        ELSE TRUE END
        )
GROUP BY tableC.ServiceOrderId, tableC.EventStatus

Wanted result:

ServiceOrderId  EventStatus tableCDate
1   status1A 2022-01-01
1   status2 2022-02-01
1   status3 2022-03-01
1   status4 2022-04-01
2   status1B 2022-01-01
2   status2 2022-02-01
2   status3 2022-03-01
2   status4 2022-01-12

but I need it in SQL Server and it won't work. There is a problem with the case. I try to change it like the following, but it won't filter out between status1A and status1B.

SELECT tableC.ServiceOrderId, tableC.EventStatus, MIN(tableC.Date) as tableCDate
FROM ServiceOrder as tableC
INNER JOIN 
    (SELECT ServiceOrderId, MAX(tableADate) as tableBDate
    FROM (SELECT ServiceOrderId, EventStatus, MIN(Date) as tableADate
          FROM ServiceOrder
          GROUP BY ServiceOrderId, EventStatus) as tableA
    WHERE EventStatus IN ('status1A', 'status1B')
    GROUP BY ServiceOrderId
    ) as tableB
ON (CASE WHEN EventStatus IN ('status1A', 'status1B')
        AND tableC.ServiceOrderId <> tableB.ServiceOrderId 
        AND tableC.Date <> tableB.tableBDate 
    THEN 0
    ELSE 1 END
    )=1
GROUP BY tableC.ServiceOrderId, tableC.EventStatus

Result (not wanted):

ServiceOrderId  EventStatus tableCDate
1   status1A    2022-01-01
1   status1B    2022-01-12
1   status2 2022-02-01
1   status3 2022-03-01
1   status4 2022-04-01
2   status1A    2022-01-12
2   status1B    2022-01-01
2   status2 2022-02-01
2   status3 2022-03-01
2   status4 2022-01-12

Solution

  • Your SQL is a bit hard to comprehend. I will handle 1A1B and others separately and union them at the end.

    SELECT SO.ServiceOrderId, SO.EventStatus, AB.MinDate
    FROM
    (
        SELECT ServiceOrderId, MIN(Date) AS MinDate
        FROM ServiceOrder
        WHERE EventStatus IN ('status1A', 'status1B')
        GROUP BY ServiceOrderId
    ) AB INNER JOIN ServiceOrder SO
        ON AB.ServiceOrderId = SO.ServiceOrderId AND AB.MinDate = SO.Date
    WHERE SO.EventStatus IN ('status1A', 'status1B')
    UNION
    SELECT ServiceOrderId, EventStatus, MIN(Date) AS MinDate
    FROM ServiceOrder
    WHERE EventStatus NOT IN ('status1A', 'status1B')
    GROUP BY ServiceOrderId, EventStatus