How could I simplify this SQL CASE statement? - tsql

I have a (badly formatted) weekly statement that I need to reformat, until recently this wasn't an issue.
Now the statement issuer has added extra fields into the statement meaning that things are no longer aligned nicely.
I have managed to replicate the old output format with the following:
SELECT
Field1
,CASE WHEN Field1 = 'DG3' THEN Field3 ELSE Field2 END AS Field2
,CASE WHEN Field1 = 'DG3' THEN Field4 ELSE Field3 END AS Field3
,CASE WHEN Field1 = 'DG3' THEN Field10 ELSE Field9 END AS Field9
,CASE WHEN Field1 = 'DG3' THEN Field11 ELSE Field10 END AS Field10
,CASE WHEN Field1 = 'DG3' THEN Field12 ELSE Field11 END AS Field11
,CASE WHEN Field1 = 'DG3' THEN Field13 ELSE Field12 END AS Field12
,CASE WHEN Field1 = 'DG3' THEN Field15 ELSE Field14 END AS Field14
,CASE WHEN Field1 = 'DG3' THEN Field16 ELSE Field15 END AS Field15
,CASE WHEN Field1 = 'DG3' THEN Field17 ELSE Field16 END AS Field16
,CASE WHEN Field1 = 'DG3' THEN Field18 ELSE Field17 END AS Field17
,CASE WHEN Field1 = 'DG3' THEN Field19 ELSE Field18 END AS Field18
,CASE WHEN Field1 = 'DG3' THEN Field20 ELSE Field19 END AS Field19
,CASE WHEN Field1 = 'DG3' THEN Field21 ELSE Field20 END AS Field20
,CASE WHEN Field1 = 'DG3' THEN Field22 ELSE Field21 END AS Field21
,CASE WHEN Field1 = 'DG3' THEN Field23 ELSE Field22 END AS Field22
,CASE WHEN Field1 = 'DG3' THEN Field24 WHEN Field1 = 'DG4' THEN Field43 ELSE Field23 END AS Field23
,CASE WHEN Field1 = 'DG3' THEN Field25 WHEN Field1 = 'DG4' THEN Field44 ELSE Field24 END AS Field24
,CASE WHEN Field1 = 'DG3' THEN Field44 WHEN Field1 = 'DG4' THEN Field45 ELSE Field25 END AS Field25
,CASE WHEN Field1 = 'DG3' THEN Field46 WHEN Field1 = 'DG4' THEN Field47 ELSE Field27 END AS Field27
,CASE WHEN Field1 = 'DG3' THEN Field48 WHEN Field1 = 'DG4' THEN Field49 ELSE Field29 END AS Field29
,CASE WHEN Field1 = 'DG3' THEN Field50 WHEN Field1 = 'DG4' THEN Field51 ELSE Field31 END AS Field31
,CASE WHEN Field1 = 'DG3' THEN Field57 WHEN Field1 = 'DG4' THEN Field58 ELSE Field38 END AS Field38
,CASE WHEN Field1 = 'DG3' THEN Field58 WHEN Field1 = 'DG4' THEN Field59 ELSE Field39 END AS Field39
,CASE WHEN Field1 = 'DG3' THEN Field59 WHEN Field1 = 'DG4' THEN Field60 ELSE Field40 END AS Field40
,CASE WHEN Field1 = 'DG3' THEN Field60 WHEN Field1 = 'DG4' THEN Field61 ELSE Field41 END AS Field41
Is there any way that I could simplify this without having to edit the original document?

On SQL Server I find using = to set alias way easier to read:
SELECT
Field1 = Field1,
Field2 = CASE WHEN Field1 = 'DG3' THEN Field3 ELSE Field2 END,
Field3 = CASE WHEN Field1 = 'DG3' THEN Field4 ELSE Field3 END,
Field9 = CASE WHEN Field1 = 'DG3' THEN Field10 ELSE Field9 END,
Field10 = CASE WHEN Field1 = 'DG3' THEN Field11 ELSE Field10 END,
Field11 = CASE WHEN Field1 = 'DG3' THEN Field12 ELSE Field11 END,
Field12 = CASE WHEN Field1 = 'DG3' THEN Field13 ELSE Field12 END,
--...
Field23 = CASE WHEN Field1 = 'DG3' THEN Field24 WHEN Field1 = 'DG4' THEN Field43 ELSE Field23 END,
Field24 = CASE WHEN Field1 = 'DG3' THEN Field25 WHEN Field1 = 'DG4' THEN Field44 ELSE Field24 END,
Field25 = CASE WHEN Field1 = 'DG3' THEN Field44 WHEN Field1 = 'DG4' THEN Field45 ELSE Field25 END,
Field27 = CASE WHEN Field1 = 'DG3' THEN Field46 WHEN Field1 = 'DG4' THEN Field47 ELSE Field27 END
--...
Can also use IIF for a simple CASE (SQL Server 2012+):
SELECT
Field1 = Field1,
Field2 = IIF(Field1 = 'DG3', Field3, Field2),
Field3 = IIF(Field1 = 'DG3', Field4, Field3),
Field9 = IIF(Field1 = 'DG3', Field10, Field9),
Field10 = IIF(Field1 = 'DG3', Field11, Field10),
--...
Field23 = IIF(Field1 = 'DG3', Field24, IIF(Field1 = 'DG4', Field43, Field23)),
--...
If performance isn't an issue and you have a key, you could LEFT JOIN the records with the Field1 = 'DG3' and avoid doing this check on every CASE (you will need ISNULL or COALESCE).

You can avoid repeating Field1 so many times by putting Field1 before WHEN
select
Field1
,CASE Field1 WHEN 'DG3' THEN Field3 ELSE Field2 END AS Field2
,CASE Field1 WHEN 'DG3' THEN Field4 ELSE Field3 END AS Field3
,CASE Field1 WHEN 'DG3' THEN Field10 ELSE Field9 END AS Field9
,CASE Field1 WHEN 'DG3' THEN Field11 ELSE Field10 END AS Field10
,CASE Field1 WHEN 'DG3' THEN Field12 ELSE Field11 END AS Field11
,CASE Field1 WHEN 'DG3' THEN Field13 ELSE Field12 END AS Field12
,CASE Field1 WHEN 'DG3' THEN Field15 ELSE Field14 END AS Field14
,CASE Field1 WHEN 'DG3' THEN Field16 ELSE Field15 END AS Field15
,CASE Field1 WHEN 'DG3' THEN Field17 ELSE Field16 END AS Field16
,CASE Field1 WHEN 'DG3' THEN Field18 ELSE Field17 END AS Field17
,CASE Field1 WHEN 'DG3' THEN Field19 ELSE Field18 END AS Field18
,CASE Field1 WHEN 'DG3' THEN Field20 ELSE Field19 END AS Field19
,CASE Field1 WHEN 'DG3' THEN Field21 ELSE Field20 END AS Field20
,CASE Field1 WHEN 'DG3' THEN Field22 ELSE Field21 END AS Field21
,CASE Field1 WHEN 'DG3' THEN Field23 ELSE Field22 END AS Field22
,CASE Field1 WHEN 'DG3' THEN Field24 WHEN 'DG4' THEN Field43 ELSE Field23 END AS Field23 ...

Related

Postgresql moving data from table to 3 other tables

I have a table:
TABLE_A
id_table_a
field1
field2
field3
field4
field5
I need to move the data to 3 tables, 1 parent with 2 children:
TABLE_B
id_table_b
id_table_a
field1
field2
TABLE_C
id_table_c
id_table_b
field3
field4
TABLE_D
id_table_d
id_table_b
field5
We're talking about millions of registers. What would be the correct and most effective way to do this?
I'm completely new to PostgreSQL and I've come up with this after reading the documentation:
INSERT INTO table_b (id_table_a, field1, field2) SELECT id_table_a FROM table_a, SELECT field1 FROM table_a, SELECT field2 FROM table_a;
INSERT INTO table_c (id_table_b, field3, field4) SELECT id_table_b FROM table_b, SELECT field3 FROM table_a WHERE table_b.id_table_a = table_a.id_table_a, SELECT field4 FROM table_a WHERE table_b.id_table_a = table_a.id_table_a;
INSERT INTO table_d (id_table_d, field5) SELECT id_table_c FROM table_c, SELECT field5 FROM table_a WHERE table_b.id_table_a = table_a.id_table_a;
Would this do what I need or am I missing something? Thank you.
This will not work:
INSERT INTO table_b (id_table_a, field1, field2) SELECT id_table_a FROM table_a, SELECT field1 FROM table_a, SELECT field2 FROM table_a;
because the query SELECT id_table_a FROM table_a will (or can) return more than 1 value.
You need to write it like:
INSERT INTO table_b (id_table_a, field1, field2)
SELECT id_table_a, field1, field2 FROM table_a;
Maybe sub-optimal but a straightforward PL/pgSQL do block will help. Pls. note the returning into clause. Assuming that id_table_b, id_table_c and id_table_d are autogenerated integers, then
DO language plpgsql
$$
declare
r record;
var_id_table_b integer;
begin
for r in select * from table_a loop
insert into table_b (id_table_a, field1, field2)
values (r.id_table_a, r.field1, r.field2)
RETURNING id_table_b INTO var_id_table_b;
insert into table_c (id_table_b, field3, field4)
values (var_id_table_b, r.field3, r.field4);
insert into table_d (id_table_b, field5)
values (var_id_table_b, r.field5);
end loop;
end;
$$;

How to change field value to null for certain conditions in a query?

I have a query where I select data from a table using TSQL. When one of the fields (field1) has a certain value, I want to change the value of two other fields (field2 and field3) to NULL depending on the value of field1. I've browsed stack overflow and see many answers saying to UPDATE the table like this:
UPDATE MyTable
SET MyField = NULL
WHERE MyField = ''
But Update changes a table, right? I don't want to change my table data. I want to change my query results. Is there a way to change results of a query like I want?
If you have table field1 | field2 | field3 and you want to query field2 and field3, but in the case of field1 is NULL, these two other field should be also NULL. So in your case:
SELECT field1
, CASE WHEN field1 IS NULL THEN NULL ELSE field2 END AS field2
, CASE WHEN field1 IS NULL THEN NULL ELSE field3 END AS field3
FROM dbo.MyTable
Update (1) after clarification in comment:
SELECT field1
, CASE WHEN field1 = 'IR' THEN NULL ELSE field2 END AS field2
, CASE WHEN field1 <> 'IR' THEN NULL ELSE field3 END AS field3
FROM dbo.MyTable
As described in CASE (Transact-SQL) documentation:
WITH Data (value) AS
(
SELECT 0
UNION ALL
SELECT 1
)
SELECT
CASE
WHEN MIN(value) <= 0 THEN 0
WHEN MAX(1/value) >= 100 THEN 1
END
FROM Data ;
Or in your case:
SELECT
MyField = CASE
WHEN MyField = '' THEN NULL
ELSE MyField
END
FROM MyTable
I did test runs just adding the two CASE statements to the Select part of my main query. Worked well. Thanks for the help!
Here are the exact statements I used. I named the resulting fields Case# and Payee instead of CaseNo and VendorName, because SQL was calling the results CaseNo1 and VendorName1:
CASE WHEN Accounting_Categories.Name LIKE 'I%' then NULL Else CaseNo END as Case#,
CASE WHEN Accounting_Categories.Name LIKE 'I%' then VendorName Else NULL END as Payee

SELECT with CASE WHEN and value after THEN instead of String

My query is:
SELECT
id,
CASE WHEN EXISTS(
SELECT
data_od
FROM
bp_stan_produkt
WHERE
id_produkt = bp_produkt.id AND data_do IS NULL AND id_stan_produkt = 313
) THEN 'TAK' ELSE 'NIE'
END AS "313"
FROM
bp_produkt
WHERE
id IN(21048528)
Is it possible to put data_od to column 313 instead of TAK or do I have to create function, make SELECT data_od into some_variable and then some_variable?
Yes it is possible:
select id,
case when exists (select data_od from bp_stan_produkt
where id_produkt = bp_produkt.id and data_do is null
and id_stan_produkt = 313)
then (select to_char(data_od, 'YYYY-MM-DD')
from bp_stan_produkt
where id_produkt = bp.id
and data_do is null
and id_stan_produkt = 313)
else 'NIE'
end as "313"
from bp_produkt
where id in(21048528);
EDIT:
SELECT bp.id, COALESCE(t.data_od_t, 'NIE') AS "313"
FROM bp_produkt bp
LEFT JOIN LATERAL (select to_char(data_od, 'YYYY-MM-DD') AS data_od_t
from bp_stan_produkt
where id_produkt = bp.id
and data_do is null
and id_stan_produkt = 313) t
ON TRUE
WHERE bp.id in(21048528)

SQL Statement with IN CASE

SELECT user
FROM userlist zH with(nolock)
where zH.user in (case when zh.trait='1' then ('B', 'HO', 'KO', 'PL','APP','2A','2B') else ('O') end)
can this statement with the where-in-case work? i hope you get what i meant. thanks.
You can use nested case statement, like
SELECT user
FROM userlist zH with(nolock)
where 'true' =
(case when zh.trait = '1'
then
case when zH.user in ('B', 'HO', 'KO', 'PL','APP','2A','2B')
then 'true'
else 'false'
end
else
case when zH.user = 'O'
then 'true'
else 'false'
end
end)
SELECT [user]
FROM userlist zH WITH ( NOLOCK )
WHERE ( zh.trait = '1'
AND zH.[user] IN ( 'B', 'HO', 'KO', 'PL', 'APP', '2A', '2B' )
)
OR ( zh.trait <> '1'
AND zH.[user] IN ( 'O' )
)

