SQL Query Select first rank 1 row From Multiple ranks/Group - sql

I have following data
Table1
id col1 col2 col3
----------------------------------
1 abc 01/01/2012 -
1 abc 01/01/2012 A
2 abc 01/01/2012 -
2 abc 01/02/2012 -
3 abc 01/02/2012 -
3 xyz 01/01/2012 -
4 abc 01/02/2012 -
4 xyz 01/01/2012 -
4 xyz 01/02/2012 -
following is order to evaluate -
if(col1 is false) then evaluate col2 if(col2 is false) then col3:
Col1 - xyz has first preference from all values in this column
col2 - min date
col3 - not '-' or min(col3)
I want to return only one row for each id, if col1 fails go to col2, if this fails then go to col3 condition.
From above table result should be
id col1 col2 col3
----------------------------------
1 abc 01/01/2012 A
2 abc 01/01/2012 -
3 xyz 01/01/2012 -
4 xyz 01/01/2012 -
I tried using dense rank but it didn't help. I'm not sure how to perform this logic using any available function or sql logic.
for col1 - if more than one row for same code or xyz code then fail
for col2 - if more than one row with same min date then fail
[use this only if col1 condition fails]

You can specify many conditions to order by in your analytic function
SELECT *
FROM (SELECT id,
col1,
col2,
col3,
dense_rank() over (partition by id
order by (case when col1 = 'xyz'
then 1
else 0
end) desc,
col2 asc,
col3 asc) rnk
FROM your_table)
WHERE rnk = 1
I'm assuming that you want dense_rank given that you used the dense_rank tag. You don't talk about how you want to handle ties or whether ties are even possible, so it's not clear from the question itself whether you want to use the rank, dense_rank, or row_number analytic functions. If you are only ever fetching the highest ranking row per id, rank and dense_rank will behave identically and will return multiple rows if there are ties for first place. row_number will always return a single row by arbitrarily breaking the tie. If you want to fetch rows other than the first row per id, then you'll need to think about ties and you'll get different behavior from rank and dense_rank. If two rows are tied for first, dense_rank will assign the third row a rnk of 2 while rank will assign it a rnk of 3.
This seems to work for the sample data you posted
SQL> ed
Wrote file afiedt.buf
1 with x as (
2 select 1 id, 'abc' col1, to_date('01/01/2012', 'MM/DD/YYYY') col2, null col3 from dual union all
3 select 1 id, 'abc' col1, to_date('01/01/2012', 'MM/DD/YYYY') col2, 'A' col3 from dual union all
4 select 2 id, 'abc' col1, to_date('01/01/2012', 'MM/DD/YYYY') col2, null col3 from dual union all
5 select 2 id, 'abc' col1, to_date('01/02/2012', 'MM/DD/YYYY') col2, null col3 from dual union all
6 select 3 id, 'abc' col1, to_date('01/02/2012', 'MM/DD/YYYY') col2, null col3 from dual union all
7 select 3 id, 'xyz' col1, to_date('01/01/2012', 'MM/DD/YYYY') col2, null col3 from dual union all
8 select 4 id, 'abc' col1, to_date('01/02/2012', 'MM/DD/YYYY') col2, null col3 from dual union all
9 select 4 id, 'xyz' col1, to_date('01/01/2012', 'MM/DD/YYYY') col2, null col3 from dual union all
10 select 4 id, 'xyz' col1, to_date('01/02/2012', 'MM/DD/YYYY') col2, null col3 from dual
11 )
12 SELECT *
13 FROM (SELECT id,
14 col1,
15 col2,
16 col3,
17 dense_rank() over (partition by id
18 order by (case when col1 = 'xyz'
19 then 1
20 else 0
21 end) desc,
22 col2 asc,
23 col3 asc) rnk
24 FROM x)
25* WHERE rnk = 1
SQL> /
ID COL COL2 C RNK
---------- --- --------- - ----------
1 abc 01-JAN-12 A 1
2 abc 01-JAN-12 1
3 xyz 01-JAN-12 1
4 xyz 01-JAN-12 1

with tmp(id, col1, col2, col3, col1b, col3b) as
(select distinct id, col1, col2, col3,
case when col1 = 'xyz' then '0' else '1' || col1 end,
case when col3 = '-' then '1' else '0' || col3 end
from Table1)
select t1.id, t1.col1, t1.col2, t1.col3
from tmp t1
left join tmp t2 on t1.id = t2.id
and t1.col1b > t2.col1b
left join tmp t3 on t1.id = t3.id
and t1.col1b = t3.col1b
and t1.col2 > t3.col2
left join tmp t4 on t1.id = t4.id
and t1.col1b = t4.col1b
and t1.col2 = t4.col2
and t1.col3b > t4.col3b
where t2.id is null
and t3.id is null
and t4.id is null

Related

SQL Postgres union data with missed values

I have two results of queries:
id | col1 | col2 | col3
1 1 null 3j
2 2 12 35
3 null 32 31
4 null 43 33
5 null 44 4
id | col1 | col2 | col3
6 1 null 3j
7 2 null 35
8 3 null 31
9 4 null 33
10 5 null null
I need to do union:
id | col1 | col2 | col3
6 1 null 3j
7 2 12 35
8 3 32 31
9 4 43 33
10 5 null null
5 null 44 4
The problem is some values are missing
I wrote this big sql query to solve this problem:
select *
from (
select max(id) as id,
max(col1) as col1,
max(col2) as col2,
max(col3) as col3
from (
select max(id) as id,
max(col1) as col1,
max(col2) as col2,
max(col3) as col3
from (
select max(id) as id,
max(col1) as col1,
max(col2) as col2,
max(col3) as col3
from (
select *
from t1
where id = 1
union
select *
from t2
where id = 2
) t
group by case
when col1 is null
or
length(col1) =
0 then id
else col1 end
) t1
group by case
when col2 is null
or length(col2) = 0
then id
else col2 end
) t2
group by case
when col3 is null
or length(col3) = 0 then id
else col3 end
) t3
may be are there some ideas to simplify it? Or are there other approaches to enrich data efficiently, because I also need to do intersection, right, left, inner union and I don't want to build so monsters queries
well you cat try something like this:
union
select max(col1),
max(col2),
max(col3)
from t1
where id = 1
or id = 2
group by coalesce(nullif(col1, ''),
nullif(col2, ''),
nullif(col3, ''));
upd:
outer union
select max(col1),
max(col2),
max(col3)
from t1
where id = 1
or id = 2
group by coalesce(nullif(col1, ''),
nullif(col2, ''),
nullif(col3, ''))
having count = 1;
inner union
select max(col1),
max(col2),
max(col3)
from t1
where id = 1
or id = 2
group by coalesce(nullif(col1, ''),
nullif(col2, ''),
nullif(col3, ''))
having count > 1;
left and right are outer intersect with common query with 'where'

Sql Query for Unique and Duplicates in oracle sql?

I need to display unique records in one column and duplicates in another column in Oracle?
COL1 COL2
1 10
1 10
2 20
3 30
3 30
unique in one set duplicate in one set
col1 col2 col1 col2
2 20 1 10
1 10
3 30
3 30
You can use the group by for both cases with the having clause:
Unique records
select *
from table as t
inner join (
select col1, col2, count(*) as times
from table
group by col1, col2
having count(*) = 1) as t2 ON t.col1 = t2.col2 and t.col2 = t2.col2
Duplicate records:
select *
from table as t
inner join (
select col1, col2, count(*) as times
from table
group by col1, col2
having count(*) > 1) as t2 ON t.col1 = t2.col1 and t.col2 = t2.col2
Would something like this do? See comments within code.
SQL> with
2 test (col1, col2) as
3 -- sample data
4 (select 1, 10 from dual union all
5 select 1, 10 from dual union all
6 select 2, 20 from dual union all
7 select 3, 30 from dual union all
8 select 3, 30 from dual
9 ),
10 uni as
11 -- unique values
12 (select col1, col2
13 from test
14 group by col1, col2
15 having count(*) = 1
16 ),
17 dup as
18 -- duplicate values
19 (select col1, col2
20 from test
21 group by col1, col2
22 having count(*) > 1
23 )
24 -- the final result
25 select u.col1 ucol1,
26 u.col2 ucol2,
27 d.col1 dcol1,
28 d.col2 dcol2
29 from uni u full outer join dup d on u.col1 = d.col1;
UCOL1 UCOL2 DCOL1 DCOL2
---------- ---------- ---------- ----------
1 10
3 30
2 20
SQL>
You can identify the duplicate values using window functions, and then filter each query. Then to get unique records:
select col1, col2
from (select t.*, count(*) over (partition by col1) as cnt
from t
) t
where cnt = 1;
To get duplicates:
select col1, col2
from (select t.*, count(*) over (partition by col1) as cnt
from t
) t
where cnt > 1;

Getting the value of no grouping column

