SQL Compare grouped values - sql

If we have a table like:
col1 | col2
-----------
A | 1
B | 2
A | 1
C | 16
B | 3
How it can be determined if the all rows for given value in col1 are the same?
For example, here whe have only '1's for A, but for B we have '2' and '3'.
Something like:
A | true
B | false
C | true

select col1, case when count(distinct col2) = 1
then 'true'
else 'false'
end as same_col2_results
from your_table
group by col1

I have a preference for using min() and max() for this purpose, rather than count(distinct):
select col1,
(case when min(col2) = max(col2) then 'true' else 'false' end) as IsCol2Same
from table t
group by col1;
Then comes the issue of NULL values. If you want to ignore them (so a column could actually have two values, NULL and another value), then the above is fine (as is count(distinct)). If you want to treat NULL the same way as other values, then you need some additional tests:
select col1,
(case when min(col2) is null then 'true' -- All NULL
when count(col2) <> count(*) then 'false' -- Some NULL
when min(col2) = max(col2) then 'true' -- No NULLs and values the same
else 'false'
end) as IsCol2Same
from table t
group by col1;

Select distinct(t1.col1), case when t1.col2=t2.col2 then TRUE else FALSE end
from table t1, table t2 where t1.col1=t2.col2

Related

How to select only those rows where one column can have null or not null values for the same id

Can someone please help me on how to select only the rows which has not null value of a column where that column can have null or not null values for the same id
I have a table like this
I need an output like this
Your description is not very clear. If I do understand correctly your requirement, you want only 1 row per col1. Choose the row with most not null value ?
select *
from
(
select *, rn = row_number() over (partition by col1
order by case when col2 is not null then 1 else 0 end
+ case when col3 is not null then 1 else 0 end
+ case when col4 is not null then 1 else 0 end
desc)
from a_table
) d
where rn = 1

How to compare two row in same table and provide the mismatch data

