T-SQL If condition inside select - sql

I have a condition where there are multiple scenarios and I need to check each scenario. I need to concatenate the result if two or more cases matches.
What I thought was , using CASE statement but i am unable to concatenate multiple scenarios using CASE statement.
So what I am doing now is , using if statement . Below is the query i did:
declare #result varchar(200)
set #result=''
;with cte as
(
select * from table 1
)
if(column 1=5) set #result=#result+'case1'
if(column 2=6) set #result=#result+'case2'
if(column 3=7) set #result=#result+'case3'
if(column 4=10) set #result=#result+'case4'
select *,#result from cte
So here, i need to use Select statement right after CTE but i cannot use IF/ELSE statement in select statement . But also i cannot use the CASE Statement with variable concatenate.
So the result should be like :
id | column 1 | column 2| column 3| column 4| Result
-----------------------------------------------------
3 | 5 | 6 | 6 | 9 | case1;`case2
4 | 4 | 7 | 7 | 10 | case2
5 | 5 | 6 | 6 | 10 | case1;`case2; case4
6 | 5 | 6 | 7 | 10 | case2;case2;case3;case4
7 | 4 | 5 | 6 | 3 | No Result
Anyone could help me to complete this ?

You can concatenate multiple CASEs in a single SELECT:
SELECT
CASE WHEN 1=1 THEN 'Case1' ELSE '' END
+ CASE WHEN 2=2 THEN 'Case2' ELSE '' END
+ CASE ...
EDIT based on comments: If, as you say, your broken code using IF really would solve your issue, then there is no issue, since the if statements you used don't need to be in the SELECT at all. You could simply do this:
declare #result varchar(200)
set #result=''
if(1=1) set #result=#result+'case1'
if(2=2) set #result=#result+'case2'
;with cte as
(
select * from table 1
)
select *,#result from cte
EDIT based on the update to original question:
So my first solution is correct. The additional details in your question allow me to make it a little more clear. By the way you can't use a variable at all for this:
;with cte as
(
select * ,
CASE WHEN column1=5 THEN 'case1' ELSE '' END +
CASE WHEN column2=6 THEN 'case2' ELSE '' END +
CASE WHEN column3=7 THEN 'case3' ELSE '' END +
CASE WHEN column4=10 THEN 'case4' ELSE '' END AS result
from table 1
)
select *,
CASE WHEN result='' THEN 'No Result Found' ELSE result END as result
from cte
Note that if you need to separate the values with semi-colons (as shown in your question) you can put a semi-colon before each value (example ';case3'), and in the final SELECT, use STUFF() to remove the first semi-colon.

;WITH cte AS
(
SELECT * FROM table1
)
SELECT *,(
CASE
WHEN Column1 <> 5 AND Column2 <> 6 AND Column3 <> 7 AND Column4 <> 10 THEN 'No Result'
ELSE
CASE WHEN Column1 = 5 THEN 'Case 1;' ELSE '' END +
CASE WHEN Column2 = 6 THEN 'Case 2;' ELSE '' END +
CASE WHEN Column3 = 7 THEN 'Case 3;' ELSE '' END +
CASE WHEN Column4 = 10 THEN 'Case 4;' ELSE '' END
END
)Result
FROM cte
| id | Column1 | Column2 | Column3 | Column4 | Result |
|----|---------|---------|---------|---------|------------------------------|
| 3 | 5 | 6 | 6 | 9 | Case 1;Case 2; |
| 4 | 4 | 7 | 7 | 10 | Case 3;Case 4; |
| 5 | 5 | 6 | 6 | 10 | Case 1;Case 2;Case 4; |
| 6 | 5 | 6 | 7 | 10 | Case 1;Case 2;Case 3;Case 4; |
| 7 | 4 | 5 | 6 | 9 | No Result |
Check SQL Fiddle

Related

how to update multiple columns while comparing them between rows in a table

