SQL: check if multiple values in the column SQL - sql

Is it possible to check if multiple values are in the column and based on that filter out one of them using a WHERE clause?
Obviously this code won't work, but here is the logical example of what I'd like to achieve:
SELECT *
FROM table
WHERE IF column includes ('value1', 'value2') THEN NOT IN 'value1'
example with conditions True:
column
value1
value1
value2
value2
value1
value3
value4
value4
result:
column
value2
value2
value3
value4
value4
Side note: process has to be automated as in one upload, dataset might contain value1 which should remain in place and in the next one both of them will be populated and only value2 will be valid.

If both val1 and val2 exist then exclude val1 otherwise no filter...
declare #t table (col varchar(10))
insert into #t
values
('val1'),('val1'),('val2'),('val3')
select *
from #t
where col <> case when 2 = (select count(*) from (select col from #t where col in('val1','val2') group by col)a)
then 'val1'
else '' end
Results:
col
val2
val3
This is an example when both are not present
declare #t2 table (col varchar(10))
insert into #t2
values
('val1'),('val1'),('val3')
select *
from #t2
where col <> case when 2 = (select count(*) from (select col from #t2 where col in('val1','val2') group by col)a)
then 'val1'
else '' end
Results:
col
val1
val1
val3
Note: the else value needs to be a value that cannot exist in the column col
Note2: This is answered using t-sql

Using QUALIFY. The idea is to compare value of the column against an array generated ad-hoc with case expression to handle subsitution logic:
SELECT *
FROM tab
QUALIFY NOT ARRAY_CONTAINS(col::VARIANT,
ARRAY_AGG(DISTINCT CASE WHEN col IN ('value1', 'value2') THEN 'value1' END) OVER());
For sample data:
CREATE OR REPLACE TABLE tab AS
SELECT $1 AS col
FROM (VALUES
('value1'), ('value1'), ('value2'),
('value2'), ('value1'), ('value3'),
('value4'), ('value4')
)s;
Output:
A more explicit approach is using windowed COUNT_IF:
SELECT *
FROM tab
QUALIFY col NOT IN (CASE WHEN COUNT_IF(col IN ('value1', 'value2')) OVER() > 1
THEN 'value1'
ELSE ''
END);

Related

WHERE IN clause in temporary columns?

I have a query that creates two temporary columns. Is there a way to check if column 2 value exists in column 1 value?
select x as column1, y as column 2
Result:
column 1 | column 2
x y
w x
how do I check if x exists in column 1 ? Ultimately I only want to get all the values in column 2 that do not have a matching value in column 1, Is this possible?
You can use EXCEPT for this:
Declare #testTable Table (col1 varchar(10), col2 varchar(10));
Insert Into #testTable (col1, col2)
Values ('x', 'y')
, ('w', 'x');
Select col2 From #testTable tt
Except
Select col1 From #testTable tt;

Showing the result of COALESCE into separate columns based from where they where retrieved

I have a table with many NULL values. Therefore I use the COALESCE function to retrieve the NON NULL values. This works fine when the result of the COALESCE is to be placed in a single Column. However I need to place the values of the COALESCE into separate Columns depending from where they where picked.
E.g. I have the following table.
SELECT COALESCE(Col1, Col2, Col3, Col4) FROM Table 1
Will produce:-
Column1
1
1
3
4
However I do not want that result but I want this result:-
Col1 Col2 Col3 Col4
1 - - -
- 1 - -
- - 3 -
- 4 - -
As you can see I want only one field populated (that why I'm suing COALESCE but the result of COALESCE should be placed as illustrated, NOTICE ONE VALUE PER ROW.
Any ideas of how I can achieve this result please.
coalesce can be built with case statements. You need something like the below:
select col1
, case when col1 is not null then null else col2 end 'Col2'
, case when col1 is not null or col2 is not null then null else col3 end 'Col3'
, case when col1 is not null or col2 is not null or col3 is not null then null else col4 end 'Col4'
from table
You can achieve this with a combination of PIVOT, UNPIVOT and ROW_NUMBER.
declare #t table(rn int identity(1,1) primary key, col1 int, col2 int, col3 int, col4 int);
insert #t values (1,null,null,null), (null,1,0,null), (null,null,3,null), (null,4,null,2);
with a as (
select *, ranking = row_number() over (partition by rn order by col)
from #t a
unpivot ([val] for [col] in ([col1],[col2],[col3],[col4])) p
)
select *
from a
pivot (min(val) for [col] in ([col1],[col2],[col3],[col4])) p
where ranking = 1

How to get records from one column such that there is no association with value in another column

I have the following MSSQL table:
Col1 Col2
A x
A y
A z
B x
B y
C x
C z
I want all the values from Col1 such that they have no record of association with a particular value of Col2
For example, I want value from Col1 such that 'z' does not occur for that value. The answer should be B
One another way:
select Col1
from your_table
group by Col1
having sum( case when Col2 = 'z' then 1 else 0 end ) = 0
You can make use of the lesser known EXCEPT keyword, like this :
SELECT Col1 FROM TableName
EXCEPT
SELECT Col1 FROM TableName WHERE col2 = 'z'
You can see this here -> http://rextester.com/KPZMB79095
Hope this helps!!!
There are a number of ways to do this. For me the clearest is to use EXCEPT:
SELECT Col1 FROM MyTable
EXCEPT
SELECT Col1 FROM MyTable WHERE Col2 = 'z';
Here we are clearly and simply saying that we want all the Col1 values, except those Col1 values that have a z entry. EXCEPT will automatically de-duplicate the result.
select distinct col1
from [table]
where col1 not in (
select col1 from [table] where col2 = 'z'
);
TRY THIS I think you want to retrieve the value where the given value does not exist in both the columns:
create table #sample(Col1 char(1), Col2 char(1))
insert into #sample values
('A', 'x'),
('A', 'y'),
('A', 'z'),
('B', 'x'),
('B', 'y'),
('C', 'x'),
('C', 'z')
declare #search char(1) = 'z'
select distinct col1
from #sample
where col1 not in (
select distinct Col1
from #sample
where (col1 = #search or col2 = #search))
OUTPUT:
col1
B

How to create separate rows for each unique value in source data

I have following table:
Cus_ID Work_Phone Home_Phone Mobile_Phone
1 x Blank x
2 x x Blank
3 x x x
.
.
. and so on (1000s of rows)
Work_Phone, Home_Phone, Mobile_Phone - varchar
x = some value present
I need to select from Source data to move it Target system like below, I need to create separate row for unique values for each Cus_ID. How do i do it?
Cus_ID Type ContactNo
1 Work x
1 Mobile x
2 Work x
2 Home x
3 Work x
3 Home x
3 Mobile x
.. and so on
Type, ContactNo - varchar
x = Should be the corresponding value from Source table
above result we can achieve using UNPIVOT or Cross Apply also by basing on your assumed data
declare #t table (PK varchar(1),col1 varchar(1),col2 varchar(1),col3 varchar(1))
insert into #t(PK,col1,col2,col3)values
('X','a','','c'),
('y','a','b',''),
('z','a','b','c')
Cross Apply :
select PK,value
from #t
cross apply
(
values
('I1', col1),
('I2', col2),
('I3', col3)
) c(col, value)
where value is not null AND value <> ''
order by PK, col
UNPIVOT
select PK,value
from #t
unpivot
(
value
for col in (col1, col2, col3)
) un
WHERE value <> ''
order by PK, col;
Assuming col1, col2 and col3 are of the same type, then:
SELECT pk, col2 AS target_value FROM your_table WHERE col2 IS NOT NULL
UNION
SELECT pk, col3 AS target_value FROM your_table WHERE col3 IS NOT NULL
UNION
SELECT pk, col4 AS target_value FROM your_table WHERE col4 IS NOT NULL
ORDER BY pk
Edit edit: here's the version with ISNULL tests, column headings and the rest, in response to your revised question:
SELECT Cus_ID, 'Work' AS Type, Work_Phone AS ContactNo FROM your_table
WHERE ISNULL(Work_Phone, '') <> ''
UNION
SELECT Cus_ID, 'Home' AS Type, Home_Phone AS ContactNo FROM your_table
WHERE ISNULL(Home_Phone, '') <> ''
UNION
SELECT Cus_ID, 'Mobile' AS Type, Mobile_Phone AS ContactNo FROM your_table
WHERE ISNULL(Mobile_Phone, '') <> ''
ORDER BY 1
If there's a chance the "blank" column may contain whitespace characters, then refine it yet further to:
... ISNULL(LTRIM(Work_Phone), '') <> ''
etc.

How to combine multiple rows into one with nulled values where row values differ

How can I do with SQL Server to get a single row where the only non-null values are the ones that are consistent and non-null through all the selected rows.
A B C D
10 NULL text NULL
4 abc text NULL
4 def text NULL
Should give the following row:
A B C D
NULL NULL text NULL
create table #t (col1 int, col2 char(3), col3 char(4), col4 int)
go
insert into #t select 10, null, 'text', null
insert into #t select 4, 'abc', 'text', null
insert into #t select 4, 'def', 'text', null
go
select
case when count(distinct isnull(col1, 0)) > 1 then null else max(col1) end as 'col1',
case when count(distinct isnull(col2, '')) > 1 then null else max(col2) end as 'col2',
case when count(distinct isnull(col3, '')) > 1 then null else max(col3) end as 'col3',
case when count(distinct isnull(col4, 0)) > 1 then null else max(col4) end as 'col4'
from
#t
go
drop table #t
go
EDIT: I added ISNULL to handle the issue identified by t-clausen.dk but this will only work if the 'default' values (i.e. zero and empty string) do not appear in the real data.
Daniel's comment about data types is also correct, but since we don't know the data types involved it's not easy to suggest an alternative. Providing a self-contained test script that uses the real data types is the best way to ask questions like this.
declare #t table(A int, b varchar(10), c varchar(max), d int)
insert #t values(10, null, 'text', null)
insert #t values(4, 'abc', 'text', null)
insert #t values(10, 'def', 'text', null)
select case when max(rna) > 1 then null else min(a) end,
case when max(rnb) > 1 then null else min(b) end,
case when max(rnc) > 1 then null else min(c) end,
case when max(rnd) > 1 then null else min(d) end
from
(
select rna = rank() over(order by a),
rnb = rank() over(order by b),
rnc = rank() over(order by c),
rnd = rank() over(order by d),
a, b,c,d
from #t
) e
If you have text columns replace the column type with varchar(max). Text columns are outdated.
Using count(distinct col1) was by first thought, but it doesn't count null values.
select count(distinct a) from (select cast(null as int) a) b
returns 0 rows
SELECT
CASE WHEN COUNT(DISTINCT col1) = 1
AND COUNT(col1) = COUNT(*)
THEN MIN(col1)
END AS col1
, CASE WHEN COUNT(DISTINCT col2) = 1
AND COUNT(col2) = COUNT(*)
THEN MIN(col2)
END AS col2
, CASE WHEN COUNT(DISTINCT col3) = 1
AND COUNT(col3) = COUNT(*)
THEN MIN(col3)
END AS col3
, CASE WHEN COUNT(DISTINCT col4) = 1
AND COUNT(col4) = COUNT(*)
THEN MIN(col4)
END AS col4
FROM
tableX