I have two transaction in the same table , i want to compare the two transaction and if there is any mismatch it will provide the mismatch data.
|Col1|Col2|Col3|Col4|
Trans-1 |ABC |123 |321 |111 |
---------------------------------------------------
Trans-2 |ABC |333 |321 |123|
OUTPUT:-
|col2|col4|
Trans-1 |123 |111 |
---------------------------------------------------
Trans-2 |333 |123 |
#
Now i am able to fetch the column value and the different, using the below command. But the result is displaying as one line as below, is there any way to break the line after each transaction?
Actual:-
Col1| MAX(Col1) |MIN(Col1) |Col2 |MAX(Col2)| MIN(Col2)|
---------------------------------------------------------
same|ABC |ABC |diff |123 |321 |
Expected:
Col1| MAX(Col1) |MIN(Col1) |
---------------------------
same|ABC |ABC |
|Col2 |MAX(Col2)| MIN(Col2)|
----------------------------
|diff |123 |321 |
Team we did a blunder here? guess if we have a single row, in that case all will be same, how to achieve if there is a single transaction then it should FAIL instead of compare the same transaction.
You could try aggregating by Col1 and then displaying all records sharing a col1 value whose other columns are not in agreement:
SELECT
Col1,
CASE WHEN COUNT(DISTINCT Col2) > 1 THEN 'Col2' END AS Col2_diff,
CASE WHEN COUNT(DISTINCT Col3) > 1 THEN 'Col3' END AS Col3_diff,
CASE WHEN COUNT(DISTINCT Col4) > 1 THEN 'Col4' END AS Col4_diff
FROM yourTable
GROUP BY Col1
HAVING
COUNT(DISTINCT Col2) > 1 OR
COUNT(DISTINCT Col3) > 1 OR
COUNT(DISTINCT Col4) > 1;
One possible trick to compare values and considering null as regular value (i.e. null = null is TRUE) is to use DECODE
DECODE(value1,value2,1,0) returns 1 if the values are equal and 0 if they differ.
Here an example returning the names of the columns with a difference
select
case when decode(max(col1),min(col1),1,0) = 0 then 'col1 ' end ||
case when decode(max(col2),min(col2),1,0) = 0 then 'col2 ' end ||
case when decode(max(col3),min(col3),1,0) = 0 then 'col3 ' end ||
case when decode(max(col4),min(col4),1,0) = 0 then 'col4 ' end as diff_col_names
from trans
For wide tables you may pre-generate part of the query direct from the data dictionary
Here an example:
select
replace(q'[case when decode(max(${column_name}),min(${column_name}),1,0) = 0 then '${column_name} ' end ||]',
'${column_name}',
column_name) as dif_col_names
from user_tab_columns where table_name = 'TRANS'
order by column_id;
DIF_COL_NAMES
------------------------------------------------------------------
case when decode(max(COL1),min(COL1),1,0) = 0 then 'COL1 ' end ||
case when decode(max(COL2),min(COL2),1,0) = 0 then 'COL2 ' end ||
case when decode(max(COL3),min(COL3),1,0) = 0 then 'COL3 ' end ||
case when decode(max(COL4),min(COL4),1,0) = 0 then 'COL4 ' end ||
Simple copy and paste the result in the query. You'll have to remove the concatenation from the last line and replace it with the column name.
You can mark the columns as the same or different:
select (case when max(col1) = min(col1) and count(col1) = count(*) or
max(col1) is null then 'same' else 'diff'
end) as col1,
(case when max(col2) = min(col2) and count(col2) = count(*) or
max(col2) is null then 'same' else 'diff'
end) as col2,
(case when max(col3) = min(col3) and count(col3) = count(*) or
max(col3) is null then 'same' else 'diff'
end) as col3,
(case when max(col4) = min(col4) and count(col3) = count(*) or
max(col4) is null then 'same' else 'diff'
end) as col4
from t;
Note that this returns the same columns that are in the data. A SQL query returns a fixed set of columns, unless you use dynamic SQL (execute immediate in PL/SQL).
Issue Resolved:-
select 'ABC' as COLUMN_NAME,
(case when to_char(count(ABC)) >1 and to_char (max(ABC)) = to_char(min(ABC)) and to_char(count(ABC)) = count(*)
and to_char(max(ABC)) is null then 'same' else 'Diff'end)as COMPARISION_VALUE,
(case when to_char(count(ABC))=1 or to_char(min(ABC)) is null then 'No Values' else to_char(max(ABC)) end ) as TRANSACTION1,
to_char(min(ABC))as TRANSACTION2
from AAA where MID ='ASD';

how to output result of group by of two columns with one column values as row and another as columns?

