SQL - condensing repeative boolan 'WHEN' statements - sql-server-2005

please can anyone suggest a way of condensing this code, to reduce its repetitive nature. many thanks
select case
when c=1 and cs=1 and f=0 and fs=0 then 'FPL02'
when c=0 and cs=0 and f=1 and fs=1 then 'FPL03'
when c=1 and cs=0 and f=0 and fs=0 then 'FPL04'
when c=0 and cs=0 and f=1 and fs=0 then 'FPL05'
when c=1 and cs=1 and f=1 and fs=1 then 'FPL06'
when c=1 and cs=1 and f=1 and fs=0 then 'FPL07'
when c=1 and cs=0 and f=1 and fs=1 then 'FPL08'
when c=1 and cs=0 and f=1 and fs=0 then 'FPL09'
when Ab=1 then 'FPL10'
when cpc=1 and plo=0 then 'FPC01'
when cpc=0 and plo=1 then 'FPC02'
when cpc=1 and plo=1 then 'FPC03'
else 'FPL01' end
from (select ptmatter, BillLHAbsolute as Ab, BillLHChildren as C, BillLHChildrenSettle as CS, BillLHFinances as F, BillLHFinancesSettle as FS, BillLHCPC as CPC, BillLHPLO as PLO from MatterDataDef) as mmd
where ptmatter=$matter$

With that many different conditional statements on different columns, I sincerely doubt you can condense that code while having it still be maintainable by someone else.
For example, you would need this:
select case
when c IN (0, 1) AND cs IN (0, 1) AND f IN (0, 1) AND fs IN (0, 1) then
case
when c=1 and cs=1 and f=1 and fs=0 then 'FPL07'
when c=1 and cs=0 and f=1 and fs=0 then 'FPL09'
else 'FPL0' + cast(c * 5 + f * 6 - cs * 2 - fs * 2 - 1 as char(1))
end
when Ab = 1 then
'FPL10'
when cpc IN (0, 1) AND plo IN (0, 1) then
'FPC0' + cast(cpc * 1 + plo * 2 as char(1))
else
'FPL01'
end
It's condensed (sort of), but you're trading off fewer lines for less readability.
All in all, it's really not that many WHEN statements.

Related

Write a query to print all prime numbers less than or equal to

Write a query to print all prime numbers less than or equal to. Print your result on a single line, and use the ampersand (&) character as your separator (instead of a space).
For example, the output for all prime numbers (<= 10) would be:
Output : 2&3&5&7
Different RDBMS have their own syntax / functions for RCTE and string aggregation.
Below is an example for PostgreSQL:
WITH RECURSIVE T (I) AS (SELECT 1 UNION ALL SELECT I+1 FROM T WHERE I < 10)
SELECT STRING_AGG (I::TEXT, '&') AS LST
FROM
(
SELECT A.I
FROM T A
LEFT JOIN T B ON B.I BETWEEN 2 AND A.I - 1
-- Actually, 1 is prime as well...
WHERE A.I > 1
GROUP BY A.I
HAVING COUNT (CASE MOD (A.I, B.I) WHEN 0 THEN 1 END) = 0
ORDER BY A.I
) A
LST
2&3&5&7

sql statement update multiple values in multiple rows in complex where statement

