Pick minimum value and update all the rows SQL - sql

I have a scenario where I need to pick up a minimum value of a priority column and take the product of those and put it in all the columns.
SD PL PRIO PRDT PNAME
1 29 10 MM CAR
1 LI 20 SS BRAKE
1 AA 30 AA ZZZZ
Since the Priority 10 is the minimum of gorup SD 1 MM should be replaced like below.
SD PL PRIO PRDT PNAME
1 29 10 MM CAR
1 LI 20 MM BRAKE
1 AA 30 MM ZZZZ
Could you please help with the select query.

You can use ROW_NUMBER:
SELECT
t.SD, t.PL, t.PRIO, t2.PRDT, t.PNAME
FROM YourTable t
INNER JOIN(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY SD ORDER BY PRIO) AS rn
FROM YourTable
) t2
ON t.SD = t2.SD
WHERE t2.rn = 1
How about using a correlated subquery:
UPDATE YourTable t
SET PRDT = (
SELECT PRDT
FROM YourTable t2
WHERE
t2.SD = t.SD
AND t2.PRIO = (SELECT MIN(t3.PRIO) FROM YourTable t3 WHERE t3.SD = t.SD)
)

Try this if you want only SELECT Query.
SELECT COL1,
COL2,
MIN(COL3) OVER(PARTITION BY COL1 ORDER BY COL3 ASC) COL3,
COL4,
COL5
FROM
(SELECT 1 COL1, '29' COL2, 10 COL3, 'MM' COL4, 'CAR' COL5 FROM DUAL
UNION ALL
SELECT 1 COL1, 'LI' COL2, 20 COL3, 'SS' COL4, 'BRAKE' COL5 FROM DUAL
UNION ALL
SELECT 1 COL1, 'AA' COL2, 30 COL3, 'AA' COL4 , 'ZZZZ' COL5 FROM DUAL
UNION ALL
SELECT 2 COL1, '29' COL2, 10 COL3, 'MM' COL4, 'CAR' COL5 FROM DUAL
UNION ALL
SELECT 2 COL1, 'LI' COL2, 05 COL3, 'SS' COL4, 'BRAKE' COL5 FROM DUAL
UNION ALL
SELECT 2 COL1, 'AA' COL2, 30 COL3, 'AA' COL4 , 'ZZZZ' COL5 FROM DUAL
);

Related

sort return data with SQL in SQLite order by row

I have a DB which has 8 columns, all are integers range 1~99:
Col1 Col2 Col3 Col4 Col5 Col6 Col7 Col8
1 13 24 18 35 7 50 88
13 4 33 90 78 42 26 57
22 18 30 3 57 90 71 8
...
When I perform "select Col1, Col2, Col3, Col5, Col6, Col7, Col8 from MyTable where Col4>10"
I would like the return data is sorted, e.g. the first row should return like this:
1,7,13,24,35,50,88
However, "order by" only work on "Column", is there anyway to preform this in SQL ? Or need a temp table/max() to perform this ? Thanks.
Regds
LAM Chi-fung
Your current design is not appropriate for this requirement.
Consider changing it to something like this:
CREATE TABLE tablename (
id INTEGER, -- corresponds to the rowid of your current table
col_id INTEGER NOT NULL, -- 1-8, corresponds to the number of each of the columns ColX
value INTEGER NOT NULL -- corresponds to the value of each of the columns ColX
);
You can populate it from your current table:
INSERT INTO tablename (id, col_id, value)
SELECT rowid, 1, Col1 FROM MyTable UNION ALL
SELECT rowid, 2, Col2 FROM MyTable UNION ALL
SELECT rowid, 3, Col3 FROM MyTable UNION ALL
SELECT rowid, 4, Col4 FROM MyTable UNION ALL
SELECT rowid, 5, Col5 FROM MyTable UNION ALL
SELECT rowid, 6, Col6 FROM MyTable UNION ALL
SELECT rowid, 7, Col7 FROM MyTable UNION ALL
SELECT rowid, 8, Col8 FROM MyTable
Now you can get the result that you want with GROUP_CONCAT() window function and aggregation:
SELECT result
FROM (
SELECT id, GROUP_CONCAT(value) OVER (PARTITION BY id ORDER BY value) result
FROM tablename
WHERE id IN (SELECT id FROM tablename WHERE col_id = 4 AND value > 10)
)
GROUP BY id
HAVING MAX(LENGTH(result))
See the demo.
Results:
result
1,7,13,18,24,35,50,88
4,13,26,33,42,57,78,90
Fix your data model! You should not be storing values in columns. You should be storing them in rows.
That is, SQL doesn't "sort columns". It deals with data in rows!
You can do what you want by unpivoting the data into rows, calculating the new order, and then reaggregating:
with t as (
select row_number() over () as id,
t.*
from mytable t
where col4 > 10
)
select max(case when seqnum = 1 then col end) as col1,
max(case when seqnum = 2 then col end) as col2,
max(case when seqnum = 3 then col end) as col3,
max(case when seqnum = 4 then col end) as col4,
max(case when seqnum = 5 then col end) as col5,
max(case when seqnum = 6 then col end) as col6,
max(case when seqnum = 7 then col end) as col7,
max(case when seqnum = 8 then col end) as col8
from (select row_number() over (partition by id order by col) as seqnum,
x.*
from (select id, col1 as col from t union all
select id, col2 as col from t union all
select id, col3 as col from t union all
select id, col4 as col from t union all
select id, col5 as col from t union all
select id, col6 as col from t union all
select id, col7 as col from t union all
select id, col8 as col from t
) x
) x
group by id;

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.