i have a table say
table 1
id | rel_id
123 | 456
789 | 321
so id column from table 1 = id from table 2 and will have catgy 180
rel_id column from table 1 = id from table 2 and will have catgy 181
table 2
id | catgy | spl_1 | spl_2 | spl_3 | spl 4
123 | 180 | 6 | 0 | 0 | 7
456 | 181 | 7 | 0 | 0 | 0
789 | 180 | 8 | 9 | 9 | 0
321 | 181 | 9 | 0 | 0 | 0
so i want to comapre spl_2, spl_3, spl_4 for id 123 with spl_1 for id 456 and if same then update id 123 spl's with null(in this case update spl_4 with null)
Thanks
You can just use a standard CASE expression for it:
Update YourTable
Set SPCLTY_2 = Case When SPCLTY_2 = SPCLTY_1 Then Null Else SPCLTY_2 End,
SPCLTY_3 = Case When SPCLTY_3 = SPCLTY_1 Then Null Else SPCLTY_3 End,
SPCLTY_4 = Case When SPCLTY_4 = SPCLTY_1 Then Null Else SPCLTY_4 End,
SPCLTY_5 = Case When SPCLTY_5 = SPCLTY_1 Then Null Else SPCLTY_5 End
When you need to update values in one table based on values from another table (or the same table, or the result of a join, etc. - from another relation* would be the most general formulation), it is often easiest to do it with the MERGE statement. This is a perfect example.
*Relation is a fancy term for a table or anything that resembles
one, such as the result of a join, an aggregate operation, or really
any kind of SELECT statement.
merge into table_2 t2
using
( select t1.id, s.spl_1
from table_1 t1 join table_2 s on t1.rel_id = s.id
) x
on ( x.id = t2.id )
when matched then update
set t2.spl_2 = case when t2.spl_2 = x.spl_1 then null else t2.spl_2 end,
t2.spl_3 = case when t2.spl_3 = x.spl_1 then null else t2.spl_3 end,
t2.spl_4 = case when t2.spl_4 = x.spl_1 then null else t2.spl_4 end
where x.spl_1 in (t2.spl_2, t2.spl_3, t2.spl_4)
-- WHERE clause so that you only update rows that need to be updated!
;
2 rows merged.
select * from table_2;
ID CATGY SPL_1 SPL_2 SPL_3 SPL_4
---------- ---------- ---------- ---------- ---------- ----------
123 180 6 0 0
456 181 7 0 0 0
789 180 8 0
321 181 9 0 0 0

SQL - Sum two columns group by ID

I would like to sum two columns "Immo"+"Conso" group by "ID" in order to create a new variable "Mixte". My new variable "Mixte" is as follow:
if one ID has (at least) 1 in "Immo" AND 1 in "Conso" then "Mixte" is yes, otherwise "Mixte" is no.
For exemple:
Ident | Immo | Conso | Mixte
---------------------------------
1 | 0 | 1 | yes
1 | 1 | 0 | yes
2 | 1 | 0 | no
3 | 0 | 1 | no
3 | 0 | 1 | no
3 | 0 | 1 | no
4 | 0 | 1 | yes
4 | 0 | 1 | yes
4 | 1 | 0 | yes
Thank you for helping me. Do not hesitate to ask me questions if I wasn't clear.
Use a correlated sub-select:
select t1.Ident, t1.Immo, t1.Conso,
case when (select max(Immo) + max(Conso) from tablename t2
where t2.Ident = t1.Ident) = 2 then 'yes'
else 'no'
end as Mixte
from tablename t1
Ident is a reserved word in ANSI SQL, so you may need to delimit it as "Ident".
select ident,result=(case when sum(Immo)>0 and sum(Conso)>0 then 'yes'
else 'no' end)
from tabname (NOLOCK)
group by id
It may not be the smoothiest way but I'll do this as:
WITH X AS
(
SELECT T.Ident, MAX(T.Immo) Immo, MAX(T.Conso) Conso FROM Table AS T
GROUP BY T.Ident
)
SELECT X.*
,CASE WHEN X.Immo > 0 AND X.Conso > 0 THEN 'YES'
ELSE 'NO'
END Mixte
FROM X
In SQL-Server you could try to use window functions, something like:
select Ident, Immo, Conso,
case when rn1 > 0 and rn2 > 0 then 'Yes' else 'No' end as Mixte
from (
select
max(Immo) over (partition by Ident) rn1,
max(Conso) over (partition by Ident) rn2,
*
from table_name
)x