Assume i have 6 columns named a,b,c,d,e,f in a table X
my logic is this:
if a=0 and b-0 and c=0 then d=0 e=0 f=0
if a=0 and b-0 and c=1 then d=0 e=0 f=1
if a=0 and b-0 and c=2 then d=0 e=0 f=8
if a=1 and b-0 and c=2 then d=0 e=555 f=8
etc
how do i build it in one single query statement
the DB is postgresql
I need to update like that around 10K records
You can do the following:
update x
set d = t.d,
e = t.e,
f = t.f
from (
values
(0,0,0,0,0,0),
(0,0,1,0,0,1),
(0,0,2,0,0,8),
(1,0,2,0,555,8)
) as t(a,b,c,d,e,f)
where x.a = t.a
and x.b = t.b
and x.c = t.c
;
Online example: http://rextester.com/APBVYG17890
Personally, I recommend a completely different approach. If you created a table that allowed you to crosswalk the values, it would be re-usable, concise, and easy to maintain.
Example:
CREATE TABLE t (
a INT
, b INT
, c INT
, d INT
, e INT
, f INT
)
INSERT INTO t (a,b,c,d,e,f) VALUES (0,0,0,0,0,0);
INSERT INTO t (a,b,c,d,e,f) VALUES (0,0,1,0,0,1);
INSERT INTO t (a,b,c,d,e,f) VALUES (0,0,2,0,0,8);
INSERT INTO t (a,b,c,d,e,f) VALUES (1,0,2,0,555,8);
SELECT * FROM t
You can simply join to this table on those three values and get the value you want to for d, e, and f.

SQL query to get the count by applying group by

I want to get the below result:
source table :
Cnt A B
4 ABC YU/FGH
5 ABC YU/DFE
5 ABC KL
2 LKP BN/ER
4 JK RE
Result:
Cnt A B
9 ABC YU
5 ABC KL
2 LKP BN
4 JK RE
Here I want the count by grouping 'B' and want to display the 'B' record only till the special character (/)
Basically, you will have to filter out the all the characters after the "/" symbol and then apply a SUM and a GROUP BY. You can see this below. The inner query filters out the unwanted string and the outer query does the SUM and the GROUP BY :
SELECT SUM(t.Cnt), t.A, t.B
FROM (
SELECT Cnt,
A,
CASE
WHEN CHARINDEX('/', B) > 0 THEN SUBSTRING(B, 0, CHARINDEX('/', B))
ELSE B
END AS B
FROM #Tab
) t
GROUP BY t.A, t.B
ORDER BY t.A
You can see this working here -> http://rextester.com/IQJ79191
Hope this helps!!!
You can get your string till '/' by using SUBSTRING.
select
count(SUBSTRING(reverse(B),0,charindex('/',reverse(B)))),
A,
SUBSTRING(reverse(B),0,charindex('/',reverse(B)))
from source_table group by B;
Solution for Oracle - substr(B,0,instr(B,'/',1)-1) B
Put this both in select and groupby
I can suggest you to use a query like this:
select
sum(Cnt) Cnt,
A,
left(B, charindex('/',B+'/',0)-1) B -- Using `+'\'` will do the trick
from
t
group by
A,
left(B, charindex('/',B+'/',0)-1);
By using String and CharIndex Functions.
;WITH SourceTable(Cnt,A,B) AS
(
SELECT 4,'ABC','YU/FGH'UNION ALL
SELECT 5,'ABC','YU/DFE'UNION ALL
SELECT 5,'ABC','KL' UNION ALL
SELECT 2,'LKP','BN/ER' UNION ALL
SELECT 4,'JK','RE'
)
SELECT SUM(Cnt) AS Cnt,A,CASE WHEN CHARINDEX('/',B) = 0 THEN B
ELSE SUBSTRING(B,0,CHARINDEX('/',B)) END AS [B] FROM SourceTable
GROUP BY A,CASE WHEN CHARINDEX('/',B) = 0 THEN B
ELSE SUBSTRING(B,0,CHARINDEX('/',B)) END
ORDER BY Cnt DESC
Try this query --
SELECT SUM(Cnt) AS [COUNT]
,A
,CASE
WHEN CHARINDEX('/', B) > 0
THEN SUBSTRING(B, 1, (CHARINDEX('/', B) - 1))
ELSE B
END
FROM tblSample
GROUP BY A, B
ORDER BY A, B

find a line based on string pattern and move it X places

