Find the maximum value in a column for each partition - oracle

I have table structure like:
CREATE TABLE new_test
( col1 NUMBER(2) NOT NULL,
col2 VARCHAR2(50) NOT NULL,
col3 VARCHAR2(50) NOT NULL,
col4 VARCHAR2(50) NOT NULL
);
It has data:
col1 col2 col3 col4
0 A B X
1 A B Y
2 A B Z
1 C D L
3 C D M
I need to find value of col4 which has maximum value for combination of col2 and col3. e.g. my result should be:
col4
Z
M
I tried to use oracle analytic function:
SELECT col4, MAX(col1) OVER (PARTITION BY col2, col3) FROM (
SELECT col2, col3, col4, col1
FROM new_test);
But it is not working as expected. Can you please help me to resolve this issue?
Update:
I could make it work using:
SELECT a.col4
FROM new_test a,
(SELECT col2,
col3,
col4,
MAX(col1) OVER (PARTITION BY col2, col3) AS col1
FROM new_test
) b
WHERE a.col2 = b.col2
AND a.col3 = b.col3
AND a.col4 = b.col4
AND a.col1 = b.col1;
Is there any better way than this?

If you expect that result:
col4
Z
M
You should write:
SELECT MAX(col4) AS col4 FROM new_test GROUP BY col2,col3
This will return the maximum value found in column col4 for each pair of col2,col3
If you need that:
COL2 COL3 COL4
A B Z
C D M
Write:
SELECT col2,col3,MAX(col4) AS col4 FROM new_test GROUP BY col2,col3
Finally, if you need:
COL1 COL2 COL3 COL4
2 A B Z
3 C D M
There are many variations. Like this one:
SELECT col1,col2,col3,col4
FROM new_test
NATURAL JOIN (SELECT col2,col3,MAX(col4) AS col4
FROM new_test GROUP BY col2,col3)

Related

Splitting a row at a given column

I have a row with 12 columns. What I need to do for this application is create 2 rows, with the first 6 columns in first row, and the second 6 columns in the second row.
COL1 COL2 COL3 COL4 COL5 COL6 COL7 COL8 COL9 COL10 COL11 COL12
I need something like this:
COL1 COL2 COL3 COL4 COL5 COL6
COL7 COL8 COL9 COL10 COL11 COL12
Is this possible to achieve?
If the columns be all of the same type, then a union query might work:
SELECT COL1, COL2, COL3, COL4, COL5, COL6 FROM yourTable
UNION ALL
SELECT COL7, COL8, COL9, COL10, COL11, COL12 FROM yourTable;
Apart from tim's answer, you can also use connect by as following:
SELECT T.RN,
CASE WHEN LVL = 1 THEN COL1 ELSE COL7 END,
CASE WHEN LVL = 1 THEN COL2 ELSE COL8 END,
CASE WHEN LVL = 1 THEN COL3 ELSE COL9 END,
CASE WHEN LVL = 1 THEN COL4 ELSE COL10 END,
CASE WHEN LVL = 1 THEN COL5 ELSE COL11 END,
CASE WHEN LVL = 1 THEN COL6 ELSE COL12 END
FROM (SELECT ROWNUM RN, T.* FROM YOUR_TABLE T)
JOIN (SELECT LEVEL AS LVL
FROM DUAL CONNECT BY LEVEL <= 2) ON (1=1)
Cheers!!

delete data from Table1 that doesn't exist in Table2

