SQL Check if ungroup column values match - sql

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

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

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

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

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 |
+------+------+------+

sql group by one column and then filter on one column and create new columns for different values

In an MS Access query, how can I group on one column, then using the max ID get specific values from the table. I know I can group and use max to get the max ID but then not sure how to get multiple max IDs.
If I have a table like this:
+----+------+-------+------+
| ID | Col1 | Col2 | Col3 |
+----+------+-------+------+
| 1 | a | one | 100 |
| 2 | a | two | 200 |
| 3 | a | three | 300 |
| 4 | a | four | 400 |
| 5 | a | five | 500 |
| 6 | b | one | 600 |
| 7 | b | two | 700 |
| 8 | b | three | 800 |
| 9 | b | four | 900 |
| 10 | b | one | 1000 |
| 11 | b | three | 1100 |
| 12 | b | six | 1200 |
| 13 | c | one | 1300 |
| 14 | c | two | 1400 |
| 15 | c | three | 1500 |
| 16 | c | four | 1600 |
| 17 | c | six | 1700 |
| 18 | c | three | 1800 |
| 19 | c | two | 1900 |
| 20 | c | one | 2000 |
+----+------+-------+------+
And I want to get this:
+------+------+------+-------+
| Col1 | one | two | three |
+------+------+------+-------+
| a | 100 | 200 | 300 |
| b | 1000 | 700 | 1100 |
| c | 2000 | 1900 | 1800 |
+------+------+------+-------+
When Col1 = b, and Col2 = one, ID = 6 and 10. Since 10 is larger it gets the value for that row which is 1000. Same for the rest of the table...
I cannot for the life of me figure out how to do this.
This returns the results you requested with your sample data in Access 2007.
SELECT
sub.Col1,
DLookup('Col3', 'YourTable', 'ID=' & sub.MaxOfID1) AS [one],
DLookup('Col3', 'YourTable', 'ID=' & sub.MaxOfID2) AS [two],
DLookup('Col3', 'YourTable', 'ID=' & sub.MaxOfID3) AS [three]
FROM
(
SELECT
y.Col1,
Max(IIf(y.Col2='one', y.ID, 0)) AS MaxOfID1,
Max(IIf(y.Col2='two', y.ID, 0)) AS MaxOfID2,
Max(IIf(y.Col2='three', y.ID, 0)) AS MaxOfID3
FROM YourTable AS y
GROUP BY y.Col1
) AS sub;
Since you have not mentioned your RDBMS, the query below is supported on almost all RDBMS.
SELECT Col1,
MAX(CASE WHEN Col2 = 'one' THEN Col3 END) one,
MAX(CASE WHEN Col2 = 'two' THEN Col3 END) two,
MAX(CASE WHEN Col2 = 'three' THEN Col3 END) three
FROM TableName
GROUP BY Col1
SQLFiddle Demo
OUTPUT
╔══════╦══════╦══════╦═══════╗
║ COL1 ║ ONE ║ TWO ║ THREE ║
╠══════╬══════╬══════╬═══════╣
║ a ║ 100 ║ 200 ║ 300 ║
║ b ║ 1000 ║ 700 ║ 1100 ║
║ c ║ 2000 ║ 1900 ║ 1800 ║
╚══════╩══════╩══════╩═══════╝
select col1,
max(case col2 when 'one' then col3 else 0 end) as One,
max(case col2 when 'two' then col3 else 0 end) as Two,
max(case col2 when 'three' then col3 else 0 end) as three
from MyTable
group by col1
This works in MS-Access:
TRANSFORM Max(tbl.Col3) AS MaxDiCol3
SELECT tbl.Col1
FROM tbl
WHERE (((tbl.Col2)="one" Or (tbl.Col2)="two" Or (tbl.Col2)="three"))
GROUP BY tbl.Col1
PIVOT tbl.Col2;
If you need to get the maximum ID, instead of the maximum value, I would suggest you to use this instead:
TRANSFORM Max(tbl.ID) AS MaxDiCol3
SELECT tbl.Col1
FROM tbl
WHERE (((tbl.Col2)="one" Or (tbl.Col2)="two" Or (tbl.Col2)="three"))
GROUP BY tbl.Col1
PIVOT tbl.Col2;
And save it as a query, for example Query1. You can then get the actual values with this:
SELECT
Query1.Col1,
tbl.Col3 AS one,
tbl_1.Col3 AS two,
tbl_2.Col3 AS three
FROM
((Query1 LEFT JOIN tbl ON Query1.one = tbl.ID)
LEFT JOIN tbl AS tbl_1 ON Query1.two = tbl_1.ID)
LEFT JOIN tbl AS tbl_2 ON Query1.three = tbl_2.ID;
I think you need Dynamic PIVOT and Group By
SQL FIDDLE DEMO
declare #cols varchar(max)
declare #query varchar(max)
DECLARE #selective nvarchar(max)
SELECT #selective =
STUFF((SELECT ',MAX(' + QUOTENAME(t.col2) +') as ' + QUOTENAME(t.col2) AS ColName
from (
select distinct col2
FROM yourtable
) t
FOR XML PATH( ''), TYPE).value ('.', 'nvarchar(max)'),1,1,'')
select #cols = STUFF((SELECT ',' + QUOTENAME(t.col2) AS ColName
from (
select distinct col2
FROM yourtable
) t
FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'),1,1,'')
set #query = ' ;with CTE_PivotTable
as
(
select ID, Col1, ' + #cols +
' from
( select ID, Col1, Col2, Col3
from yourtable
) t
pivot
(
max(col3) for col2 in ('+#Cols+')
) as P
)
select Col1, ' + #selective +
'from CTE_PivotTable
group by Col1'
exec(#query)
OUTPUT RESULT
COL1 | FIVE | FOUR | ONE | SIX | THREE | TWO
-----------------------------------------------------
a | 500 | 400 | 100 | (null)| 300 | 200
b | (null)| 900 | 1000 | 1200 | 1100 | 700
c |(null) | 1600 | 2000 | 1700 | 1800 |1900
Try this:
SELECT
t1. col1,
MAX(CASE WHEN t1.col2 = 'one' THEN t1.col3 END) AS 'one',
MAX(CASE WHEN t1.col2 = 'two' THEN t1.col3 END) AS 'two',
MAX(CASE WHEN t1.col2 = 'three' THEN t1.col3 END) AS 'three'
FROM tablename AS t1
INNER JOIN
(
SELECT col1, MAX(ID) AS MAXID
FROM tablename
WHERE col2 IN('one', 'two', 'three')
GROUP BY col1, col2
) AS t2 ON t1.col1 = t2.col1 AND t1.id = t2.maxid
GROUP BY t1.col1;
The JOIN with the subquery will give you only the values for the max id for each col1 and col2.
SQL Fiddle Demo
This will give you:
| COL1 | ONE | TWO | THREE |
------------------------------
| a | 100 | 200 | 300 |
| b | 1000 | 700 | 1100 |
| c | 2000 | 1900 | 1800 |
However: if you are using a RDBMS that supports the window functions and the PIVOT table operator, like SQL Server, you can do this:
WITH CTE
AS
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY col1, col2
ORDER BY ID) AS RN
FROM tablename
WHERE col2 IN('one', 'two', 'three')
)
SELECT *
FROM
(
SELECT col1, col2, col3
FROM CTE
WHERE RN <= 3
) AS t
PIVOT
(
MAX(col3)
FOR col2 IN([one], [two], [three])
) AS p;
Updated SQL Fiddle Demo