SQL any of 1st list IN 2nd - sql

SQL Server 2005.
I have 2 lists:
('a', 'b', 'c', 'd')
('d', 'e', 'f', 'g')
I need to make a WHERE clause with these 2 inside a dynamic SQL string, something like:
select *
from tbl
where ... afewconditionshere ...
AND anyitemfrom[('a', 'b', 'c', 'd')] is inside [('d', 'e', 'f', 'g')]
it looks kinda weird, but I get this data from a 3rd party, cannot make too big changes
the lists would always have < 20 items inside
UPDATE
a,b,c,d,e,f,g are like security things not related to the table in any way, the idea is that if you have this anyitemfrom[('a', 'b', 'c', 'd')] is inside [('d', 'e', 'f', 'g')] then you are able to view the records returned, otherwise no records should be returned
yes this condition should basically return true or false

Based on your question, it seems if you perform one IN (), then a second IN() on the filtered data set, you should get what you're looking for:
select * from
(
select * from tbl
where ... afewconditionswhere ...
and anyitemfrom in('a', 'b', 'c', 'd')
) as derived
where derived.anyitemfrom in ('d', 'e', 'f', 'g')

declare #cmd varchar(2000), #list1 varchar(100), #list2 varchar(100)
select #list1 = "('a', 'b', 'c', 'd')", #list2 = "('d', 'e', 'f', 'g')"
select #cmd =
'select * from
(
select * from tbl
where ... afewconditionswhere ...
and anyitemfrom in ' + #list1 + '
) as derived
where derived.anyitemfrom in ' + #list2
exec(#cmd)

i figured it out
i used something like :
CREATE TABLE #t (UserName VARCHAR(50))
DECLARE #sql VARCHAR(MAX)
SELECT #sql = 'INSERT INTO #t SELECT ''' + REPLACE(#UserList, ',', ''' UNION SELECT ''') + ''''
PRINT (#sql)
EXEC (#sql)
SELECT * FROM #t
IF OBJECT_ID('tempdb..#t') IS NOT NULL BEGIN DROP TABLE #t END
for both lists
then I was able to join them, and use the join's recordcount

WHERE (
SELECT COUNT(*)
FROM ( select 'a' union all select 'b' union all select 'c' union all select 'd'
intersect
select 'd' union all select 'e' union all select 'f' union all select 'g'
)
) > 0

Related

Do not include fixed value in JSON SQL

I have to make JSON from the table.
Problem is, that I have to show NULL values, but hide all fixed constant values.
For example, I have dataset #table. In the JSON output I want to show all values, where Value != 0.
Deleting row (select Value from #table where cn = 'c') as 'c' isn't an option.
How can I do that?
create table #table (
Value int,
cn nvarchar(1)
)
insert into #table
values
(null, 'a'),
(null, 'b'),
(0, 'c'),
(3, 'd'),
(3, 'f')
select
(select Value from #table where cn = 'a') as 'a',
(select Value from #table where cn = 'b') as 'b',
(select Value from #table where cn = 'c') as 'c',
(select Value from #table where cn = 'd') as 'd',
(select Value from #table where cn = 'f') as 'f'
FOR JSON PATH, INCLUDE_NULL_VALUES
Expected output would be:
[{"a":null,"b":null,"d":3,"f":3}]
You can do it with a dynamic sql. I use conditional aggregation to pivot the table.
declare #cmd nvarchar(max) = 'select ' + stuff(
(select ', max(case cn when ''' + cn + ''' then value end) as ''' + cn +''''
from #table
where value <> 0 or value is null
for xml path(''))
, 1, 1, '') + ' from #table FOR JSON PATH, INCLUDE_NULL_VALUES';
--select #cmd;
exec (#cmd);
Another option builds on the type of the value column (int), so 0.5 is impossible as a column value. Cast the original values to decimal, then replace impossible values with null and remove decimal point and 0 after it.
select replace(replace(
(select
max(case cn when 'a' then value end) 'a',
max(case cn when 'b' then value end) 'b',
max(case cn when 'c' then value end) 'c',
max(case cn when 'd' then value end) 'd',
max(case cn when 'f' then value end) 'f'
from(
select coalesce(cast(value as decimal), 0.5) value, cn
from #table
where value <> 0 or value is null) t
FOR JSON PATH)
,'0.5', 'null'), '.0', '');
I do not think you can vary the number of selected column in a SQL statement, but you can do this:
declare #statement varchar(max) ='
select
(select Value from #table where cn = ''a'') as ''a'',
(select Value from #table where cn = ''b'') as ''b'',
--(select Value from #table where cn = ''c'') as ''c'',
(select Value from #table where cn = ''d'') as ''d'',
(select Value from #table where cn = ''f'') as ''f''
FOR JSON PATH, INCLUDE_NULL_VALUES'
exec sp_sqlexec #statement
Effectively build you SQL statement, to only select those values which are not equal to the value you want to hide.
It is not elegant, but one possibility is manually building the string:
select concat('[{',
string_agg(concat('"', cn, '":', coalesce(convert(varchar(255), value), 'null')
), ','
), '}]'
)
from t
where value <> 0 or value is null

MSSQL - Masking data based on mapping table

wanted to perform data masking according to mapping as below by using MSSQL 2008R2:
Mapping Table
A = C
B = A
C = E
1 = 3
2 = 1
3 = 9
Original
ABC123
Masked
CAE319
The idea would be using replace however the second replace function will replacing previous replaced value.
select Replace(Replace(Replace(Replace(Replace(REPLACE('ABC123', 'A', 'C'), 'B', 'A'), 'C', 'E'), '1', '3'), '2', '1'), '3', '9')
Result: CAE319
P.s. value edited, because Reverse or reverse replace cannot be use in this case
any idea?
If you want a more table approach.
There are two code segments below which will Mask or UnMask a string. Easily converted into a UDF or even placed in a CROSS APPLY
Declare #Mask table (MapFrom varchar(10),MapTo varchar(10))
Insert into #Mask values
('A','C'),
('B','D'),
('C','E'),
('1','2'),
('2','3'),
('3','9')
Declare #Yourtable table (ID int,SomeCol varchar(max))
Insert Into #Yourtable values
(1,'ABC123')
-- To Mask
Declare #U varchar(max) ='ABC123'
Select NewSting = Stuff((Select ''+S
From (
Select N
,S=IsNull(MapTo,Substring(#U,N,1))
From (Select Top (Len(#U)) N=Row_Number() Over (Order By (Select null)) From master..spt_values) N
Left Join #Mask on Substring(#U,N,1)=MapFrom
) X
Order By N
For XML Path ('')),1,0,'')
-- To UnMask
Declare #M varchar(max) = 'CDE239'
Select NewSting = Stuff((Select ''+S
From (
Select N
,S=IsNull(MapFrom,Substring(#M,N,1))
From (Select Top (Len(#M)) N=Row_Number() Over (Order By (Select null)) From master..spt_values) N
Left Join #Mask on Substring(#M,N,1)=MapTo
) X
Order By N
For XML Path ('')),1,0,'')
Just change the order of replace and reverse the result
select REVERSE( Replace(Replace(Replace(Replace(Replace(REPLACE('321CBA', '3', '9'), '2', '3'), '1', '2'), 'C', 'E'), 'B', 'D'), 'A', 'C'))
RESULT :
CDE239
EDIT:
Declare #Mask table (MapFrom varchar(10),MapTo varchar(10))
Insert into #Mask values
('A','C'),
('B','A'),
('C','E'),
('1','3'),
('2','1'),
('3','9')
DECLARE #pos INT
,#result VARCHAR(100)
,#maskfrom NCHAR(1)
,#mask_to NCHAR(1);
SET #result = 'ABC123';
SET #pos = 1
WHILE #pos < LEN(#result) + 1
BEGIN
SELECT #mask_to = MapTo
FROM #mask
WHERE MapFrom = substring(#result, #pos, 1)
SET #result = STUFF(#result, #pos, 1, #mask_to);
SET #pos = #pos + 1;
END
SELECT #result
RESULT
CAE319

Having an issue with SQL Order by Query

I have a table like this in SQL Server 2012:
DECLARE #test TABLE (dateField datetime,
col1 varchar(10),
col2 varchar(10)
)
insert into #test
select dateadd(day,-1,getdate()), 'Test A', 'Other A'
union
select dateadd(day,-1,getdate()), 'Test E', 'Other E'
union
select getdate(), 'Test B', 'Other B'
union
select dateadd(day,1,getdate()), 'Test C', 'Other C'
union
select dateadd(day,1,getdate()), 'Test A', 'Other C'
union
select dateadd(day,1,getdate()), 'Test D', 'Other B'
I want to retrieve the data in a specific order.
Order should be:
dateField ASC
Col1 ends with A
Col2 ends with B
If datefield is greater than today's date, then it should switch to col1 check.
If there's no col1 that ends with A, then it should switch to col2 check
If there's no col2 that ends with B, no rows should be returned
Any help would be appreciated. I tried doing case statements in SQL. It didn't work as I expected.
You seem a bit confused about if it is filtered or sorted or a combination of that. This does both.
select
*
from (
SELECT
*
,substring(col1,len(col1),1) [LastCharCol1]
,substring(col2,len(col2),2) [LastCharCol2]
FROM #test
) t
where 1=1
AND (
[LastCharCol1]='A'
OR [LastCharCol2]='B'
)
order by CASE WHEN dateField>CONVERT(DATE,GETDATE()) THEN 1 ELSE 0 END DESC,CASE WHEN [LastCharCol1]='A' THEN 1 ELSE 0 END DESC,CASE WHEN [LastCharCol2]='B' THEN 1 ELSE 0 END DESC
If I understood right, you want to filter records:
select *
from #test
where dateField > getdate() and (col1 like '%A' or col2 like '%B')

Replace multiple characters in SQL

I have a problem where I want to replace characters
I am using replace function but that is not giving desired output.
Values of column table_value needs to replaced with their fill names like
E - Email
P - Phone
M - Meeting
I am using this query
select table_value,
replace(replace(replace(table_value, 'M', 'MEETING'), 'E', 'EMAIL'), 'P', 'PHONE') required_value
from foobar
so second required_value row should be EMAIL,PHONE,MEETING and so on.
What should I do so that required value is correct?
The below will work (even it's not a smart solution).
select
table_value,
replace(replace(replace(replace(table_value, 'M', 'MXXTING'), 'E', 'XMAIL'), 'P', 'PHONX'), 'X', 'E') required_value
from foobar
You can do it using CTE to split the table values into E, P and M, then replace and put back together.
I assumed each record has a unique identifer Id but please replace that with whatever you have.
;WITH cte
AS
(
SELECT Id, SUBSTRING(table_value, 1, 1) AS SingleValue, 1 AS ValueIndex
FROM replacetable
UNION ALL
SELECT replacetable.Id, SUBSTRING(replacetable.table_value, cte.ValueIndex + 1, 1) AS SingleValue, cte.ValueIndex + 1 AS ValueIndex
FROM cte
INNER JOIN replacetable ON cte.ValueIndex < LEN(replacetable.table_value)
)
SELECT DISTINCT Id,
STUFF((SELECT DISTINCT ','+ CASE SingleValue
WHEN 'E' THEN 'EMAIL'
WHEN 'P' THEN 'PHONE'
WHEN 'M' THEN 'MEETING'
END
FROM cte c
WHERE c.Id = cte.Id
AND SingleValue <> ','
FOR XML PATH ('')),1,1,'')
FROM cte
Sorry , for mess code, maybe this is not best way to solve this, but what I've tried:
SET QUOTED_IDENTIFIER ON
SET ANSI_NULLS ON
GO
CREATE Function [dbo].[fn_CSVToTable]
(
#CSVList Varchar(max)
)
RETURNS #Table TABLE (ColumnData VARCHAR(100))
AS
BEGIN
DECLARE #S varchar(max),
#Split char(1),
#X xml
SELECT #Split = ','
SELECT #X = CONVERT(xml,' <root> <s>' + REPLACE(#CSVList,#Split,'</s> <s>') + '</s> </root> ')
INSERT INTO #Table
SELECT CASE RTRIM(LTRIM(T.c.value('.','varchar(20)'))) WHEN 'M' THEN 'Meeting'
WHEN 'P' THEN 'Phone'
WHEN 'E' THEN 'Email'
End
FROM #X.nodes('/root/s') T(c)
RETURN
END
GO
Then When I run this:
Select Main.table_value,
Left(Main.ColumnData,Len(Main.ColumnData)-1) As ColumnData
From
(
Select distinct tt2.table_value,
(
Select tt1.ColumnData+ ',' AS [text()]
From (
SELECT
*
FROM dbo.TestTable tt
CROSS APPLY dbo.fn_CSVToTable(tt.table_value)
) tt1
Where tt1.table_value = tt2.TABLE_value
ORDER BY tt1.table_value
For XML PATH ('')
) ColumnData
From dbo.TestTable tt2
) [Main]
I get this:
table_value ColumnData
-------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
E,P Email,Phone
E,P,M Email,Phone,Meeting
P,E Phone,Email
P,M Phone,Meeting
(4 row(s) affected)
You could also use a DECODE or CASE to translate the values.

sql query complex

I have table where in a table called test which have 4 fields.one field named as listing, I have 1,2,3,4,5,6 multiple values separated by comma, I need to check whether in that table and in that particular field an id say 4 is there or not.. by a sql query.
You database design is wrong, that's why you have problems querying the data. You should have the values in a separate table, so that teach value is in it's own field. Then it would be easy to find the records:
select t.testId
from test t
inner join listing l on l.testId = t.testId
where l.id = 4
Now you have to use some ugly string comparison to find the records:
select testId
from test
where ','+listing+',' like '%,4,%'
You can try
SELECT *
FROM YourTable
WHERE REPLACE(Col, ' ', '') LIKE '4,%' --Starts with
OR REPLACE(Col, ' ', '') LIKE '%,4' --Ends with
OR REPLACE(Col, ' ', '') LIKE '%,4,%' --Contains
OR REPLACE(Col, ' ', '') = '4' --Equals
Just as a matter of interest, have a look at this
DECLARE #delimiter NVARCHAR(5),
#Val INT
SELECT #Val = 40
SELECT #delimiter = ','
DECLARE #YourTable TABLE(
ID INT,
Vals VARCHAR(50)
)
INSERT INTO #YourTable (ID,Vals) SELECT 1, '1,2,3,4,5,6,7,8'
DECLARE #TempTable TABLE(
ID INT,
Vals XML
)
INSERT INTO #TempTable
SELECT ID,
CAST('<d>' + REPLACE(Vals, #delimiter, '</d><d>') + '</d>' AS XML)
FROM #YourTable
SELECT *
FROM #TempTable tt
WHERE EXISTS(
SELECT T.split.value('.', 'nvarchar(max)') AS data
FROM tt.Vals.nodes('/d') T(split)
WHERE T.split.value('.', 'nvarchar(max)') = #Val
)
The common approach is to parse the list into a table variable or table-valued function, then either join against the table, or use an EXISTS sub-query.
There are lots of examples on how to do this:
http://www.bing.com/search?setmkt=en-US&q=SQL+parse+list+into+table
You could use an instring function in the where clause and in the select clause:
Oracle:
select substr(column, instr(column, '1', 1), 1)
where instr(column, '1', 1) > 0
works if you want a single value. Alternatively you can use a combination of case or decode statements to create a single column for each possible value:
select
decode(instr(column, '1', 1), 0, substr(column, instr(column, '1', 1), 1), null) c1,
decode(instr(column, '2', 1), 0, substr(column, instr(column, '2', 1), 1), null) c2,
decode(instr(column, '3', 1), 0, substr(column, instr(column, '3', 1), 1), null) c3
The beauty of this approach for such a poorly normalised set of data is you can save this as a view and then run SQL on that, so if you save the above you could use:
select c1, c2 from view where c1 is not null or c2 is not null
NB. In other dbms you might have to use different syntax, possibly the case rather decode statement
If you need to find 4 and only 4 (ie not 14 or 24 or 40 etc) you should use
SELECT * FROM foo WHERE col LIKE '%, 4,%'
or
SELECT * FROM foo WHERE col LIKE '%,4,%'
if there are no spaces between the commas and numbers
How about this?
Select * From Foo Where Col like '%4%'