简体   繁体   中英

Errors when using multiple CASE statements [SQL]

Using SQL in Microsoft SQL Server, I have a very specific problem where I am comparing data between two months. This data can either have one of two status' - to keep it simple it's either "1" or "0" or basically "yes" or "no."

I'd like to consider the following scenarios:

  • if a row has status of "1" in one month and that changes to "0" the next month and vice versa
  • if the status hasn't changed from one month to another (ie "1" to "1" or "0" to "0")
  • if value for status was null in previous month and changes to either "1" or "0" the next

I assume I can use multiple CASE statements for this and what I have so far is the following until I get stuck:

select
     --determine if status changes from 1 to 0 between two months or stays the same
     case
     when T.[status] = '1' and T.date between '01-01-2017' and '02-28-2017'
     then 'No Change'
     else
     'Status Changed to 0'
     end,

     --determine if status changes from 0 to 1 between two months or stays the same
     case
     when T.[status] = '0' and T.date between '01-01-2017' and '02-28-2017'
     then 'No Change'
     else 
     'Status Changed to 1'
     end,

     --determine if there was new row that didn't appear in first month with a status of 1 or 0 in second month
     case
     when T.status is null in T.date between '01-01-2017' and '01-31-2017'  
     and T.status = '1' in T.date between '02-01-2017' and '02-28-2017' then 'New Status to 1'
     else
     'New Status of 1'
     end, *
from table T
where ....

This code is working for the first CASE statement to display a status change from 1 to 0 in a new column but its making another column for the second status and I am not sure if it is working as I intend it to. Moreover, since I am examining the two months together, I can't seem to logic my way around coding the other scenarios into my SQL.

I'm trying to find some way to condense all of the CASE statements to only producing one extra column and another problem I am having is that the third CASE statement is throwing a syntax error at the first "IN." Hope this makes sense to someone and thanks in advance for any input or tips you can provide.

Taking your last problem, first, incorrect syntax with "IN":

 case
 when T.status is null in t.date between '01-01-2017' and '01-31-2017'  
 and T.status = '1' in T.date between '02-01-2017' and '02-28-2017' then 'New Status to 1'
 else
 'test'
 end

Try:

 case
 when (T.status is null AND t.date between '01-01-2017' and '01-31-2017')  
 OR (T.status = '1' AND T.date between '02-01-2017' and '02-28-2017') then 'New Status to 1'
 else
 'test'
 end

For easier checking the change in status, join your table to itself with a one-month offset.

I added an example table with input data in a WITH clause, which could match your requirement. I'm using the LAG() analytic function, which is supported by SQL Server. Remember that, in the first row in a partition of an analytic window, LAG() returns NULL - as there is no previous row in that case.

In other DBMSs than SQL Server, you will need the ANSI concatenation operator || instead of + to concatenate two strings.

WITH
input(input_id,monthfirst,status) AS (
          SELECT 1,DATE '2017-01-01',0
UNION ALL SELECT 1,DATE '2017-02-01',1
UNION ALL SELECT 1,DATE '2017-03-01',0
UNION ALL SELECT 2,DATE '2017-01-01',1
UNION ALL SELECT 2,DATE '2017-02-01',1
UNION ALL SELECT 2,DATE '2017-03-01',0
UNION ALL SELECT 3,DATE '2017-01-01',CAST(NULL AS INT)
UNION ALL SELECT 3,DATE '2017-02-01',1
UNION ALL SELECT 3,DATE '2017-03-01',0
)
,
input_with_prev_status AS (
SELECT
  *
, LAG(status) OVER (PARTITION BY input_id ORDER BY monthfirst) AS prev_status
FROM input
)
SELECT
  input_id
, monthfirst
, status
, CASE
    WHEN prev_status IS NULL AND status IS NOT NULL
      THEN 'New status to ' + CAST(status AS CHAR(1))
    WHEN prev_status <> status
      THEN 'Status changed to ' + CAST(status AS CHAR(1))
    WHEN prev_status = status
      THEN 'no status change'
    WHEN prev_status IS NULL AND status IS NULL
      THEN 'status remains missing'
    ELSE 'unforeseen status change'
  END AS status_change
FROM input_with_prev_status
;
input_id|monthfirst|status|status_change
       1|2017-01-01|     0|New status to 0
       1|2017-02-01|     1|Status changed to 1
       1|2017-03-01|     0|Status changed to 0
       2|2017-01-01|     1|New status to 1
       2|2017-02-01|     1|no status change
       2|2017-03-01|     0|Status changed to 0
       3|2017-01-01|-     |status remains missing
       3|2017-02-01|     1|New status to 1
       3|2017-03-01|     0|Status changed to 0

See code for self join (suggested by Dejan) below. You need to create the ON statement using the key column in table T

select 
  case 
  when (T1.[status] = '1' and T2. [status] = '1') or (T1.[status] = '0' and T2. [status] = '0') then 'No Change'
  when T1.[status] = '1' and T2. [status] = '0' then 'Status Changed to 0'
  when T1.[status] = '0' and T2. [status] = '1' then 'Status Changed to 1'
  when T.status is null and  T2. [status] = '1' then 'New Status to 1'
 else
 'test'
  end
table T1 
inner join T2
   on ....join on the key column
where T1.date between '01-01-2017' and '31-01-2017' 
and T2.date between '01-02-2017' and '28-02-2017'

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM