row number based on unique column value in oralce? - oracle

I do need to implement a oracle sql to have row number defined as below:
row_num, column1, column2, cloumn3
1, ABC, 123, a1
1, ABC, 125, a2
2, ABD, 123, a3
2, ABD, 124, a4
2, ABD, 125, a5
3, ABE, 123, a1
Here I defined row number based on unique value of column1.
Can any one help me to write oracle sql to define row number in this way?
Thanks
Venkat

You need the DENSE_RANK() analytic function:
WITH your_table AS (SELECT 'ABC' col1, 123 col2, 'a1' col3 FROM dual UNION ALL
SELECT 'ABC' col1, 125 col2, 'a2' col3 FROM dual UNION ALL
SELECT 'ABD' col1, 123 col2, 'a3' col3 FROM dual UNION ALL
SELECT 'ABD' col1, 124 col2, 'a4' col3 FROM dual UNION ALL
SELECT 'ABD' col1, 125 col2, 'a5' col3 FROM dual UNION ALL
SELECT 'ABE' col1, 123 col2, 'a1' col3 FROM dual)
-- end of subquery mimicking your_table with data in; See SQL below:
SELECT dense_rank() OVER (ORDER BY col1) row_num,
col1,
col2,
col3
FROM your_table
ORDER BY col1, col2, col3;
ROW_NUM COL1 COL2 COL3
---------- ---- ---------- ----
1 ABC 123 a1
1 ABC 125 a2
2 ABD 123 a3
2 ABD 124 a4
2 ABD 125 a5
3 ABE 123 a1
DENSE_RANK() is similar to RANK() in that it will assign tied rows the same rank (that is, rows that have the same values in the columns being ordered - in your case, col1), but unlike RANK(), DENSE_RANK() won't skip rank numbers.

You can use this as well, but I forgot about dense rank. So Boneist's answer is better.
select rn.row_num , t.* from
(select column1,row_number() over (order by column1) as row_num from
(select distinct column1 column1 from tbl23) )rn
inner join tbl23 t
on rn.column1=t.column1

Related

Joining 2 tables for value comparision

I have 2 tables which contain data as follows:
Table1-
Col1. Col2
A1. B1
A1. B2
Table2-
Col1. Col2
A1. C1
A1. C2
Now when I am joining these 2 tables to compare col2, sometimes I get the output like A1-B1-C1;A1-B2-C2 and some time I get it as A1-B1-C2;A1-B2-C1.
But I don't want it to come in the second form ever.can anyone suggest any query which will help me achieve this.
Thanks in advance
You can do it with ROW_NUMBER() window function:
select t1.col1, t1.col2, t2.col2
from (
select t.*, row_number() over (partition by col1 order by col2) rn
from table1 t
) t1 inner join (
select t.*, row_number() over (partition by col1 order by col2) rn
from table2 t
) t2 on t2.col1 = t1.col1 and t2.rn = t1.rn
See the demo.
Results:
> COL1 | COL2 | COL2
> :--- | :--- | :---
> A1 | B1 | C1
> A1 | B2 | C2
The most obvious join is on col1 column which returns 4 rows:
SQL> with
2 t1 (col1, col2) as
3 (select 'A1', 'B1' from dual union all
4 select 'A1', 'B2' from dual
5 ),
6 t2 (col1, col2) as
7 (select 'A1', 'C1' from dual union all
8 select 'A1', 'C2' from dual
9 )
10 select a.col1, a.col2 a_col2, b.col2 b_col2
11 from t1 a join t2 b on a.col1 = b.col1
12 order by a.col1, a.col2, b.col2;
COL1 A_COL2 B_COL2
------- ------- -------
A1 B1 C1
A1 B1 C2
A1 B2 C1
A1 B2 C2
SQL>
These are all combinations you named, so - how do you "sometimes" get only two of them, and "sometimes" another two? What's wrong with another two results (as you said you don't want them to show at all)?
Yet another option is to use the RANK windows function as follows:
SELECT T1C1, T1C2, T2C2 FROM
(SELECT T1.COL1 T1C1, T1.COL2 T1C2, T2.COL1 T2C1, T2.COL2 T2C2,
RANK() OVER (PARTITION BY T1.COL1 ORDER BY T1.COL2) AS T1RN,
RANK() OVER (PARTITION BY T1.COL1 ORDER BY T2.COL2) AS T2RN
FROM TABLE1 T1 JOIN TABLE2 T2 ON T1.COL1 = T2.COL1)
WHERE T1RN = T2RN;

SQL query to get grouped sums and concatenated lists, preferably without using functions

