Trying to write a query that will display duplicates results as null - sql

I have a table that looks like the first example.
I'm trying to write a MSSQL2012 statement that that will display results like the second example.
Basically I want null values instead of duplicate values in columns 1 and 2. This is for readability purposes during reporting.
This seems like it should be possible, but I'm drawing a blank. No amount of joins or unions I've written has rendered the results I need.
| Col1 | Col2 | Col3 |
+------+------+------+
| 1 | 2 | 4 |
| 1 | 2 | 5 |
| 1 | 3 | 6 |
| 1 | 3 | 7 |
+------+------+------+
| Col1 | Col2 | Col3 |
+------+------+------+
| 1 | 2 | 4 |
| Null | null | 5 |
| null | 3 | 6 |
| null | null | 7 |
+------+------+------+

I would do this with no subqueries at all:
select (case when row_number() over (partition by col1 order by col2, col3) = 1
then col1
end) as col1,
(case when row_number() over (partition by col2 order by col3) = 1
then col2
end) as col2,
col3
from t
order by t.col1, t.col2, t.col3;
Note that the order by at the end of the query is very important. The result set that you want depends critically on the ordering of the rows. Without the order by, the result set could be in any order. So, the query might look like it works, and then suddenly fail one day or on a slightly different set of data.

Using a common table expression with row_number():
;with cte as (
select *
, rn_1 = row_number() over (partition by col1 order by col2, col3)
, rn_2 = row_number() over (partition by col1, col2 order by col3)
from t
)
select
col1 = case when rn_1 > 1 then null else col1 end
, col2 = case when rn_2 > 1 then null else col2 end
, col3
from cte
without the cte
select
col1 = case when rn_1 > 1 then null else col1 end
, col2 = case when rn_2 > 1 then null else col2 end
, col3
from (
select *
, rn_1 = row_number() over (partition by col1 order by col2, col3)
, rn_2 = row_number() over (partition by col1, col2 order by col3)
from t
) sub
rextester demo: http://rextester.com/UYA17142
returns:
+------+------+------+
| col1 | col2 | col3 |
+------+------+------+
| 1 | 2 | 4 |
| NULL | NULL | 5 |
| NULL | 3 | 6 |
| NULL | NULL | 7 |
+------+------+------+

Related

Create View with reverse Row values SQL

I have data like
name| col1 | col2 | col3 | col4 | col4 | col5 |
rv | rv1 | rv2 | rv3 | rv4 | | |
sgh | sgh1 | sgh2 | | | | |
vik | vik1 | vik2 | vik3 | vik4 |vik5 |vik6 |
shv | shv1 | shv2 | shv3 | shv4 |shv5 | |
Table Name: emp_data
to create View to get DATA like
name| col1 | col2 | col3 | col4 | col4 | col5 |
rv | rv4 | rv3 | rv2 | rv1 | | |
sgh | sgh2 | sgh1 | | | | |
vik | vik6 | vik5 | vik4 | vik3 |vik2 |vik1 |
shv | shv5 | shv4 | shv3 | shv2 |shv1 | |
MySql 8 supports LATERAL, this way you can sort values by positions and conditionally aggregate them back.
with tbl(name, col1, col2, col3 ,col4 ,col5 , col6) as
(
select 'rv ','rv1 ','rv2 ','rv3 ','rv4 ',null,null union all
select 'sgh','sgh1','sgh2', null,null,null,null union all
select 'vik','vik1','vik2','vik3','vik4','vik5','vik6' union all
select 'shv','shv1','shv2','shv3','shv4','shv5', null
)
select tbl.name, t.*
from tbl
, lateral (
select
max(case n when 1 then val end) col1,
max(case n when 2 then val end) col2,
max(case n when 3 then val end) col3,
max(case n when 4 then val end) col4,
max(case n when 5 then val end) col5,
max(case n when 6 then val end) col6
from (
select row_number() over( order by n) n, val
from (
select case when col1 is null then 99 else 6 end n, col1 val union all
select case when col2 is null then 99 else 5 end n, col2 val union all
select case when col3 is null then 99 else 4 end n, col3 val union all
select case when col4 is null then 99 else 3 end n, col4 val union all
select case when col5 is null then 99 else 2 end n, col5 val union all
select case when col6 is null then 99 else 1 end n, col6 val
) t
) t
) t
db<>fidle

How to query based on priority of column values

I have a table
| group | col1 | col2 |
| 1 | test1 | val1 |
| 1 | test2 | val2 |
| 3 | test3 | val3 |
| 3 | test4 | val4 |
I need to select rows by priority. For example, if row has col1 value as test1 so show it. If it's not then show test2. Don't remember about group. Just if values in one group.
I expect this result:
| group | col1 | col2 |
| 1 | test1 | val1 |
| 3 | test3 | val3 |
In standard SQL, you seem to want:
select t.*
from t
order by (case when col1 = 'test1' then 1
when col2 = 'test2' then 2
else 3
end)
fetch first 1 row only;
EDIT:
For the revised question, you can use distinct on:
select distinct on (group) t.*
from t
order by group,
(col1 = 'test1') desc,
(col1 = 'test2') desc;
Please use below query,
select * from
(select group, col1, col2, row_number() over (partition by group order by col1) as rnk
from table) where rnk = 1;
This is the query that work!
select * from
(select group,
col1,
col2,
row_number() over (partition by group order by (case when col1 = 'test1' then 2
when col1 = 'test2' then 1
else 3
end)) as rnk
from test) AS tab1 where rnk = 1;

Select most recent rows - last 24 hours