I have 2 different tables and I want to delete records from table1 which does not exist in Tables2
Table1:
select col1 from Table1
Table2:
select
concat('A_',col1)
from
Table2
where
Col2 = '748'
and Col3 = 'D'
and Col4 = 'Account'
now I want to delete the difference from Table1...
This can be done using the minus operation, and an insert into statement.
insert into table3(col) (
select col1 from Table1
minus
select
concat('A_',col1)
from
Table2
where
Col2 = '748'
and Col3 = 'D'
and Col4 = 'Account'
)
Records can then be deleted from table1 using a delete statement like
delete from table1
where col1 in (
select col1 from Table1
minus
select
concat('A_',col1)
from
Table2
where
Col2 = '748'
and Col3 = 'D'
and Col4 = 'Account'
)
delete from table1 t1
where not exists ( select * from table2 where col2 || col3 || col4 = t1.col1 );
This will work EXCEPT for the following situation; you need to explain what you want in that case. The DELETE statement can be modified to accommodate.
If t1.col1 is NULL, it will be deleted even if there are rows in table2 where col2, col3 and col4 are all NULL. Is that situation possible (where t1.col1 and col2, col3, col4 in table2 are all NULL? In that case, should the row in t1 be kept rather than deleted?

Insert 1 row from 1 table and 2nd row form other table into a new table and so on

I have a table TABLE101 with the following fields:
COL1 COLB COLC COLD
ACT1 UYT 876 KJH
ACT2 CFG 976 TRY
I have another table TABLE102 as under:
COL1 COL2 COL3 COL4 COL5 COL6
ACt1 A1_B1 98 UI 2
ACT2 C1 00 N
ACT2 D1_D4 1 PP Y RT
ACT2 A1_F1 9T UI 2
Now i want to insert data into a 3rd table which has all the fields from table101 and table 102 like:
COL1 COLB COLC COLD COL2 COL3 COL4 COL5 COL6 LVL
ACT1 UYT 876 KJH 1
ACt1 A1_B1 98 UI 2 2
ACT2 CFG 976 TRY 1
ACT2 C1 00 N
ACT2 D1_D4 1 PP Y RT 2
ACT2 A1_F1 9T UI 2 2
So 1st i need to insert 1st row from table101 and for corresponding COL1 value i need to insert row from TABLE102.
In table101, COL1 is unique but in table 101 col1 can have multiple rows.
If i insert row from table101, i have to set lvl col to 1 and if i insert form table102 i set lvl col to 2
How can i do so?
You can use UNION ALL for combining your tables. Then use INSERT ... SELECT syntax for inserting into 3rd table.
SELECT COL1 , COLB, COLC , COLD, NULL AS COL2, NULL AS COL3, NULL AS COL4, NULL AS COL5, NULL AS COL6, 1 AS LVL FROM TABLE101
UNION ALL
SELECT COL1, NULL AS COLB, NULL AS COLC, NULL AS COLD, COL2, COL3, COL4 , COL5 , COL6 , 2 AS LVL FROM TABLE102
Setup:
create table table101 as
select 'ACT1' col1, 'UYT' colb, 876 colc, 'KJH' cold from dual union all
select 'ACT2' , 'CFG' , 976 , 'TRY' from dual
;
commit;
select * from table101;
COL1 COLB COLC COLD
---- ---- ---- ----
ACT1 UYT 876 KJH
ACT2 CFG 976 TRY
2 rows selected.
create table table102 as
select 'ACT1' col1, 'A1_B1' col2, '98' col3, 'UI' col4, null col5, '2' col6 from dual union all
select 'ACT2' , 'C1' , null , '00' , 'N' , null from dual union all
select 'ACT2' , 'D1_D4' , '1' , 'PP' , 'Y' , 'RT' from dual union all
select 'ACT2' , 'A1_F1' , '9T' , 'UI' , null , '2' from dual
;
commit;
select * from table102;
COL1 COL2 COL3 COL4 COL5 COL6
---- ----- ---- ---- ---- ----
ACT1 A1_B1 98 UI 2
ACT2 C1 00 N
ACT2 D1_D4 1 PP Y RT
ACT2 A1_F1 9T UI 2
4 rows selected.
create table table110 (col1 varchar2(4000), colb varchar2(4000), colc number, cold varchar2(4000),
col2 varchar2(4000), col3 varchar2(4000), col4 varchar2(4000), col5 varchar2(4000),
col6 varchar2(4000), lvl number);
Table TABLE110 created.
Insert statement and outcome:
insert into table110
select col1, colb, colc, cold, null, null, null, null, null, 1
from table101
union all
select col1, null, null, null, col2, col3, col4, col5, col6, 2
from table102
;
commit;
select * from table110;
COL1 COLB COLC COLD COL2 COL3 COL4 COL5 COL6 LVL
---- ---- ---- ---- ----- ---- ---- ---- ---- ---
ACT1 UYT 876 KJH 1
ACT2 CFG 976 TRY 1
ACT1 A1_B1 98 UI 2 2
ACT2 C1 00 N 2
ACT2 D1_D4 1 PP Y RT 2
ACT2 A1_F1 9T UI 2 2
6 rows selected.
select * from table110;

Oracle - Is it possible to "set" values inside case statement during update as below?

Is it possible to "set" values inside case statement during update as below ?
UPDATE TABLE1
CASE WHEN COL1 = 'A' THEN SET COL2 = 10, COL3 = 20, COL4 = 30
WHEN COL1 IN ('B','N') THEN SET COL2 = 1, COL3 = 5, COL4 = 7
WHEN COL1 = 'D' THEN SET COL2 = 11, COL3 = 13, COL4 = 17
ELSE SET COL2 = 0, COL3 = 0, COL4 = 0
END;
The corresponding valid syntax would be like this.
UPDATE TABLE1 SET
COL2 = (CASE WHEN COL1 = 'A' THEN 10
WHEN COL1 IN ('B','N') THEN 1
WHEN COL1 = 'D' THEN 11
ELSE 0
END),
COL3 = (CASE WHEN COL1 = 'A' THEN 20
WHEN COL1 IN ('B','N') THEN 5
WHEN COL1 = 'D' THEN 13
ELSE 0
END),
COL4 = (CASE WHEN COL1 = 'A' THEN 30
WHEN COL1 IN ('B','N') THEN 7
WHEN COL1 = 'D' THEN 17
ELSE 0
END);
Looks like you're trying to do a MERGE, with one exception. You can update the table in a single merge statement as follows, except your logic to update all non-matching rows to 0's.
SQL> create table tab1
(
col1 varchar2(10),
col2 number,
col3 number,
col4 number,
merge_flag char(1)
)
Table created.
SQL> insert into tab1 values ('A', 10,11,12,null)
1 row created.
SQL> insert into tab1 values ('B', 20,21,22,null)
1 row created.
SQL> insert into tab1 values ('C', 30,31,32,null)
1 row created.
SQL> commit
Commit complete.
SQL> select * from tab1
COL1 COL2 COL3 COL4 MERGE_FLAG
---------- ---------- ---------- ---------- ----------
A 10 11 12
B 20 21 22
C 30 31 32
3 rows selected.
SQL> merge into tab1 t
using (
select 'A' as col1, 10 as col2, 20 as col3, 30 as col4 from dual
union
select 'B' as col1, 1 as col2, 5 as col3, 7 as col4 from dual
union
select 'N' as col1, 1 as col2, 5 as col3, 7 as col4 from dual
union
select 'D' as col1, 11 as col2, 13 as col3, 17 as col4 from dual
) x
on (t.col1 = x.col1)
when matched then
update set t.col2 = x.col2, t.col3 = x.col3, t.col4 = x.col4, t.merge_flag = 'X'
Merge successfully completed.
SQL> commit
Commit complete.
SQL> select * from tab1
COL1 COL2 COL3 COL4 MERGE_FLAG
---------- ---------- ---------- ---------- ----------
A 10 20 30 X
B 1 5 7 X
C 30 31 32
3 rows selected.
You could run a single update after the merge to change all non matching rows with 0.
In order to do this via case, you would have to repeat the case for each field (as demonstrated by #MaheswaranRavisankar). That's just the way case works. An alternative would be to fabricate a sub-query that provides the same results. While this is longer, it does group related values together, which may be more readable/easier to maintain.
Given the need to set all non-matching values to zero, I would solve that by setting all values to zero first, then updating the matches the appropriate value.
UPDATE table1
SET col2 = 0, col3 = 0, col4 = 0;
UPDATE table1
SET (col2, col3, col4) =
(SELECT a.col2, a.col3, a.col4
FROM (SELECT 'A' AS col1,
10 AS col2,
20 AS col3,
30 AS col4
FROM DUAL
UNION ALL
SELECT 'B' AS col1,
5 AS col2,
5 AS col3,
7 AS col4
FROM DUAL
UNION ALL
SELECT 'N' AS col1,
5 AS col2,
5 AS col3,
7 AS col4
FROM DUAL
UNION ALL
SELECT 'D' AS col1,
13 AS col2,
7 AS col3,
17 AS col4
FROM DUAL) a
WHERE a.col1 = table1.col1);
This also hints at another option: perhaps you should consider creating a table with these values, rather than embedding them in the SQL.

Need to transform the rows into columns for the similar ID's in oracle

I need to transform the rows into columns for the similar ID's in oracle
e.g.
The following is the result I will get if i query my database
Col1 Col2 Col3
---- ---- ----
1 ABC Yes
1 XYZ NO
2 ABC NO
I need to transform this into
Col1 Col2 Col3 Col4 Col5
---- ---- ---- ---- ----
1 ABC Yes XYZ No
2 ABC NO NULL NULL
Someone please help me in solving this issue
Thanks,
Siv
Based on AskTom:
select Col1,
max( decode( rn, 1, Col2 ) ) Col_1,
max( decode( rn, 1, Col3 ) ) Col_2,
max( decode( rn, 2, Col2 ) ) Col_3,
max( decode( rn, 2, Col3 ) ) Col_4
from (
select Col1,
Col2,
Col3,
row_number() over (partition by Col1 order by Col2 desc nulls last) rn
from MyTable
)
group by Col1;
I don't have access to an Oracle db to test it but I think that will work. If there could be more than two records per ID then you could just add more rows to the select cause with the corresponding row number.
One solution is to use the 10g MODEL clause:
SQL> select col1
2 , col2
3 , col3
4 , col4
5 , col5
6 from t23
7 model
8 return updated rows
9 partition by ( col1 )
10 dimension by ( row_number() over ( partition by col1
11 order by col2 desc nulls last) rnk
12 )
13 measures (col2, col3, lpad(' ',4) col4, lpad(' ',4) col5)
14 rules upsert
15 (
16 col2 [0] = col2 [1]
17 , col3 [0] = col3 [1]
18 , col4 [0] = col2 [2]
19 , col5 [0] = col3 [2]
20 )
21 /
COL1 COL2 COL3 COL4 COL5
---------- ---- ---- ---- ----
1 ABC Yes ABC NO
2 XYZ NO
SQL>
It is an unfortunate truth about such solutions that we need to specify the number of columns in the query. That is, in regular SQL there is no mechanism for determining that the table contains three rows where COL1 = 1 so we need seven columns, which is not unreasonable. For situations in which the number of pivot values is unknown at the time of coding there is always dynamic sql.

Resources