Counting Combinations

I have an Oracle table with multiple columns some populated with a variable, there a large number of possible variables the example below is not exhaustive.
ID Col1 Col2 Col3 Col4 Col5 Col6
-------------------------------------
1 X2 B2
2 C3 D1 R4
3 B2 X2
4 E4 T1 W2
5 X2 B2
6 R4 D1
7 D1 R4 C3
I need to identify the number of distinct combinations where row 1, row 3 and row 5 in the above example are considered the same combination and rows 2 and 7 are also considered the same. So the desired result would look like:
Col1 Col2 Col3 Col4 Col5 Col6 Count(*)
------------------------------------------------
B2 X2 3
C3 D1 R4 2
E4 T1 W2 1
D1 R4 1
But if I use this:
SELECT Col1, Col2, Col3, Col4, Col5, Col6, Count(*)
FROM MyTable
GROUP BY Col1, Col2, Col3, Col4, Col5, Col6
ORDER BY Count(*) DESC
Then row 3 in my data is considered unique. However, it has the same combination as rows 1 and row 5. Also row 2 and 7 are not considered the same and the result is:
Col1 Col2 Col3 Col4 Col5 Col6 Count(*)
------------------------------------------------
X2 B2 2
C3 D1 R4 1
B2 X2 1
E4 T1 W2 1
R4 D1 1
D1 R4 C3 1
It looks like I need to sort the col variables before comparing them. But is there an elegant solution to doing this for large record sets (3 million+ records) with up to 20 columns of data in Oracle?
There are two ways that come to my mind. First you can write a function accepting six or more strings and concatenating them in order. Then:
select colstring, count(*)
from
(
select id, concat_sorted(col1, col2, col3, col4, col5, col6) as colstring
from MyTable
)
group by colstring;
Another way would be to make each column a separate record and use listagg on them, provided you have Oracle 11g or higher available:
select colstring, count(*)
from
(
select id, listagg (colx, ',') within group (order by colx) as colstring
from
(
select id, col1 as colx from MyTable
union all
select id, col2 from MyTable
union all
select id, col3 from MyTable
union all
select id, col4 from MyTable
union all
select id, col5 from MyTable
union all
select id, col6 from MyTable
)
group by id
)
group by colstring
Try like this,
WITH t AS (
SELECT 1 ID, 'X2' col1, 'B2' col2, NULL col3, NULL col4, NULL col5, NULL col6 FROM dual
UNION
SELECT 2, 'C3', 'D1', 'R4', NULL, NULL, NULL FROM dual
UNION
SELECT 3, 'B2', 'X2', NULL, NULL, NULL, NULL FROM dual
UNION
SELECT 4, 'E4', 'T1', 'W2', NULL, NULL, NULL FROM dual
UNION
SELECT 5, 'X2', 'B2', NULL, NULL, NULL, NULL FROM dual
UNION
SELECT 6, 'R4', 'T1', NULL, NULL, NULL, NULL FROM dual
UNION
SELECT 7, 'D1', 'R4', 'C3', NULL, NULL, NULL FROM dual
)
SELECT col1, col2, col3, col4, col5, col6, tot_count
FROM (
SELECT col1, col2, col3, col4, col5, col6, cnt,
MAX(cnt) OVER (PARTITION BY val) AS tot_count,
row_number() OVER (PARTITION BY val ORDER BY cnt DESC) AS rn
FROM (
SELECT col1, col2, col3, col4, col5, col6, val, count(*) OVER (PARTITION BY val) cnt
FROM (
SELECT A.ID, col1, col2, col3, col4, col5, col6, val
FROM (SELECT ID, col1, col2, col3, col4, col5, col6
FROM t
) A,
(SELECT ID, listagg( val,',') WITHIN GROUP(ORDER BY val DESC) AS val
FROM (
SELECT ID, val
FROM t
unpivot ( val FOR origin IN (col1, col2, col3, col4, col5, col6))
)
GROUP BY ID
)b
WHERE A.ID = b.ID
)
ORDER BY val
)t1
)t2
WHERE tot_count = cnt
AND rn = 1
ORDER BY tot_count DESC;

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

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