I have a table like
colA colB colC
A 10 1
A 20 2
A 30 3
B 10 1
B 20 2
I want an output like this
ColA colB colC
A 60 1,2,3
B 30 1,2
Can someone tell me how to do it with and without using functions, and in PL/SQL?
Here is sql you needed.
Note : it will only run on oracle 11g R2 onwards.
with tab as(
select 'A' col1,10 col2,1 col3 from dual union all
select 'A' col1,20 col2,2 col3 from dual union all
select 'A' col1,30 col2,3 col3 from dual union all
select 'B' col1,10 col2,1 col3 from dual union all
select 'B' col1,20 col2,2 col3 from dual )
select col1, sum(col2),listagg(col3,',') WITHIN GROUP (ORDER BY col3) AS col3_list
from tab group by col1
Assume your table name is tab
with tab1(col1, col2) as (
select colA, sum(colB)
from tab group by colA
),
tab2(col1, col2) as (
select colA, LISTAGG(TO_CHAR(colC),',')
WITHIN GROUP (ORDER BY colC)
from tab group by colA
)
select a.col1, a.col2, b.col2
from tab1 a, tab2 b
where a.col1 = b.col1
OutPut:
COL1 COL2 COL2
A 60 1,2,3
B 30 1,2
For more information about LISTAGG link
This link explains about multiple with clause
Or simply;
select colA, sum(colB) colB,LISTAGG(TO_CHAR(colC),',')
WITHIN GROUP (ORDER BY colC)
from tab group by colA
Probably this may the easiest solution syntactically :)
SELECT COLA,
SUM(COLB),
WMSYS.WM_CONCAT(COLC)
FROM
(SELECT 'A' cola,10 AS colb,1 AS colc FROM dual
UNION
SELECT 'A' cola,20 AS colb,2 AS colc FROM dual
UNION
SELECT 'A' cola,30 AS colb,3 AS colc FROM dual
UNION
SELECT 'B' cola,10 AS colb,1 AS colc FROM dual
UNION
SELECT 'B' cola,20 AS colb,2 AS colc FROM dual
)
GROUP BY COLA ;
OUTPUT
COLA SUM(COLB) WMSYS.WM_CONCAT(COLC)
A 60 1,2,3
B 30 1,2

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.

fetch different columns from two tables and combine the results

I want to combine the Two different tables result in oracle.
conditions:
From both the tables ID ,STAMp columns are common columns.
Remaining columns are different.
Table1;
Element_ID STAMP Col1 Col2
1 22/03/2014 85 100
2 22/03/2014 95 105
Table2.
Element_ID STAMP Col3 Col4
5 22/03/2014 100 110
6 22/04/2014 200 210
Result:
Element_ID STAMP Col1 Col3
1 22/03/2014 85 null
5 22/03/2014 null 100
Query:
Select Element_ID, Stamp, Col1 from Table1 where element_ID in (1, 5)
Select Element_ID, STAMP, Col3 from Table2 where Element_ID in (1,5)
How to combine the above two queries results, and results should come as above format.
You can try as this,
Select Element_ID, Stamp, Col1, NULL Col3 from Table1 where element_ID in (1,5)
union
Select Element_ID, STAMP, NULL Col1, Col3 from Table2 where Element_ID in (1,5)
Select T1.Element_ID,T1.Stamp, T1.Col1, T2.Col3
from Table1 T1, Table2 T2
where T1.Element_ID in (1, 5)
This is one interesting use case for a RIGHT JOIN on some minimum set -- both to restrict the result to records 1 and 5 and to force the presence of those two records:
SELECT "Element_ID", "STAMP", "Col1", "Col3"
FROM Table1
FULL OUTER JOIN Table2
USING("Element_ID", "STAMP")
RIGHT JOIN
(
SELECT 1 "Element_ID" FROM DUAL
UNION ALL SELECT 5 FROM DUAL
) MinimumSet
USING("Element_ID")
See http://sqlfiddle.com/#!4/725d9/18 for a live example

How to get the following result?

How to get the following result ?
Input : -
t1
--------------
col1 col2 col3
--------------
101, abc, 100
101, xyz, 200
101, rst, 300
-------------
Output : -
101 abc 100 xyz 200 rst 300
Please try:
SELECT col1, replace(wm_concat(col2||col3),',', '') FROM t1 GROUP BY col1;
or
SELECT col1, (SELECT XMLAGG(xmlelement(X, X1.col2||col3)order by X1.col2).extract('//text()')
FROM t1 X1 WHERE X1.col1=X.col1)
FROM t1 X
This works with 11g and maintains the order of items.
select LISTAGG (code, ' ') WITHIN GROUP (ORDER BY rn, coln)
from(
select code, min(rn) as rn, min(coln) as coln
from(
select col1 as code, rownum rn, 1 as coln from t order by col1, col3
union all
select col2 as code, rownum rn, 2 as coln from t order by col1, col3
union all
select col3 as code, rownum rn, 3 as coln from t order by col1, col3
)
group by code
)
Try with
select col1 || ' ' || listagg( col2 || ' ' || col3, ' ' ) within group ( order by COL3 )
from t1
group by col1;

Resources