How to pivot or 'merge' rows with column names?

I have the following table:
crit_id | criterium | val1 | val2
----------+------------+-------+--------
1 | T01 | 9 | 9
2 | T02 | 3 | 5
3 | T03 | 4 | 9
4 | T01 | 2 | 3
5 | T02 | 5 | 1
6 | T03 | 6 | 1
I need to convert the values in 'criterium' into columns as 'cross product' with val1 and val2. So the result has to lool like:
T01_val1 |T01_val2 |T02_val1 |T02_val2 | T03_val1 | T03_val2
---------+---------+---------+---------+----------+---------
9 | 9 | 3 | 5 | 4 | 9
2 | 3 | 5 | 1 | 6 | 1
Or to say differently: I need every value for all criteria to be in one row.
This is my current approach:
select
case when criterium = 'T01' then val1 else null end as T01_val1,
case when criterium = 'T01' then val2 else null end as T01_val2,
case when criterium = 'T02' then val1 else null end as T02_val1,
case when criterium = 'T02' then val2 else null end as T02_val2,
case when criterium = 'T03' then val1 else null end as T03_val1,
case when criterium = 'T03' then val2 else null end as T04_val2,
from crit_table;
But the result looks not how I want it to look like:
T01_val1 |T01_val2 |T02_val1 |T02_val2 | T03_val1 | T03_val2
---------+---------+---------+---------+----------+---------
9 | 9 | null | null | null | null
null | null | 3 | 5 | null | null
null | null | null | null | 4 | 9
What's the fastest way to achieve my goal?
Bonus question:
I have 77 criteria and seven different kinds of values for every criterium. So I have to write 539 case statements. Whats the best way to create them dynamically?
I'm working with PostgreSql 9.4
Prepare for crosstab
In order to use crosstab() function, the data must be reorganized. You need a dataset with three columns (row number, criterium, value). To have all values in one column you must unpivot two last columns, changing at the same time the names of criteria. As a row number you can use rank() function over partitions by new criteria.
select rank() over (partition by criterium order by crit_id), criterium, val
from (
select crit_id, criterium || '_v1' criterium, val1 val
from crit
union
select crit_id, criterium || '_v2' criterium, val2 val
from crit
) sub
order by 1, 2
rank | criterium | val
------+-----------+-----
1 | T01_v1 | 9
1 | T01_v2 | 9
1 | T02_v1 | 3
1 | T02_v2 | 5
1 | T03_v1 | 4
1 | T03_v2 | 9
2 | T01_v1 | 2
2 | T01_v2 | 3
2 | T02_v1 | 5
2 | T02_v2 | 1
2 | T03_v1 | 6
2 | T03_v2 | 1
(12 rows)
This dataset can be used in crosstab():
create extension if not exists tablefunc;
select * from crosstab($ct$
select rank() over (partition by criterium order by crit_id), criterium, val
from (
select crit_id, criterium || '_v1' criterium, val1 val
from crit
union
select crit_id, criterium || '_v2' criterium, val2 val
from crit
) sub
order by 1, 2
$ct$)
as ct (rank bigint, "T01_v1" int, "T01_v2" int,
"T02_v1" int, "T02_v2" int,
"T03_v1" int, "T03_v2" int);
rank | T01_v1 | T01_v2 | T02_v1 | T02_v2 | T03_v1 | T03_v2
------+--------+--------+--------+--------+--------+--------
1 | 9 | 9 | 3 | 5 | 4 | 9
2 | 2 | 3 | 5 | 1 | 6 | 1
(2 rows)
Alternative solution
For 77 criteria * 7 parameters the above query may be troublesome. If you can accept a bit different way of presenting the data, the issue becomes much easier.
select * from crosstab($ct$
select
rank() over (partition by criterium order by crit_id),
criterium,
concat_ws(' | ', val1, val2) vals
from crit
order by 1, 2
$ct$)
as ct (rank bigint, "T01" text, "T02" text, "T03" text);
rank | T01 | T02 | T03
------+-------+-------+-------
1 | 9 | 9 | 3 | 5 | 4 | 9
2 | 2 | 3 | 5 | 1 | 6 | 1
(2 rows)
DECLARE #Table1 TABLE
(crit_id int, criterium varchar(3), val1 int, val2 int)
;
INSERT INTO #Table1
(crit_id, criterium, val1, val2)
VALUES
(1, 'T01', 9, 9),
(2, 'T02', 3, 5),
(3, 'T03', 4, 9),
(4, 'T01', 2, 3),
(5, 'T02', 5, 1),
(6, 'T03', 6, 1)
;
select [T01] As [T01_val1 ],[T01-1] As [T01_val2 ],[T02] As [T02_val1 ],[T02-1] As [T02_val2 ],[T03] As [T03_val1 ],[T03-1] As [T03_val3 ] from (
select T.criterium,T.val1,ROW_NUMBER()OVER(PARTITION BY T.criterium ORDER BY (SELECT NULL)) RN from (
select criterium, val1 from #Table1
UNION ALL
select criterium+'-'+'1', val2 from #Table1)T)PP
PIVOT (MAX(val1) FOR criterium IN([T01],[T02],[T03],[T01-1],[T02-1],[T03-1]))P
I agree with Michael's comment that this requirement looks a bit weird, but if you really need it that way, you were on the right track with your solution. It just needs a little bit of additional code (and small corrections wherever val_1 and val_2 where mixed up):
select
sum(case when criterium = 'T01' then val_1 else null end) as T01_val1,
sum(case when criterium = 'T01' then val_2 else null end) as T01_val2,
sum(case when criterium = 'T02' then val_1 else null end) as T02_val1,
sum(case when criterium = 'T02' then val_2 else null end) as T02_val2,
sum(case when criterium = 'T03' then val_1 else null end) as T03_val1,
sum(case when criterium = 'T03' then val_2 else null end) as T03_val2
from
crit_table
group by
trunc((crit_id-1)/3.0)
order by
trunc((crit_id-1)/3.0);
This works as follows. To aggregate the result you posted into the result you would like to have, the first helpful observation is that the desired result has less rows than your preliminary one. So there's some kind of grouping necessary, and the key question is: "What's the grouping criterion?" In this case, it's rather non-obvious: It's criterion ID (minus 1, to start counting with 0) divided by 3, and truncated. The three comes from the number of different criteria. After that puzzle is solved, it is easy to see that for among the input rows that are aggregated into the same result row, there is only one non-null value per column. That means that the choice of aggregate function is not so important, as it is only needed to return the only non-null value. I used the sum in my code snippet, but you could as well use min or max.
As for the bonus question: Use a code generator query that generates the query you need. The code looks like this (with only three types of values to keep it brief):
with value_table as /* possible kinds of values, add the remaining ones here */
(select 'val_1' value_type union
select 'val_2' value_type union
select 'val_3' value_type )
select contents from (
select 0 order_id, 'select' contents
union
select row_number() over () order_id,
'max(case when criterium = '''||criterium||''' then '||value_type||' else null end) '||criterium||'_'||value_type||',' contents
from crit_table
cross join value_table
union select 9999999 order_id,
' from crit_table group by trunc((crit_id-1)/3.0) order by trunc((crit_id-1)/3.0);' contents
) v
order by order_id;
This basically only uses a string template of your query and then inserts the appropriate combinations of values for the criteria and the val-columns. You could even get rid of the with-clause by reading column names from information_schema.columns, but I think the basic idea is clearer in the version above. Note that the code generated contains one comma too much directly after the last column (before the from clause). It's easier to delete that by hand afterwards than correcting it in the generator.

SQL Server - Repeat Characters after nth row

How to repeat characters after every nth Row. For example, I want result like this:
1 1234-A
2 32423-B
3 324234-C
4 afsd-D
5 32432-A
6 32423-B
7 3dsfa33-C
8 sdfw3rf-D
This A,B,C and D will be repeated though out result set. NO other characters.
Try this:
SELECT YourID
,YourDescr + '-' + CHAR((ROW_NUMBER() OVER (ORDER BY YourID) - 1) % 4 + 65) AS YourDescr
FROM YourTable
It doesn't rely on the value of the ID column.
Generate a row number for every four rows and use case statement to append the required characters. Try this.
CREATE TABLE #str(id INT,string VARCHAR(50))
INSERT #str
VALUES (1,'1234' ),(2,'32423' ),(3,'324234' ),
(4,'afsd' ),(5,'32432' ),(6,'32423' ),
(7,'3dsfa33' ),(8,'sdfw3rf' )
SELECT id,
CASE
WHEN rn = 1 THEN string + '-A'
WHEN rn = 2 THEN string + '-B'
WHEN rn = 3 THEN string + '-C'
WHEN rn = 4 THEN string + '-D'
END AS String
FROM (SELECT ( ( id - 1 )%4 ) + 1 rn,*
FROM #str)a
Result :
+----+-----------+
| id | String |
+----+-----------+
| 1 | 1234-A |
| 2 | 32423-B |
| 3 | 324234-C |
| 4 | afsd-D |
| 5 | 32432-A |
| 6 | 32423-B |
| 7 | 3dsfa33-C|
| 8 | sdfw3rf-D|
+----+-----------+

Can we use two pivot in a single query?

Declare #tbl table(SessionId varchar(max),ItemID_FK int,Roles varchar(max))
insert into #tbl
select distinct SessionID,ItemID_FK,Roles from tbl_Answers where ID_FK=#ID
SELECT ItemID_PK,ItemName,case when [Role1] IS NULL then 0 else [Role1] end as [Role1],
case when [Role2] IS NULL then 0 else [Role2] end as [Role2],
case when [Role3] IS NULL then 0 else [Role3] end as [Role3],
case when [Role4] IS NULL then 0 else [Role4] end as [Role4],
case when [Role5] IS NULL then 0 else [Role5] end as [Role5],
case when [Role6] IS NULL then 0 else [Role6] end as [Role6],
case when [Role7] IS NULL then 0 else [Role7] end as [Role7]
FROM
(
select items.ItemID_PK ,items.ItemName,count(ans.Roles) as cntRoles,ans.Roles from tbl_Items items Full join #tbl ans
on items.ItemID_PK=ans.ItemID_FK where items.ID_FK= #ID group by Roles,ItemName , items.ItemID_PK
) d PIVOT
(
max(cntRoles)
FOR Roles IN ([Role1],[Role2],[Role3],[Role4],[Role5],[Role6],[Role7])
) AS pvt order by ItemID_PK
I used the above stored procedure and got the output as
+----------+----------+-----+-----+-----+-----+-----+-----+-----+
|ItemID_PK |ItemName |Role1|Role2|Role3|Role4|Role5|Role6|Role7|
+----------+----------+-----+-----+-----+-----+-----+-----+-----+
| 111 | aaaaa | 6 | 5 | 0 | 5 | 1 | 4 | 2 |
| 222 | bbbbb | 1 | 1 | 7 | 2 | 0 | 3 | 1 |
+----------+----------+-----+-----+-----+-----+-----+-----+-----+
I have another query and got the following output.
Select Category,Answer,Roles
from tbl_Answers where ID_FK=1 and Category='OtherText'
+---------+--------+-----+
|Category |Answer |Roles|
+---------+--------+-----+
|OtherText| xxx |Role1|
|OtherText| yyy |Role1|
|OtherText| zzz |Role2|
|OtherText| xzx |Role3|
+---------+--------+-----+
I need to merge the above two outputs to generate the result as
+----------+----------+-----+-----+-----+-----+-----+-----+-----+
|ItemID_PK |ItemName |Role1|Role2|Role3|Role4|Role5|Role6|Role7|
+----------+----------+-----+-----+-----+-----+-----+-----+-----+
| 111 | aaaaa | 6 | 5 | 0 | 5 | 1 | 4 | 2 |
| 222 | bbbbb | 1 | 1 | 7 | 2 | 0 | 3 | 1 |
| Null | Othertext| xxx | zzz | xzx | saa | | xxx | |
| Null | Othertext| yyy | | | zxz | | | |
+----------+----------+-----+-----+-----+-----+-----+-----+-----+
How to combine the second query to the first pivot query to get the result mentioned above?
Thanks in advance.
You could just use UNION ALL to combine the two results, you would need to convert the roles from the top query from int to VARCHAR though:
DECLARE #ID INT = 1;
WITH Ans AS
( SELECT DISTINCT SessionID,ItemID_FK,Roles
FROM tbl_Answers
WHERE ID_FK = #ID
), PivotData AS
( SELECT items.ItemID_PK,
items.ItemName,
cntRoles = COUNT(ans.Roles),
ans.Roles
FROM tbl_Items items
FULL JOIN Ans
ON items.ItemID_PK = ans.ItemID_FK
WHERE items.ID_FK = #ID
GROUP BY Roles,ItemName, items.ItemID_PK
)
SELECT ItemID_PK,
ItemName,
[Role1] = CAST(ISNULL([Role1], 0) AS VARCHAR(255)),
[Role2] = CAST(ISNULL([Role2], 0) AS VARCHAR(255)),
[Role3] = CAST(ISNULL([Role3], 0) AS VARCHAR(255)),
[Role4] = CAST(ISNULL([Role4], 0) AS VARCHAR(255)),
[Role5] = CAST(ISNULL([Role5], 0) AS VARCHAR(255)),
[Role6] = CAST(ISNULL([Role6], 0) AS VARCHAR(255)),
[Role7] = CAST(ISNULL([Role7], 0) AS VARCHAR(255))
FROM PivotData
PIVOT
( MAX(cntRoles)
FOR Roles IN ([Role1],[Role2],[Role3],[Role4],[Role5],[Role6],[Role7])
) AS pvt
UNION ALL
SELECT ItemID_PK = NULL,
ItemName = Category,
[Role1] = ISNULL([Role1], ''),
[Role2] = ISNULL([Role2], ''),
[Role3] = ISNULL([Role3], ''),
[Role4] = ISNULL([Role4], ''),
[Role5] = ISNULL([Role5], ''),
[Role6] = ISNULL([Role6], ''),
[Role7] = ISNULL([Role7], '')
FROM ( SELECT Category,Answer,Roles
FROM tbl_Answers
WHERE ID_FK = 1
AND Category = 'OtherText'
) pd
PIVOT
( MAX(Answer)
FOR Roles IN ([Role1],[Role2],[Role3],[Role4],[Role5],[Role6],[Role7])
) AS pvt
ORDER BY ItemID_PK;
Note, I have changed this expression:
case when [Role2] IS NULL then 0 else [Role2] end
to
ISNULL([Role2], 0)
as the effect is the same, but it is much shorter. I have also removed the table variable, and just placed the same query within a Common Table Expression, as it seems redundant to fill a table variable then only refer to it once. You are removing the use of indexes and statistics on the actual table and gaining no benefit for it.
Use a UNION to combine the two pivots.