I have table like this
id col1 col2
1 A 1
2 B 0
3 A 1
4 C 1
5 B 0
6 A 0
7 C 1
8 C 1
9 B 1
10 B 0
I need a query something like this
Values 1 0
A 2 1
B 1 3
C 3 0
In the above result the header shows the col2 distinct values (1,0) and rows names represents distinct values of col1. The values in the table shows the counts.
Any suggestion to get the result like this in postgresql?
You need conditional aggregation :
select col1,
sum(case when col2 = 1 then 1 else 0 end) as 1,
sum(case when col2 = 0 then 1 else 0 end) as 0
from table t
group by col1;
You could also use FILTER:
SELECT
col1,
COUNT(*) FILTER (WHERE col2 = 1) AS 1,
COUNT(*) FILTER (WHERE col2 = 0) AS 0,
FROM
foo
GROUP BY
col1;
Here are simpler ways to write this logic. The first is Postgres-specific:
select col1,
sum( (col2 = 1)::int ) as num_1,
sum( (col2 = 0)::int as num_0
from t
group by col1;
The second just uses arithmetic:
select col1,
sum( col2 ) as num_1,
sum( 1 - col2 ) as num_0
from t
group by col1;

SQL : MAX() OVER (PRITITON BY ... ORDER BY ..) : ORDER BY Clause is working looks like

I am trying to use MAX(case when col4='ABC' then col1 else 0 end) OVER (PARTITION BY col2 order by **col3**)
col1~col2~col3~col4
30 A B1 ABC
35 A A1 ABC
36 A NULL NULL
40 A X1 ABC
50 B M1 ABD
but I am getting the result as 40, but I want 35 as my result. It looks like
order by col3
is not getting applied before MAX aggregate. Is there any other way?
I can't write the row_number() in where clause, as we are trying to create columns and there are many columns and complicated logic around it.
Currently I am trying it in Teradata, but actually it will be implemented in HIVE.
This is too long for a comment. For this expression:
MAX(case when col4 = 'ABC' then col1 else 0 end) OVER (PARTITION BY col2 order by col3)
You should be getting:
col1~col2~col3~col4
30 A A1 ABC 30
40 A X1 ABC 40
50 B M1 ABD 0
If you want "30" for all, you might consider:
min(case when col4 = 'ABC' then col1 end) over ()
PARTITION BY will aggregate on distinct col2 values. Therefore, the MAX() value of the three col2 'A' values is 40.
If you want to return '35'. This is suggesting that A1 is the first row returned in the partition. First_Value() could be used to return the first row in the partition group.
FIRST_VALUE(CASE WHEN col4 = 'ABC' THEN col1
ELSE 0 END) OVER (PARTITION BY col2 order by col3)
EDIT: Have moved to window function in sub query to get correct row
Sample data (as table variable)
declare #t table(col1 int,col2 char(1),col3 char(2),col4 char(3))
insert #t values (30,'A','B1','ABC')
,(35,'A','A1','ABC')
,(36,'A',NULL,NULL)
,(40,'A','X1','ABC')
,(50,'B','M1','ABD')
Query using row_number() to get row to join to
select t.*, mx
from #t t
join (
select
col2,
case when col4='ABC' then col1 else 0 end as mx,
row_number() over (PARTITION BY col2 order by case when col3 is null then 1 else 0 end, col3) rn
from #t
) m on m.col2=t.col2 and rn=1
Result:
col1 col2 col3 col4 col2 mx
30 A B1 ABC A 35
35 A A1 ABC A 35
36 A NULL NULL A 35
40 A X1 ABC A 35
50 B M1 ABD B 0

SQL GROUP BY with a SUM CASE statement

I know that there are a lot of question like this:
SQL GROUP BY CASE statement with aggregate function
but I've tried doing:
with T as (select col1 , SUM(CASE WHEN col2 = 1 THEN col3 ELSE 0 END) AS Totale
from tabella
group by col1, col2)
select col1, Totale
from T
group by col1, Totale
but I'm getting
col1 | Totale
---------------
1 0
1 70
2 0
2 90
Instead of:
col1 | Totale
---------------
1 70
2 90
What's wrong with that?
UPDATE:
My bad, I try to keep it simple but I've overdone it.. What I've to do is:
with T as (select col1 , SUM(CASE WHEN col2 = 1 THEN col3 ELSE 0 END) AS TotaleA,
SUM(CASE WHEN col2 = 2 THEN col3 ELSE 0 END) AS TotaleB,
(...)
from tabella
group by col1, col2)
select col1, TotaleA, TotaleB
from T
group by col1, TotaleA, TotaleB
And the solution is to do without col2 in the grouping... I was afraid getting "invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause", but it's not.. Can I mark a comment as the correct answer?
You can also approach this problem by using a subquery:
SELECT
col1,
SUM(A) TotalA,
SUM(B) TotalB
FROM
(
select col1,
CASE WHEN col2 = 1 THEN col3 ELSE 0 END A,
CASE WHEN col2 = 2 THEN col3 ELSE 0 END B
from tabella
) t
GROUP BY Col1
Here is one idea. Not sure why you have this wrapped in a cte but why not make it simpler?
select col1
, SUM(col3) AS Totale
from tabella
where col2 = 1
group by col1