How to design T-SQL query to calculate sum in one pass?

I am trying to develop a T-SQL query which will do the following:
ROUND(100 * A / B, 1)
Simple in concept, but it's tricky because of possible B=0 denominator and also because of A and B variables. What I expect is a percent value like 93.2 (given in this format without %). Or even 932 would be acceptable since I could convert it later.
But instead, I'm currently getting 151, which is the number of records.
A = CASE WHEN A.MFG IS NULL AND A.MFG2 IS NULL AND A.QC IS NULL AND A.QC2 IS NULL THEN 1 ELSE 0 END
B = CASE WHEN [Date_Completed] IS NOT NULL THEN 1 ELSE 0 END
My current logic only divides A/B if B is not equal to zero. Can you please help me fix this? p.s. all fields above are from the same table A.
I tried:
SELECT CASE WHEN t.VarB<>0 THEN ROUND(100 * t.VarA / t.VarB, 1)
ELSE 0 /* or whatever you'd want to return in this case */
END
FROM (SELECT CASE WHEN A.MFG IS NULL AND A.MFG2 IS NULL AND A.QC IS NULL AND A.QC2 IS NULL THEN 1
ELSE 0
END AS VarA,
CASE WHEN [Date_Completed] IS NOT NULL THEN 1
ELSE 0
END AS VarB
FROM EXCEL.Batch_Records A) t
But I got 33000 rows returned instead of just one, where each row = 100 or 0.
Good idea, Conrad! I tested your solution and it works if I just want that one value. But what I didn't tell you was that there are additional values I need returned from same query. When I tried adding in the other value calculations, I got syntax errors. So here is my current query. How should htis be rewritten please?
select
SUM(CASE WHEN A.DATE_RECEIVED IS NOT NULL THEN 1 ELSE 0 END) AS NUM_RECEIVED,
SUM(CASE WHEN [Date_Completed] IS NOT NULL THEN 1 ELSE 0 END) AS NUM_COMPLETE_OF_OPENED,
SUM(CASE WHEN A.DATE_COMPLETED IS NOT NULL THEN 1 ELSE 0 END) AS NUM_COMPLETED_IN_MONTH,
SUM(CASE WHEN A.MFG IS NULL AND A.MFG2 IS NULL AND A.QC IS NULL AND A.QC2 IS NULL THEN 1 ELSE 0 END) AS NUM_WITHOUT_ERROR,
round(100 * a/b , 1)
from
(select
sum(CASE
WHEN A.MFG IS NULL AND A.MFG2 IS NULL AND A.QC IS NULL AND A.QC2 IS NULL THEN
1.0
ELSE 0.0 END) A,
sum(CASE WHEN [Date_Completed] IS NOT NULL THEN
1.0 ELSE 0.0 END) B
FROM EXCEL.Batch_Records a
LEFT JOIN EXCEL.QC_CODES d ON a.Part_Number = d.CODE_ID
WHERE (a.[Group] = #GROUP or #GROUP = '' OR #GROUP IS NULL) AND A.Date_Received >= #STARTDATE AND A.Date_Received <= #ENDDATE
Conrad correctly advised me that #TEMP1 was an empty table. But now I populated it and successfully designed this query with his help:
SET #STARTDATE = '1/1/11'
SET #ENDDATE = '1/31/11'
SET #GROUP = 'INTERMEDIATES_FISH'
--SET #TABLE_TITLE = 'BATCH RECORD SUCCESS RATE'
--SET #DEPT = 'QC'
IF EXISTS(SELECT * FROM TEMPDB.INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE '#TEMP1%')
DROP TABLE #TEMP1
--CREATE TABLE #TEMP1 ( MFG int , MFG2 int , QC int, QC2 INT , [Group] NVARCHAR(MAX), [Date_Completed] datetime, Date_Received datetime)
SELECT
MFG, MFG2, QC, QC2, [GROUP], [DATE_COMPLETED], [DATE_RECEIVED]
INTO #TEMP1
FROM EXCEL.Batch_Records a
WHERE (a.[Group] = #GROUP or #GROUP = '' OR #GROUP IS NULL) AND A.Date_Received >= #STARTDATE AND A.Date_Received <= #ENDDATE
------------------------------------------
;WITH CTE AS
(
SELECT
CASE
WHEN A.MFG IS NULL AND A.MFG2 IS NULL AND A.QC IS NULL AND A.QC2 IS NULL THEN
1.0
ELSE 0.0 END A,
CASE WHEN [Date_Completed] IS NOT NULL THEN 1.0 ELSE 0.0 END B,
CASE WHEN A.Date_Received IS NOT NULL THEN 1 ELSE 0 END NUM_RECEIVED,
CASE WHEN [Date_Completed] IS NOT NULL THEN 1 ELSE 0 END NUM_COMPLETE_OF_OPENED,
CASE WHEN A.DATE_COMPLETED IS NOT NULL THEN 1 ELSE 0 END NUM_COMPLETED_IN_MONTH,
CASE WHEN A.MFG IS NULL AND A.MFG2 IS NULL AND A.QC IS NULL AND A.QC2 IS NULL THEN 1 ELSE 0 END AS NUM_WITHOUT_ERROR
FROM
#TEMP1 a
--WHERE (a.[Group] = #GROUP or #GROUP = '' OR #GROUP IS NULL) AND A.Date_Received >= #STARTDATE AND A.Date_Received <= #ENDDATE
)
select
round(100 * SUM(A)/SUM(b) , 1) ,
SUM(NUM_RECEIVED) NUM_RECEIVED,
SUM(NUM_COMPLETE_OF_OPENED) NUM_COMPLETE_OF_OPENED,
SUM(NUM_COMPLETED_IN_MONTH) NUM_COMPLETED_IN_MONTH,
SUM(NUM_WITHOUT_ERROR) NUM_WITHOUT_ERROR
FROM CTE
Basically you need to use SUM() to get the sum. You should also use 1.0 and 0.0 so you get decimal values.
You should also do the SUM before the Division
UPDATE
Since you're adding in a number of SUM(CASE statements its probably more readable to move the CASE statments out to a CTE.
CREATE TABLE #Batch_Records (
MFG int ,
MFG2 int ,
QC int,
QC2 INT ,
[Group] int,
[Date_Completed] datetime,
Date_Received datetime)
INSERT INTO #Batch_Records (MFG , MFG2 , QC , QC2 , [Group] , [Date_Completed] , Date_Received )
VALUES (1,null,null,null,1,'1/4/2011','2/4/2011'),
(null,null,null,null,1,'2/2/2011','3/4/2011'),
(1,null,null,null,1,'3/6/2011','4/3/2011'),
(null,null,null,null,1,NULL,'5/4/2011'),
(1,null,null,null,1,'5/4/2011','6/6/2011'),
(1,null,null,null,1,NULL,'7/4/2011')
DECLARE #GROUP int
DECLARE #STARTDATE DateTime
DECLARE #ENDDATE DateTime
SET #GROUP = 1
SET #STARTDATE = '1/1/2001'
SET #ENDDATE = '1/1/2012'
;WITH CTE AS
(
SELECT
CASE
WHEN A.MFG IS NULL AND A.MFG2 IS NULL AND A.QC IS NULL AND A.QC2 IS NULL THEN
1.0
ELSE 0.0 END A,
CASE WHEN [Date_Completed] IS NOT NULL THEN
1.0 ELSE 0.0 END B,
CASE WHEN A.Date_Received IS NOT NULL THEN 1 ELSE 0 END NUM_RECEIVED,
CASE WHEN [Date_Completed] IS NOT NULL THEN 1 ELSE 0 END NUM_COMPLETE_OF_OPENED,
CASE WHEN A.DATE_COMPLETED IS NOT NULL THEN 1 ELSE 0 END NUM_COMPLETED_IN_MONTH,
CASE WHEN A.MFG IS NULL AND A.MFG2 IS NULL AND A.QC IS NULL AND A.QC2 IS NULL THEN 1 ELSE 0 END AS NUM_WITHOUT_ERROR
FROM
#Batch_Records a
WHERE
(a.[Group] = #GROUP or #GROUP = '' OR #GROUP IS NULL)
AND A.Date_Received >= #STARTDATE AND A.Date_Received <= #ENDDATE
)
select
round(100 * SUM(A)/SUM(b) , 1) ,
SUM(NUM_RECEIVED) NUM_RECEIVED,
SUM(NUM_COMPLETE_OF_OPENED) NUM_COMPLETE_OF_OPENED,
SUM(NUM_COMPLETED_IN_MONTH) NUM_COMPLETED_IN_MONTH,
SUM(NUM_WITHOUT_ERROR) NUM_WITHOUT_ERROR
FROM CTE
DROP TABLE #Batch_Records