WHERE field1 IN (NULL) - tsql

I want to retrieve data from the table based on a couple of columns, some with data, other with NULL. In the first code example below, the procedure is fine - all the rows are returned. In the second example no rows are returned - because NULL=NULL return FALSE.
The third example is more or less what I have in mind, when the column has NULL values, then this clause has to be "ignored" and only the data in the first two columns are used to return the rows based on these two columns.
SELECT * FROM XYZ
WHERE col1 IN ('a', 'b')
AND col2 IN ('c', 'd')
AND col3 IN ('e', 'f')
SELECT * FROM XYZ
WHERE col1 IN ('a', 'b')
AND col2 IN ('c', 'd')
AND col3 IN (NULL) --???
SELECT * FROM XYZ
WHERE col1 IN ('a', 'b')
AND col2 IN ('c', 'd')
--AND col3 IN (NULL) --IGNORED
Currently I use a dynamic SQL statement, but is very slow.
SET #EventCodeList =
(SELECT REPLACE(tEventCode, ' ', '') FROM tblActivityPerCC_Config WHERE tCostCenter = #CostCenter)
SET #EventCodeStr =
CASE WHEN #EventCodeList IS NULL THEN ' logs.tEventCode LIKE (''%'') '
ELSE ' logs.tEventCode IN (SELECT qValue FROM nsEMV.dbo.fncReturnCommaDelimitedStringAsTable(#EventCodeList))'
END
SET #SQLString = N'
SELECT
ccg.Field1,
logs.Field2
FROM dbo.tblEMV_Logsheet AS logs
INNER JOIN dbo.tblLookup_EMVEquipment AS ccg ON logs.tEquipmentKey = ccg.tEquipmentKey
WHERE tDate BETWEEN ''' + CONVERT(varchar(30), #BMonth) + ''' AND ''' + CONVERT(varchar(30), #EMonth) + '''
AND logs.tAreaCode IN ('XYZ', 'ABC')
AND ' + #EventCodeStr + ' --THE FOLLOWING COLUMNS MAY HAVE NULL VALUES
AND ' + #SourceStr + '
AND ' + #DestinationStr'
Any help would be appreciated.
IN RESPONSE TO Jayvee's SUGGESTION BELOW:
This does not do what was intended or I do something wrong!
CREATE TABLE #temp
(
value varchar(10),
value2 varchar(10)
)
INSERT INTO #temp (value, value2) SELECT '5', '3'
INSERT INTO #temp (value, value2) SELECT NULL, '2'
INSERT INTO #temp (value, value2) SELECT '4', NULL
INSERT INTO #temp (value, value2) SELECT '6', '2'
INSERT INTO #temp (value, value2) SELECT '6', NULL
INSERT INTO #temp (value, value2) SELECT '6', '4'
INSERT INTO #temp (value, value2) SELECT NULL, '1'
INSERT INTO #temp (value, value2) SELECT NULL, '4'
SELECT value as [value],value2 as [value2] FROM #temp
WHERE ISNULL(value,'') IN ('4')
AND ISNULL(value2,'') IN ('4')
DROP TABLE #temp

Try IS NULL
SELECT * FROM XYZ
WHERE col1 IN ('a', 'b')
AND col2 IN ('c', 'd')
AND col3 IS NULL -- Instead of IN (NULL)

for each column test it against your parameter and include the IS NULL test, combine these with parentheses, like this:
SELECT
*
FROM XYZ
WHERE (col1 IN ('a', 'b') OR col1 IS NULL)
AND (col2 IN ('c', 'd') OR col2 IS NULL)
AND (col3 IN (NULL) OR col3 IS NULL)
here col3 IN (NULL) is used to demonstrate a parameter that was not provided

something like this should work:
SELECT * FROM XYZ
WHERE ISNULL(col1,'') IN ('a', 'b','')
AND ISNULL(col2,'') IN ('c', 'd','')
AND ISNULL(col3,'') IN ('')

Related

DB2 Replacing string using CASE When

My problem is that I concatenate the values ​​of column col2 but in the When I have to replace them with the values ​​in Then. For some reason my code doesn't work when I put this code inside my query, is there another way to do it? Would using Replace be good in this case? Could you give an example?
So my code is this one:
cte(col1, AliasCol) as
(Select col1,
CASE
WHEN Replace(Replace( LISTAGG(DISTINCT CONCAT( ', ', col2)) WITHIN GROUP
(ORDER BY CONCAT( ', ',col2)),',',''),' ','') = 'A' THEN 'Z'
WHEN Replace(Replace( LISTAGG(DISTINCT CONCAT( ', ', col2)) WITHIN GROUP
(ORDER BY CONCAT( ', ', col2)),',',''),' ','') = 'B' THEN 'Y'
WHEN Replace(Replace( LISTAGG(DISTINCT CONCAT( ', ', col2)) WITHIN GROUP
(ORDER BY CONCAT( ', ', col2)),',',''),' ','') = 'C' THEN 'X'
END
FROM Table
Where
col2 IN ('A','B','C','D')
and col >= (select TodaysDate from Date)
GROUP By col1
)
My output should be: ZYXD
Edit:
My col2 column displays all codes like this:
A
B
C
D
After I concat them to this ABCD the value of ABCD should be replace by ZYXD. Replacing 'A' by 'Z', 'Y' by 'B' and 'C' by X'.
Try this:
WITH MYTAB(COL1, COL2) AS
(
VALUES
(1, 'A')
, (1, 'A')
, (1, 'B')
, (1, 'C')
, (1, 'D')
)
SELECT
COL1
, TRANSLATE(LISTAGG(DISTINCT COL2) WITHIN GROUP (ORDER BY COL2), 'ZYX', 'ABC') MY_OUT
FROM MYTAB
GROUP BY COL1;

TSQL Pivoting Issue - looking for better approach

This is a T-SQL related question. I am using SQL Server 2012.
I have a table like this:
I would like to have output like this:
Explanation:
For each employee, there will be a row. An employee has one or more assignments. Batch Id specifies this. Based on the batch Id, the column names will change (e.g. Country 1, Country 2 etc.).
Approach so far:
Un-pivot the source table like the following:
select
EmpId, 'Country ' + cast(BatchId as varchar) as [ColumnName],
Country as [ColumnValue]
from
SourceTable
UNION
select
EmpId, 'Pass ' + cast(BatchId as varchar) as [ColumnName],
Pass as [ColumnValue]
from
SourceTable
which gives each column's values as rows. Then, this result can be pivoted to get the desired output.
Questions:
Is there a better way of doing this?
At the moment, I know there will be fixed amount of batches, but, for future, if I like to make the pivoting part dynamic, what is the best approach?
Using tools like SSIS or SSRS, is it easier to handle the pivot dynamically?
Screw doing it in SQL.
Let SSRS do the work for you with a MATRIX. It will PIVOT for you without having to create dynamic SQL to handle the terrible limitation of needing to know all the columns.
For your data, you would have EMP ID as the ROW Group and PASS as your column grouping.
https://msdn.microsoft.com/en-us/library/dd207149.aspx
There are many possible solutions to achieve what you want (search for Dynamic Pivot on multiple columns)
SqlFiddleDemo
Warning: I assume that columns Country and Pass are NOT NULL
CREATE TABLE SourceTable(EmpId INT, BatchId INT,
Country NVARCHAR(100) NOT NULL, Pass NVARCHAR(5) NOT NULL);
INSERT INTO SourceTable(EmpId, BatchId, Country, Pass)
VALUES
(100, 1, 'UK', 'M'), (200, 2, 'USA', 'U'),
(100, 2, 'Romania', 'M'), (100, 3, 'India', 'MA'),
(100, 4, 'Hongkong', 'MA'), (300, 1, 'Belgium', 'U'),
(300, 2, 'Poland', 'U'), (200, 1, 'Australia', 'M');
/* Get Number of Columns Groups Country1..Country<MaxCount> */
DECLARE #max_count INT
,#sql NVARCHAR(MAX) = ''
,#columns NVARCHAR(MAX) = ''
,#i INT = 0
,#i_s NVARCHAR(10);
WITH cte AS
(
SELECT EmpId
,[cnt] = COUNT(*)
FROM SourceTable
GROUP BY EmpId
)
SELECT #max_count = MAX(cnt)
FROM cte;
WHILE #i < #max_count
BEGIN
SET #i += 1;
SET #i_s = CAST(#i AS NVARCHAR(10));
SET #columns += N',MAX(CASE WHEN [row_no] = ' + #i_s + ' THEN Country END) AS Country' + #i_s +
',MAX(CASE WHEN [row_no] = ' + #i_s + ' THEN Pass END) AS Pass' + #i_s;
END
SELECT #sql =
N';WITH cte AS (
SELECT EmpId, Country, Pass, [row_no] = ROW_NUMBER() OVER (PARTITION BY EmpId ORDER BY BatchId)
FROM SourceTable)
SELECT EmpId ' + #columns + N'
FROM cte
GROUP BY EmpId';
/* Debug */
/* SELECT #sql */
EXEC(#sql);
Or:
SQLFiddleDemo2
DECLARE #cols NVARCHAR(MAX),
#sql NVARCHAR(MAX) = '';
;WITH cte(col_name, rn) AS(
SELECT DISTINCT col_name = col_name + CAST(BatchId AS VARCHAR(10)),
rn = ROW_NUMBER() OVER(PARTITION BY EmpId ORDER BY BatchId)
FROM SourceTable
CROSS APPLY (VALUES ('Country', Country), ('Pass', Pass)) AS c(col_name, val)
)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(col_name)
FROM cte
ORDER BY rn /* If column order is important for you */
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SET #sql =
N';WITH cte AS
(
SELECT EmpId, col_name = col_name + CAST(BatchId AS VARCHAR(10)), val
FROM SourceTable
CROSS APPLY (VALUES (''Country'', Country), (''Pass'', Pass)) AS c(col_name, val)
)
SELECT *
FROM cte
PIVOT
(
MAX(val)
FOR col_name IN (' + #cols + ')
) piv';
EXEC(#sql);

SELECT Columns WHERE one column Is Distinct?

Can anyone help me with the query ?
I've tried the following but it comes up with an error
SELECT Column1, Column2, Column3 FROM Table WHERE [Column1] NOT IN
(SELECT [Column1] FROM Table GROUP BY [Column1] HAVING COUNT([Column]) > 1)
Invalid MEMO, OLE, or Hyperlink Object in subquery [Column1].
Use Group By with Having clause:
SELECT Column1, MIN(Column2)AS Column2, MIN(Column3)AS Column3
FROM dbo.Table
GROUP BY Column1
HAVING ( COUNT(Column1) = 1 )
Should work since there's only one row per "group".
Your original query should work you just had column instead of column1.
SELECT Column1, Column2, Column3 FROM TableName WHERE [Column1] NOT IN
(SELECT [Column1] FROM TableName GROUP BY [Column1] HAVING COUNT(Column1) > 1)
see: http://sqlfiddle.com/#!3/d99a8/5/0
As far as i get , you need all data where [Column1] is unique (appear just one time)
DECLARE #x TABLE (col1 INT, col2 INT, col3 INT)
INSERT INTO #x
( [col1], [col2], [col3] )
VALUES ( 1, 2, 3 )
,( 1, 4, 5 )
,( 2, 6, 7 )
SELECT * FROM #x
SELECT col1, col2 , col3 FROM #x
WHERE col1 NOT IN
( SELECT [col1] FROM #x GROUP BY [col1] HAVING COUNT(*) > 1 )

T-SQL Column values count

Say I have a table A and it has 5 columns (Column1, Column2.. Column5), the values in each column is one char size and stored only
as alphabetic as follows
ID Column1 Column2 Column3 Column4 Column5
1 A C D A B
2 A D A B A
3 B K Q C Q
4 A K E E B
5 F K F F S
I need a count of each different value stored in column1 to column5, I want the following information
Column1 has A's count=3, B's count=1, F's count=1
Column2 has C's count=1, D's count=1, K's count=3
and so on
What is the correct way and format to return these values?
Thanks
You could try:
SELECT 'Col1' As 'Col', Column1 as 'Value', COUNT(*) as 'Ct'
FROM MyTable
GROUP BY Column1
UNION ALL
SELECT 'Col2', Column2, COUNT(*)
FROM MyTable
GROUP BY Column2
...
You will need to write an additional SELECT to UNION for each column you want to aggregate, but it will return the data you are after.
Can you just execute an individual query for each needed column using a GROUP BY?
SELECT Column1, COUNT(Column1) FROM TableName
GROUP BY Column1
You can use unpivot in a derived table (or CTE) and group by column name and value in the outer query.
Try this:
declare #T table
(
ID int,
Column1 char(1),
Column2 char(1),
Column3 char(1),
Column4 char(1),
Column5 char(1)
)
insert into #T values
(1, 'A', 'C', 'D', 'A', 'B'),
(2, 'A', 'D', 'A', 'B', 'A'),
(3, 'B', 'K', 'Q', 'C', 'Q'),
(4, 'A', 'K', 'E', 'E', 'B'),
(5, 'F', 'K', 'F', 'F', 'S')
;with C as
(
select ID, Col, Val
from (
select ID, Column1, Column2, Column3, Column4, Column5
from #T
) as T
unpivot (Val for Col in (Column1, Column2, Column3, Column4, Column5)) as U
)
select Col, Val, count(*) as ValCount
from C
group by Col, Val
order by Col
The union approach is going to server you best. The individual query for each union would look something like this:
Select Distinct Column1, COUNT(Column1)OVER(PARTITION BY Column1) Col1Count, 'Column1' ColumnName
From ColTable
Union All
...

SQL Server 2008 T-SQL UDF Split() Tailoring

I'm useing SQL Ser 2008 and have a large table with only one column of data. The data is a random string with very little consistency. Eample: Name Account 445566 0010020056893010445478008 AFD 369. I've been working with a split function that a stackoverflow user suggested. It works great but the function assigns the split string into one column. I need a row of individual columns. The present result is 1col with values Name, Account, 445566,... in it but the result I'm looking for is col1 Name, col2 Account, col3 445566,...
If anyone could provide some insight on how to tailor this script or its usage to get the desired result it would be much appreciated.
CREATE FUNCTION [dbo].[Split]
(
#String varchar(max)
,#Delimiter char
)
RETURNS #Results table
(
Ordinal int
,StringValue varchar(max)
)
as
begin
set #String = isnull(#String,'')
set #Delimiter = isnull(#Delimiter,'')
declare
#TempString varchar(max) = #String
,#Ordinal int = 0
,#CharIndex int = 0
set #CharIndex = charindex(#Delimiter, #TempString)
while #CharIndex != 0 begin
set #Ordinal += 1
insert #Results values
(
#Ordinal
,substring(#TempString, 0, #CharIndex)
)
set #TempString = substring(#TempString, #CharIndex + 1, len(#TempString) - #CharIndex)
set #CharIndex = charindex(#Delimiter, #TempString)
end
if #TempString != '' begin
set #Ordinal += 1
insert #Results values
(
#Ordinal
,#TempString
)
end
return
end
--The usage:
SELECT
*
FROM
mytable M
CROSS APPLY
[dbo].[Split] (M.TheColumn, ' ') S
Where rtrim(s.StringValue) != ''
If you know that you have 6 columns in the string you can use a split functions that looks like this and of course modify the function to whatever number of columns you want. A function can not return a dynamic number of columns.
create function dbo.Split6(#String varchar(max), #Delimiter char(1))
returns table as return
(
select
substring(T.Col, 1, S1.Pos-1) as Col1,
substring(T.Col, S1.Pos+1, S2.Pos-S1.Pos-1) as Col2,
substring(T.Col, S2.Pos+1, S3.Pos-S2.Pos-1) as Col3,
substring(T.Col, S3.Pos+1, S4.Pos-S3.Pos-1) as Col4,
substring(T.Col, S4.Pos+1, S5.Pos-S4.Pos-1) as Col5,
substring(T.Col, S5.Pos+1, S6.Pos-S5.Pos-1) as Col6
from (select #String+replicate(#Delimiter, 6)) as T(Col)
cross apply (select charindex(#Delimiter, T.Col, 1)) as S1(Pos)
cross apply (select charindex(#Delimiter, T.Col, S1.Pos+1)) as S2(Pos)
cross apply (select charindex(#Delimiter, T.Col, S2.Pos+1)) as S3(Pos)
cross apply (select charindex(#Delimiter, T.Col, S3.Pos+1)) as S4(Pos)
cross apply (select charindex(#Delimiter, T.Col, S4.Pos+1)) as S5(Pos)
cross apply (select charindex(#Delimiter, T.Col, S5.Pos+1)) as S6(Pos)
)
Test:
declare #T table (Col varchar(100))
insert into #T values
('Name Account 445566 0010020056893010445478008 AFD 369'),
(''),
('1 2'),
('1 3')
select S.Col1, S.Col2, S.Col3, S.Col4, S.Col5, S.Col6
from #T as T
cross apply
dbo.Split6(T.Col, ' ') as S
Result:
Col1 Col2 Col3 Col4 Col5 Col6
---- ------- ------ ------------------------- ---- ----
Name Account 445566 0010020056893010445478008 AFD 369
1 2
1 3
You might try using a PIVOT.
http://msdn.microsoft.com/en-us/library/ms177410.aspx