I have a table that looks like this:
col1 | col2 | col3 | t_insert
---------------------------------
1 | z | |2018-04-25 17:23:46.686816+10
1 | zy | |2018-04-26 18:53:46.686816+10
2 | f | |2018-04-26 19:23:46.686816+10
3 | g | |2018-04-27 17:23:46.686816+10
2 | z | |2018-04-27 18:23:46.686816+10
4 | z | |2018-04-27 20:13:46.686816+10
Where there are duplicate values in col1 I want to select by most recent timestamp and create a new column (col4) and insert the string 'update'.
Where there are not duplicate values in col1 I want to select the value and insert the string 'new' into col4.
Also I only want to select rows that have a timestamp from the last 24 hours.
The expected result: (This result dosen't show select rows from last 24 hours)
col1 | col2 | col3 | t_insert | col4 |
-------------------------------------------------------------
1 | zy | |2018-04-26 18:53:46.686816+10 |update |
3 | g | |2018-04-27 17:23:46.686816+10 |new |
2 | z | |2018-04-27 18:23:46.686816+10 |update |
4 | z | |2018-04-27 20:13:46.686816+10 |new |
Thanks in advance,
Hmmm, window function can help here:
select col, col2, col3, t_insert,
(case when cnt > 1 then 'update' else 'new' end) as col4
from (select t.*,
count(*) over (partition by col1) as cnt,
row_number() over (partition by col1 order by t_insert desc) as seqnum
from t
where t_insert >= now() - interval '24 hour'
) t
where seqnum = 1;

Get column with two two rows having specific values

I have a table that looks like this:
| col1 | col2 |
|------|------|
| a | 1 |
| a | 2 |
| a | 3 |
| b | 1 |
| b | 3 |
| c | 1 |
| c | 2 |
I need to find the value of col1 where two rows with the same col1 value exist that has a col2 value of 1 and 2
results would be:
| col1 |
|------|
| a |
| c |
You can filter the rows with the col2 values you want, then group by col1 and only take the groups with count = 2
select col1
from yourTable
where col2 in (1, 2)
group by col1
having count(distinct col2) = 2
Another solution would be
select col1
from your_table
group by col1
having sum(case when col2 = 1 then 1 else 0 end) > 0
and sum(case when col2 = 2 then 1 else 0 end) > 0

SQL Check if ungroup column values match

I have a sql table with following values
| col1 | col2| source | values
| 1 | 2 | A | null
| 1 | 2 | B | 1.0
| 1 | 2 | C | null
| 1 | 4 | A | 2.0
| 1 | 4 | B | 2.0
| 1 | 4 | C | 2.0
| 1 | 5 | A | null
| 1 | 5 | B | null
| 1 | 5 | C | null
How can I get an output with a group by of col1 and col2 with a flag:
all values match for a group ( flag = 1)
all values are null ( flag = 2)
some values is null (flag = 3)
Output:
| col1 | col2| flag
| 1 | 2 | 3
| 1 | 4 | 1
| 1 | 5 | 2
Or: based on your updated question:
SELECT
col1,
col2,
SUM(CASE WHEN SomeConditionHere THEN 1 ELSE 0 END) AS Flag
FROM Table1
GROUP BY col1, col2;
SQL Fiddle Demo
This will give you:
| COL1 | COL2 | FLAG |
----------------------
| 1 | 2 | 2 |
| 1 | 4 | 0 |
| 1 | 5 | 3 |
Note that: I assumed that the flag is how many NULL values are in the VALUES column, so I used "Values" IS NULL instead of SomeConditionHere.
I couldn't understand how the flag should be computed in the expected results you posted. You have to use the predicate that define your flag instead of "Values" IS NULL.
Update:
Try this:
WITH Flags
AS
(
SELECT
col1, col2,
COUNT(*) ValuesCount,
SUM(CASE WHEN "Values" IS NULL THEN 1 ELSE 0 END) AS NULLValues
FROM Table1
GROUP BY col1, col2
)
SELECT
col1,
col2,
Flag = CASE WHEN ValuesCount = NULLValues THEN 2
WHEN NULLVALUES = 0
AND ValuesCount = (SELECT COUNT(*)
FROM Table1 t2
WHERE t1.col1 = t2.col1
AND t1.col2 = t2.col2) THEN 1
ELSE 3
END
FROM Flags t1;
Updated SQL Fiddle Demo
This will give you:
| COL1 | COL2 | FLAG |
----------------------
| 1 | 2 | 3 |
| 1 | 4 | 1 |
| 1 | 5 | 2 |
In SQLServer2005+
;WITH cte AS
(
SELECT col1, col2, [values],
COUNT(CASE WHEN [values] IS NULL THEN 1 END) OVER(PARTITION BY col1, col2) AS cntNULL,
COUNT(*) OVER(PARTITION BY col1, col2) AS cntCol
FROM dbo.test5
)
SELECT col1, col2, MAX(CASE WHEN cntNULL = 0 THEN 1
WHEN cntNULL = cntCol THEN 2
ELSE 3 END) AS flag
FROM cte
GROUP BY col1, col2
Demo on SQLFiddle
...And solution without CTE if you want more portable SQL:
select col1,
col2,
case
when DistinctValuesWithoutNulls = 1 and NullCount = 0 then 1
when DistinctValuesWithoutNulls = 0 then 2
when NullCount > 0 then 3
end flag
from
(
select col1,
col2,
count(distinct [values]) DistinctValuesWithoutNulls,
sum(case when [values] is null then 1 else 0 end) NullCount
from Table1
group by col1, col2
) tmp