CASE statement with in SQL IN Condition - sql-server

Input parameter contains a comma separated string.
DECLARE #FacilityCode VARCHAR(MAX) = 'a,b,c,d,e,f,g,h,i'
ID | Value
1 | a
2 | b
3 | c
4 | d
I need to query the matching data using the comma separated string.
If #FacilityCode = 'a,b'
Table should return a,b as rows.
If #FacilityCode = ''
Table should return all the rows
I am using the below query. It shows me syntax errors. Thanks for advance.
SELECT Value
FROM Table
WHERE [GROUP] IN (CASE WHEN COUNT(SELECT Value FROM STRING_SPLIT(#FacilityCode,','))) = 0
THEN '%%' ELSE (SELECT Value FROM STRING_SPLIT(#FacilityCode,',')) END

You can simply add OR #FacilityCode = '' to the where clause:
SELECT value
FROM t
WHERE #FacilityCode = '' OR EXISTS (
SELECT 1
FROM STRING_SPLIT(#FacilityCode, ',') AS x
WHERE x.[value] = t.[group]
)

Related

Based on ID values , how to find the columns which has values for the ID in a table?

I have table where there is an identity column . For each ID , the other columns would have 0 or 1 as a value . I need to find how many columns have 1s and list those as comma separated string .
This is my table
ID T1 T2 T3
1 1 0 0
2 0 1 1
3 1 1 1
Expected Output
ID 1 2 3-- COLUMNS
1 t1 t2,t3 t1,t2,t3
under Column 1 , the value should be "t1" since t1 had the value '1'
under Column 2 , the value should be "t2,t3" since t2,t3 had the value '1'
under Column 3 , the value should be "t1,t2,t3" since t1,t2,t3 had the value '1'
Any help how to achieve this output ?
Thanks in advance .
WITH T AS (
SELECT ID, CASE WHEN T1 = 0 THEN NULL ELSE 'T1' END AS T1, CASE WHEN T2 = 0 THEN NULL ELSE 'T2' END AS T2, CASE WHEN T3 = 0 THEN NULL ELSE 'T3' END AS T3 FROM Foo
)
SELECT ID, SUBSTRING(CONCAT(', ' + T1, ', ' + T2, ', ' + T3), 3, 100) AS Result FROM T
The first select converts the columns into their names or NULL, the second concatenates them into a comma (+ space) delimitered list.
If you are looking for a simple function to enable your task then export the file from the SQL Server as a .csv file and then apply the concatenate function on excel side.
=CONCATENATE(A1, ", ", B1)
On SQL side,
CONCAT(string1, string2, ...., string_n)
Thanks All for your help .
I was able to get the required result from the below code . But the one which #Christoph posted is much more simpler than mine .
Anyway posting my approach as below .
select u.ID, u.Columns1, u.values1 into #temptable from ColumnsAsValues s
unpivot
(
values1
for Columns1 in (t1,t2,t3,t4)
) u;
SELECT DISTINCT MC2.ID,
(
SELECT STUFF((SELECT ',' + MC1.Columns1 AS [text()]
FROM #temptable MC1
WHERE MC1.ID = MC2.ID and values1=1
ORDER BY MC1.ID
FOR XML PATH (''),type).value('.', 'NVARCHAR(MAX)'),1,1,'')
) [ColumnsWith1]
INTO #CommaSeparatedString
FROM #temptable MC2
select * from
( select id , [ColumnsWith1] from
#CommaSeparatedString ) datatable pivot (max(ColumnsWith1) for id in ([1],[2],[3]))piv;

Return string with each value separated or null if all are null, using FOR XML PATH

I have a query like this:
select stuff (
(select '; ' + isnull(org.Number, '-')
from Organization org
for xml path('')), 1, 2, ''
)
I need to return a string with all values, including nulls, so that its amount of values is equal to amount of organizations, but if ALL values are empty or null, I need to return empty string or null (doesn't matter).
First thought was to use regex and check if there are digits in returned query. but it's not so easy to use. Is there some other options to solve it?
For example, if table Organization contains
ID | Number
1 | 123456
2 | null
3 | 3232
then resulted string has to be '123456; -; 3232'
if table Organization contains
ID | Number
1 | null
2 | null
3 | null
resulted string has to be null
Here's one way
select
iif(patindex('%[0-9]%', res) = 0, null, res)
from
(select res = stuff((
select
'; ' + isnull(cast(number as varchar(200)), '-')
from
Organization
order by id
for xml path('')
), 1, 2, '')
) t

T-SQL: Query across tables converting rows to columns?

SQL Server 2012.
Straight to the point, this is what I'm trying to do:
User table
id username fullname
---- -------- ----------------------
1 test0001 Test User #1
2 test0002 Test User #2
3 test0003 Test User #3
4 test0004 Test User #4
Flags table
id name description
--- ---------- -------------------------------------------
1 isActive true if user is currently active
2 isAdmin true if user can do Admin things
3 canEdit true if user can can edit
UserFlags table
user flag
---- ----
1 1
1 2
1 3
2 1
2 3
3 1
(user = FK to user.id, flag = FK to flag.id)
Desired result
userId username isActive isAdmin canEdit
------ -------- -------- ------- -------
1 test0001 1 1 1
2 test0002 1 0 1
3 test0003 1 0 0
4 test0004 0 0 0
In short I want to convert each flag in the flags table into a column with the name field used as the column header. Then I want a row for each user, with a boolean in each column indicating whether they have that particular flag.
This needs to be able to adapt - e.g. if another flag is added, the result of the query should have another column with that flag's name as its title.
I'd prefer to do this in a view, but I'd be OK with a table-valued function.
I haven't done anything like this before so I'm not even sure where to start - I can do a full join on the tables and end up with a row per user per flag, but I then want to fold that all down into a single row per user.
EDIT One of the key points is "able to adapt" - the best scenario would be a query that automatically pulls in all currently defined flags from the flags table when building the response. Having to edit the query isn't necessarily bad, but consider the instance where an admin is allowed to add a new flag to the system. It's easy to INSERT a new flag, it's much harder to autonomously edit a stored query to reflect that. If that's simply not possible to do, then an explanation as to why would be helpful. Thanks!
You can use pivot as below:
Select * from (
Select u.id, u.username, f.[name] from #user u
left join #userflags uf
on uf.[user] = u.id
left join #flags f
on uf.flag = f.id
) a
pivot (count([name]) for [name] in ([isActive],[isAdmin],[canEdit])) p
Output as below:
+----+----------+----------+---------+---------+
| id | username | isActive | isAdmin | canEdit |
+----+----------+----------+---------+---------+
| 1 | test0001 | 1 | 1 | 1 |
| 2 | test0002 | 1 | 0 | 1 |
| 3 | test0003 | 1 | 0 | 0 |
| 4 | test0004 | 0 | 0 | 0 |
+----+----------+----------+---------+---------+
Demo
Updated my query if you have dynamic list of flags as below:
Declare #cols1 varchar(max)
Declare #query nvarchar(max)
Select #cols1 = stuff((select distinct ','+QuoteName([name]) from #flags for xml path('')),1,1,'')
Select #query = ' Select * from (
Select u.id, u.username, f.[name] from #user u
left join #userflags uf
on uf.[user] = u.id
left join #flags f
on uf.flag = f.id
) a
pivot (count([name]) for [name] in (' + #cols1 + ')) p '
Exec sp_executesql #query
If you dont like pivot function like me; you can use the SUM IIF method like this
SELECT u.id
, username
, SUM(IIF(flag = 1, 1, 0)) AS isActive
, SUM(IIF(flag = 2, 1, 0)) AS isAdmin
, SUM(IIF(flag = 3, 1, 0)) AS canEdit
FROM User u
LEFT JOIN UserFlags uf ON uf.[user] = u.id
GROUP BY u.id
, username
You can do this with a case statement and subquery or with a pivot. They both essentially do the same thing. This includes the DDL for the tables I made for testing.
declare #user_tbl table (
id int,
username nvarchar(max)
)
INSERT #user_tbl VALUES
(1,'test0001'),
(2,'test0002'),
(3,'test0003'),
(4,'test0004')
declare #userflags_tbl table(
userid int,
flag int
)
INSERT #userflags_tbl VALUES
(1,1),
(1,2),
(1,3),
(2,1),
(2,3),
(3,1)
declare #flags_tbl table(
id int
)
INSERT #flags_tbl VALUES
(1),
(2),
(3)
SELECT
userid,
username,
MAX(isActive) AS isActive,
MAX(isAdmin) AS isAdmin,
MAX(canEdit) AS canEdit
FROM (
SELECT
user_tbl.id AS userid,
user_tbl.username,
CASE
WHEN userflags_tbl.flag = 1 THEN 1
ELSE 0
END AS isActive,
CASE
WHEN userflags_tbl.flag = 2 THEN 1
ELSE 0
END AS isAdmin,
CASE
WHEN userflags_tbl.flag = 3 THEN 1
ELSE 0
END AS canEdit
FROM #user_tbl user_tbl
LEFT JOIN #userflags_tbl userflags_tbl ON
user_tbl.id = userflags_tbl.userid
LEFT JOIN #flags_tbl flags_tbl ON
userflags_tbl.flag = flags_tbl.id
)tbl
GROUP BY
userid,
username
SELECT
userid,
username,
ISNULL([1],0) AS isActive,
REPLACE(ISNULL([2],0),2,1) AS isAdmin,
REPLACE(ISNULL([3],0),3,1) AS canEdit
FROM (
SELECT
user_tbl.id AS userid,
user_tbl.username,
userflags_tbl.flag
FROM #user_tbl user_tbl
JOIN #userflags_tbl userflags_tbl ON
user_tbl.id = userflags_tbl.userid
JOIN #flags_tbl flags_tbl ON
userflags_tbl.flag = flags_tbl.id
) tbl
PIVOT (
MAX(flag)
FOR flag IN ([1],[2],[3])
) AS PivotTable

Select default value if column does not exist in oracle

I am trying to select a default value to a column if the column does not exist in the table. Following code seems doesn't give my expected output.
SELECT CustomerID, (select case when exists (select *
from INFORMATION_SCHEMA.COLUMNS
where table_name = 'Customers' and
column_name = 'Age'
)
then Age
else 0 as Age
end)
FROM (select * from Customers);
Since Age column doesn't exist in the table result should be given as:-
CustomerID | Age
-----------|----
Cust01 | 0
Cust02 | 0
Can someone suggest me a solution or the error in above code snippet.
As mentioned in the comments by experts, you need to use a Oracle PLSQL block and achieve your requirement using dynamic sql. Please see below the same code which is written keeping your requirement in mind. You can try implementing your code with this:
declare
var number;
var2 varchar2(4000);
type abc is record
( col1 number,
col2 number);
type var3 is table of abc index by pls_integer;
var4 var3;
begin
select count(1)
into var
from employee;
var2:= 'select A, '||case when var is null then 1 else 2 end || ' from test where rownum < 10';
dbms_output.put_line(var2);
execute immediate var2 bulk collect into var4;
for rec in 1..var4.last
loop
dbms_output.put_line(var4(rec).col1 ||',' ||var4(rec).col2);
end loop;
end;
Output:
select A, 2 from test where rownum < 10
1444,2
1445,2
1446,2
1447,2
1448,2
1449,2
1450,2
1451,2
1452,2

TSQL stuff for xml path and sub query

I've to replace csv datas in column by correspondant id always in csv format
I've a problem with this query :
select t0.code , t0.categories, t0.departement, (
SELECT Stuff((
SELECT N', ' + CONVERT(varchar, id_categorie) FROM tcategories t1 WHERE t0.departement = t1.departement COLLATE French_CI_AI and categorie IN (t0.tcategories)
FOR XML PATH(''),TYPE).value('text()[1]','varchar(max)'),1,1,N'')) as id_colonne
FROM #codes_reductions t0 where categories is not null
Here is the result :
code | categories | departement | id_colonne
AIRSTREAM | 'A','B','BA' | JMQ | NULL
If I replace 'and categorie IN (t0.tcategories)' by and categorie IN ('A','B','BA') the query works good
Here is the result :
code | categories | departement | id_colonne
AIRSTREAM | 'A','B','BA' | JMQ | 128, 129, 260
I tryed to use COLLATE French_CI_AI on my column, but without success. Any idea ?
... categorie IN (t0.tcategories) ....
The cause of your problem is that categorie column stores those values ('A','B','BA') as a single value not as an array / list / table of values. So, SQL Server compares two strings thus s1 IN (s2) which is equivalent to s1 = s2 => A IN ('A','B','BA') <=> A = 'A','B','BA'.
Example (note: double single quotes ('') are used to define an empty string while four single quotes ('''') are used to define a string with a single quote: SELECT '''' --> '):
DECLARE #categories VARCHAR(1000);
SET #categories = '''A'',''B'',''BA'''
SELECT #categories AS ColA, CASE WHEN 'A' IN (#categories) THEN 'TRUE' ELSE 'FALSE' END AS Col2
/*
ColA Col2
------------ -----
'A','B','BA' FALSE
*/
The solution on short term is to use one of following conditions:
C#1 (if categorie contains single quotes): ... t0.tcategories LIKE '%' + categorie + '%' ....
C#2 (when categorie doesn't contains single quotes): ... t0.tcategories LIKE '%''' + categorie + '''%' ....
Example:
DECLARE #categories VARCHAR(1000);
SET #categories = '''A'',''B'',''BA'''
SELECT #categories AS ColA, CASE WHEN #categories LIKE '%''A''%' THEN 'TRUE' ELSE 'FALSE' END AS Col2
/*
ColA Col2
------------ -----
'A','B','BA' TRUE
*/
Second note: this works when every separate value from t0.tcategories column doesn't includes single quote(s) (example: 'B'A' / 'B''A' ).
On medium/long term, you should store separately every single value from tReduction.tCategories column using another table :
CREATE TABLE dbo.ReductionCategory (
... pk ...,
ReductionCode INT NOT NULL REFERENCES dbo.tReduction(ReductionCode), -- FK
CategoryCode INT NOT NULL REFERENCES dbo.tCategories(CategoryCode) -- FK
)
Thus, condition becomes
... categorie /*CategoryCode*/
IN (
SELECT rc.CategoryCode FROM dbo.ReductionCategory rc
WHERE rc.ReductionCode = t0.ReductionCode
) ....

Resources