I want to move the 1st instance of AND F.col_x IS NOT NULL pattern ( for each of the blocks 23 24 25 etc ) that follows the WHERE F.col_x = D.col_x pattern either of these ways
--Look for brackets Just before group by and add it in there
--alternately move that line 1 line below from where it was taken.
Either way the results would be the same
INPUT
*22
Select ((MYLILFUNC(F.col_x,-99999))) AS WIDTH,
COUNT(*) AS SIZE
FROM MYDB.BGSQLTB F where NOT EXISTS ( sel '1' from MYDB.col_x D
WHERE F.col_x = D.col_x
AND F.col_x IS NOT NULL
AND D.col_x IS NOT NULL )
GROUP BY F.col_x;
*23
Select ((MYLILFUNC(F.COL_y,-99999))) AS WIDTH,
COUNT(*) AS SIZE
FROM MYDB.BGSQLTB F where NOT EXISTS ( sel '1' from MYDB.DIM_DRG_CODE D
WHERE F.COL_y = D.COL_y
AND F.COL_y IS NOT NULL
AND D.COL_y IS NOT NULL )
GROUP BY F.COL_y;
*24
Select ((MYLILFUNC(F.COL_Z,-99999))) AS WIDTH,
COUNT(*) AS SIZE
FROM MYDB.BGSQLTB F where NOT EXISTS ( sel '1' from MYDB.COL_Z D
WHERE F.COL_Z = D.COL_Z
AND F.COL_Z IS NOT NULL
AND D.COL_Z IS NOT NULL )
GROUP BY F.COL_Z;
*25
Select ((MYLILFUNC(F.COL_XXX,-99999))) AS WIDTH,
COUNT(*) AS SIZE
FROM MYDB.BGSQLTB F where NOT EXISTS ( sel '1' from MYDB.COL_XX D
WHERE F.COL_XXX = D.COL_XXX
AND F.COL_XXX IS NOT NULL
AND D.COL_XXX IS NOT NULL )
GROUP BY F.COL_XXX;
OUTPUT
*22
Select ((MYLILFUNC(F.col_x,-99999))) AS WIDTH,
COUNT(*) AS SIZE
FROM MYDB.BGSQLTB F where NOT EXISTS ( sel '1' from MYDB.col_x D
WHERE F.col_x = D.col_x
AND D.col_x IS NOT NULL ) AND F.col_x IS NOT NULL
GROUP BY F.col_x;
*23
Select ((MYLILFUNC(F.COL_y,-99999))) AS WIDTH,
COUNT(*) AS SIZE
FROM MYDB.BGSQLTB F where NOT EXISTS ( sel '1' from MYDB.DIM_DRG_CODE D
WHERE F.COL_y = D.COL_y
AND D.COL_y IS NOT NULL ) AND F.COL_y IS NOT NULL
GROUP BY F.COL_y;
*24
Select ((MYLILFUNC(F.COL_Z,-99999))) AS WIDTH,
COUNT(*) AS SIZE
FROM MYDB.BGSQLTB F where NOT EXISTS ( sel '1' from MYDB.COL_Z D
WHERE F.COL_Z = D.COL_Z
AND D.COL_Z IS NOT NULL ) AND F.COL_Z IS NOT NULL
GROUP BY F.COL_Z;
*25
Select ((MYLILFUNC(F.COL_XXX,-99999))) AS WIDTH,
COUNT(*) AS SIZE
FROM MYDB.BGSQLTB F where NOT EXISTS ( sel '1' from MYDB.COL_XX D
WHERE F.COL_XXX = D.COL_XXX
AND D.COL_XXX IS NOT NULL ) AND F.COL_XXX IS NOT NULL
GROUP BY F.COL_XXX;
My search pattern using Ed is a bit too wide and takes more lines and I am not sure how I can get the moving logic done because it is relative to each selected line.
You can do this in a couple ways. With sed you can do
sed -e '/AND F\.[a-zA-Z_]* *IS *NOT *NULL/ { h; d }; /GROUP BY/ { H; x }'
What happens is anytime the first regular expression matches, the { h; d }; commands store the line in the hold buffer and move to the next line without outputting anything. Whenever the second regexp matches, the { H; x } append the current line to the hold buffer with a newline in between and then swap the hold buffer and the current line buffer. Then sed will automatically print out the pattern line. It's easy for this not to work correctly depending on your input, but it works fine for the sample you provided.
In awk it would be
awk 'tolower($0) ~ /and f.col_[a-z]* is not null/ {save = $0; next} /GROUP BY/ { print save } {print}'