I know the basics in SQL programming and I know how to apply some tricks in SQL Server in order to get the result set, but I don't know all tricks in Oracle.
I have these columns:
col1 col2 col3
And I wrote this query
SELECT
col1, MAX(col3) AS mx3
FROM
myTable
GROUP BY
col1
And I need to get the value of col2 in the same row where I found the max value of col3, do you know some trick to solve this problem?
The easiest way to do this, IMHO, is not to use max, but the window function rank:
SELECT col1 , col2, col3
FROM (SELECT col1, col2, col3,
RANK() OVER (PARTITION BY col1 ORDER BY col3 DESC) rk
FROM myTable) t
WHERE rk = 1
BTW, the same syntax should also work for MS SQL-Server and most other modern databases, with MySQL being the notable exception.
A couple of different ways to do this:
In both cases I'm treating your initial query as either a common table expression or as an inline view and joining it back to the base table to get your added column. The trick here is that the INNER JOIN eliminates all the records not in your max query.
SELECT A.*,
FROM myTable A
INNER JOIN (SELECT col1 , MAX( col3 ) AS mx3 FROM myTable GROUP BY col1) B
on A.Col1=B.Col1
and B.mx3 = A.Col3
or
with CTE AS (SELECT col1 , MAX( col3 ) AS mx3 FROM myTable GROUP BY col1)
SELECT A.*
FROM MyTable A
INNER JOIN CTE
on A.col1 = B.Col1
and A.col3= cte.mx3
Here's an alternative that's just a slight extension of your existing group by query (ie. doesn't require querying the same table more than once):
with mytable as (select 1 col1, 1 col2, 1 col3 from dual union all
select 1 col1, 2 col2, 2 col3 from dual union all
select 1 col1, 1 col2, 3 col3 from dual union all
select 1 col1, 3 col2, 3 col3 from dual union all
select 2 col1, 10 col2, 1 col3 from dual union all
select 2 col1, 23 col2, 2 col3 from dual union all
select 2 col1, 12 col2, 2 col3 from dual)
SELECT
col1,
MAX(col2) keep (dense_rank first order by col3 desc) mx2,
MAX(col3) AS mx3
FROM
myTable
GROUP BY
col1;
COL1 MX2 MX3
---------- ---------- ----------
1 3 3
2 23 2

how to get the maximum occurrence value from a table for a combination?

I have the following table;
column 1 column 2 column 3
1 2 X
1 2 X
1 2 Y
1 3 Z
1 3 X
I need to write an SQL query to get the output as;
1 2 X (because X is the maximum occurrence)
1 3 Z or X(because number of occurrence of Z or X is same)
How do i do this ?
I think i have a solution for you, try this script using the functions RANK(), ROW_NUMBER() & DENSE_RANK(), you choose the function that fits with your needs :
with temp as (
select 1 as col1, 2 AS col2, 'X' as col3 union all
select 1 as col1, 2 AS col2, 'Y' as col3 union all
select 1 as col1, 2 AS col2, 'X' as col3 union all
select 1 as col1, 3 AS col2, 'Z' as col3 union all
select 1 as col1, 3 AS col2, 'T' as col3 union all
select 1 as col1, 3 AS col2, 'Y' as col3 union all
select 1 as col1, 3 AS col2, 'Y' as col3 union all
select 1 as col1, 4 AS col2, 'Y' as col3 union all
select 1 as col1, 4 AS col2, 'W' as col3)
,temp2 AS (
select
col1
,col2
,col3
,COUNT(1) nb_occurence
,RANK() OVER(PARTITION BY col1,col2 ORDER BY COUNT(1) DESC) Ordre_RANK
,ROW_NUMBER() OVER(PARTITION BY col1,col2 ORDER BY COUNT(1) DESC) Ordre_ROW_NUMBER
,DENSE_RANK() OVER(PARTITION BY col1,col2 ORDER BY COUNT(1) DESC) Ordre_DENSE_RANK
from temp
GROUP BY
col1
,col2
,col3 )
SELECT *
FROM temp2
--WHERE Ordre_RANK = 1
--WHERE Ordre_ROW_NUMBER = 1
--WHERE Ordre_DENSE_RANK = 1
I hope this will help you.

Select records where all rows have same value in two columns

Here is my sample table
Col1 Col2
A 1
B 1
A 1
B 2
C 3
I want to be able to select distinct records where all rows have the same value in Col1 and Col2. So my answer should be
Col1 Col2
A 1
C 3
I tried
SELECT Col1, Col2 FROM Table GROUP BY Col1, Col2
This gives me
Col1 Col2
A 1
B 1
B 2
C 3
which is not the result I am looking for. Any tips would be appreciated.
Try this out:
SELECT col1, MAX(col2) aCol2 FROM t
GROUP BY col1
HAVING COUNT(DISTINCT col2) = 1
Output:
| COL1 | ACOL2 |
|------|-------|
| A | 1 |
| C | 3 |
Fiddle here.
Basically, this makes sure that amount the different values for col2 are unique for a given col1.
Try this:
SELECT * FROM MYTABLE
GROUP BY Col1, Col2
HAVING COUNT(*)>1
For example SQLFiddle here
you can try either of the below -
select col1, col2 from
(
select 'A' Col1 , 1 Col2
from dual
union all
select 'B' , 1
from dual
union all
select 'A' ,1
from dual
union all
select 'B' ,2
from dual
)
group by col1, col2
having count(*) >1;
OR
select col1, col2
from
(
select col1, col2, row_number() over (partition by col1, col2 order by col1, col2) cnt
from
(
select 'A' Col1 , 1 Col2
from dual
union all
select 'B' , 1
from dual
union all
select 'A' ,1
from dual
union all
select 'B' ,2
from dual
)
)
where cnt>1;