Conditional row subtraction

I'm using sql server 2005.
I have two columns a and b.
I wish to subtract b from a to produce c, which I do as follows:
a - b as c
but I want to complement this by having c = 0 if a or b is 0. How do I do this?
Thanks,
Barry
Try using case
SELECT
CASE WHEN a = 0 OR b = 0
THEN 0
ELSE a - b
END c
More on this
http://msdn.microsoft.com/en-us/library/ms181765.aspx
Using a CASE statement is definitly the most recommended way but for fun and pleasure, using some bit shifting would work as well
c = ABS(a - b)
* (((CAST(ABS(a * b) AS BIGINT)+0x7FFFFFFF))
/ POWER(2, 16) / POWER(2, 15))
Steps taken
Multiply to get either a zero (a or b is zero) or non-zero (a and b both <> 0) value. The non-zero value needs to get converted to 1
ABS(a * b)
Cast to BIGINT to prevent Arithmetic overflow
CAST(ABS(a * b) AS BIGINT)
Make sure bit 32 is set to 1 if ABS(a * b) was a non-zero value.
CAST(ABS(a * b) AS BIGINT)+0x7FFFFFFF
Shift 31 bits retaining either a 0 or 1. (Because POWER returns an int this has to be done in two steps instead of a simpler POWER(2, 31))
((CAST(ABS(a * b) AS BIGINT)+0x7FFFFFFF))
/ POWER(2, 16) / POWER(2, 15)
Multiply the original equation with our calculated 0 or 1.
ABS(a - b)
* (((CAST(ABS(a * b) AS BIGINT)+0x7FFFFFFF))
/ POWER(2, 16) / POWER(2, 15))
Test script
;WITH q (a, b) AS (
SELECT * FROM (VALUES
(0, 0)
, (0, 1)
, (0, 2)
, (1, 0)
, (1, 1)
, (1, 2)
, (2, 0)
, (2, 1)
, (2, 2)
, (9, 0)
, (9, 1)
, (9, 2)
) a (b, c)
)
SELECT a
, b
, c = ABS(a - b)
* (((CAST(ABS(a * b) AS BIGINT)+0x7FFFFFFF))
/ POWER(2, 16) / POWER(2, 15))
FROM q
Edit
As in comments we wondered about performance differences, following is a quick test setup.
Performance test setup
CREATE TABLE q (a INTEGER, b INTEGER)
;WITH numbers (a) AS (
SELECT 0
UNION ALL
SELECT a + 1
FROM numbers
WHERE a < 999
)
INSERT INTO q
SELECT a1.a, a2.a
FROM numbers a1
CROSS APPLY numbers a2
OPTION (MAXRECURSION 0)
Performance Test
SET STATISTICS IO ON
SET STATISTICS TIME ON
SELECT c = (a - b)
* (((CAST(ABS(a * b) AS BIGINT)+0x7FFFFFFF))
/ POWER(2, 16) / POWER(2, 15))
FROM q
SELECT
CASE WHEN a = 0 OR b = 0
THEN 0
ELSE a - b
END c
FROM q
Well, this should do it :)
SELECT
CASE WHEN A=0 OR B=0 THEN 0 ELSE A-B END AS C
FROM
TABLE
Try his:
SELECT
a,b,
CASE
WHEN a = 0 OR b = 0 THEN 0
ELSE
a - b
END AS c
FROM table
SELECT a, b,
COALESCE(NULLIF(a, 0) - NULLIF(b, 0), 0) AS c
FROM T1;
Or
SELECT a, b, 0 AS c
FROM T1
WHERE 0 IN (a, b)
UNION
SELECT a, b, a - b AS c
FROM T1
WHERE 0 NOT IN (a, b);
Note each handles nulls differently (you spec does not mention how to handle nulls).