Oracle query to select rows with unique code - sql

I have a table like this
C1 C2 C3 Code
1 2 3 33
1 2 3 34
2 4 1 14
1 2 3 14
i want to select only those record whose code is appearing only in single row. ie, in this case rows with code 33 and 34.. as they appear only once in this table.
How can i write a query for that

If you want only one pass over your data, then you can use this query:
SQL> create table mytable (c1,c2,c3,code)
2 as
3 select 1, 2, 3, 33 from dual union all
4 select 1, 2, 3, 34 from dual union all
5 select 2, 4, 1, 14 from dual union all
6 select 1, 2, 3, 14 from dual
7 /
Table created.
SQL> set autotrace on
SQL> select max(c1) c1
2 , max(c2) c2
3 , max(c3) c3
4 , code
5 from mytable
6 group by code
7 having count(*) = 1
8 /
C1 C2 C3 CODE
---------- ---------- ---------- ----------
1 2 3 33
1 2 3 34
2 rows selected.
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 FILTER
2 1 SORT (GROUP BY)
3 2 TABLE ACCESS (FULL) OF 'MYTABLE'
Regards,
Rob.

SELECT C1, C2, C3, Code FROM tablename
WHERE Code IN
(
SELECT Code FROM tablename
GROUP BY Code
HAVING count(Code) = 1
)

select C1, C2, C3, Code
from tablename T1
where not exists ( select T2.exclude
from tablename T2
where T2.Code = T1.Code
and T2.rowid <> T1.rowid
)
PS. Watch out for NULL values in the Code column

Related

How can I select a data from another column from rows that have been selected?

I tried my best to figure and google this out, but couldn't really find a solid answer to it.
The problem I'm facing is that
Table 1:
ID Value 1
1 a
2 b
3 c
Table 2:
ID Value 2
1 4a
3 5b
4 6c
and I'd basically have to select the value from Table 1 that doesn't exist on Table 2 (Thus, 'b')
I can select and identify the ID that I want by using minus function between the tables, but can't seem to figure out a way to call a query to instead call the data.
Use the MINUS as a subquery (i.e. an inline view) (lines #14 - 16):
Sample data:
SQL> with
2 table1(id, value1) as
3 (select 1, 'a' from dual union all
4 select 2, 'b' from dual union all
5 select 3, 'c' from dual
6 ),
7 table2 (id, value2) as
8 (select 1, '4a' from dual union all
9 select 3, '5b' from dual union all
10 select 4, '6c' from dual
11 )
Query begins here:
12 select a.*
13 from table1 a
14 where a.id in (select t1.id from table1 t1
15 minus
16 select t2.id from table2 t2
17 );
ID VALUE1
---------- ----------
2 b
SQL>
Alternatively, use not exists:
<snip>
12 select a.*
13 from table1 a
14 where not exists (select null
15 from table2 b
16 where b.id = a.id
17 );
ID VALUE1
---------- ----------
2 b
SQL>

SQL Group by fixed list of values

If I have two columns:
col1 col2 amount
1 2 15
2 3 12
1 3 10
3 1 4
3 2 3
And I perform a group by col1,col2 then I get a row for each combination (present) in the data.
My problem though is, that I dont always have all combinations, but I would want to return a row of each combination still. So if there isn't a combination. for example 2 -> 1 then I would want its value to be 0.
Can I somehow specify the "levels" of the group by?
I'm using SQL Oracle.
and the outcome I would want is:
1 -> 2 15
1 -> 3 10
2 -> 1 0
2 -> 3 12
3 -> 1 4
3 -> 2 3
With their respective amount, and 0 if they dont exist, or null works. ( I have a filter to exclude where col1 and col2 are same)
Generate all the rows using cross join and then filter for the ones you want:
select c1.col1, c2.col2, coalesce(t.amount, 0)
from (select 1 as co1l from dual union all
select 2 as co1l from dual union all
select 3 as co1l from dual
) c1 cross join
(select 1 as co12 from dual union all
select 2 as co12 from dual union all
select 3 as co12 from dual
) c2 left join
t
on t.col1 = c1.col1 and t.col2 = c2.col2
where c1.col1 <> c2.col2;

update value in table oracle depand on oldest date

I have two tables (T1,T2) connected to each other by foreign key columns (B,C).
T1
C B A
11 1 123
12 2 123
13 3 123
14 4 222
15 5 222
16 6 333
T2
A2 B2 C2 D
1 11 25/4/1972
2 12 2/11/1982
3 13 4/6/2000
4 14 2/7/1992
5 15 14/2/2010
6 16 6/3/1999
I need to update A2 value(T2) from A value(T1) according to oldest date in column D (T2) which gives the following result:
T2
A2 B2 C2 D
123 1 11 25/4/1972
2 12 2/11/1982
3 13 4/6/2000
222 4 14 2/7/1992
5 15 14/2/2010
333 6 16 6/3/1999
show resulet
This is far from a smart & nice solution, but - might be OK until someone posts something better.
Test case for the rest of you (saving you some time, as Omar chose not to):
create table t1 (c number, b number, a number);
create table t2 (a2 number, b2 number, c2 number, d date);
insert into t1
select 11, 1, 123 from dual union
select 12, 2, 123 from dual union
select 13, 3, 123 from dual union
select 14, 4, 222 from dual union
select 15, 5, 222 from dual union
select 16, 6, 333 from dual;
insert into t2 (b2, c2, d)
select 1, 11, date '1972-04-25' from dual union
select 2, 12, date '1982-11-02' from dual union
select 3, 13, date '2000-06-04' from dual union
select 4, 14, date '1992-07-02' from dual union
select 5, 15, date '2010-02-14' from dual union
select 6, 16, date '1999-03-06' from dual;
First update every row, then remove unnecessary values:
SQL> update t2 set
2 t2.a2 = (select t1.a
3 from t1
4 where t1.b = t2.b2
5 and t1.c = t2.c2
6 );
6 rows updated.
SQL> update t2 set
2 t2.a2 = (select distinct
3 case when min(x.d) over (partition by x.a2) = t2.d then t2.a2
4 else null
5 end
6 from t2 x
7 where x.a2 = t2.a2
8 );
6 rows updated.
SQL> select * from t2;
A2 B2 C2 D
---------- ---------- ---------- ----------
123 1 11 25.04.1972
2 12 02.11.1982
3 13 04.06.2000
222 4 14 02.07.1992
5 15 14.02.2010
333 6 16 06.03.1999
6 rows selected.
SQL>
Assuming that the dates are unique (as in your example), you can do:
update t2
set t2.a2 = (select t1.a from t1 where t1.b = t2.b2)
where t2.d = (select min(tt2.d)
from t1 join
t2 tt2
on tt2.b2 = t1.b
group by t1.a
);
If you do have duplicates, you can change the where to:
where (t2.b2, t2.d) = (select min(tt2.b2) keep (dense_rank first over order by tt2.d), min(tt2.d)
from t1 join
t2 tt2
on tt2.b2 = t1.b
group by t1.a
)

SQL pad query result for missing groups

Assume the following table:
TableA:
ID GroupName SomeValue
1 C 1
2 C 1
2 B 1
2 A 1
I need to construct a query that selects the following result:
ID GroupName SomeValue
1 C 1
1 B 0
1 A 0
2 C 1
2 B 1
2 A 1
The GroupName is actually derived from TableA column's CASE expression and can take only 3 values: A, B, C.
Are the analytic functions the way to go?
EDIT
Sorry, for not mentioning it, but the ID could consist of multiple columns. Consider this example:
ID1 ID2 GroupName SomeValue
1 1 C 1
1 2 C 1
2 2 C 1
2 2 B 1
2 2 A 1
I need to pad SomeValue with 0 for each unique combination ID1+ID2. So the result should be like this:
ID1 ID2 GroupName SomeValue
1 1 C 1
1 1 B 0
1 1 A 0
1 2 C 1
1 2 B 0
1 2 A 0
2 2 C 1
2 2 B 1
2 2 A 1
EDIT2
Seems like solution, proposed by #Laurence should work even for multiple-column 'ID'. I couldn't rewrite the query proposed by #Nicholas Krasnov to conform to this requirement. But could somebody compare these solutions performance-wise? Will the analytic function work faster than 'cross join + left outer join'?
To fill in gaps, you could write a similar query using partition by clause of outer join:
SQL> with t1(ID,GroupName,SomeValue) as
2 (
3 select 1, 'C', 1 from dual union all
4 select 2, 'C', 1 from dual union all
5 select 2, 'B', 1 from dual union all
6 select 2, 'A', 1 from dual
7 ),
8 groups(group_name) as(
9 select 'A' from dual union all
10 select 'B' from dual union all
11 select 'C' from dual
12 )
13 select t1.ID
14 , g.group_name
15 , nvl(SomeValue, 0) SomeValue
16 from t1
17 partition by (t1.Id)
18 right outer join groups g
19 on (t1.GroupName = g.group_name)
20 order by t1.ID asc, g.group_name desc
21 ;
ID GROUP_NAME SOMEVALUE
---------- ---------- ----------
1 C 1
1 B 0
1 A 0
2 C 1
2 B 1
2 A 1
6 rows selected
UPDATE: Response to the comment.
Specify ID2 column in the partition by clause as well:
SQL> with t1(ID1, ID2, GroupName,SomeValue) as
2 (
3 select 1, 1, 'C', 1 from dual union all
4 select 1, 2, 'C', 1 from dual union all
5 select 2, 2, 'C', 1 from dual union all
6 select 2, 2, 'B', 1 from dual union all
7 select 2, 2, 'A', 1 from dual
8 ),
9 groups(group_name) as(
10 select 'A' from dual union all
11 select 'B' from dual union all
12 select 'C' from dual
13 )
14 select t1.ID1
15 , t1.ID2
16 , g.group_name
17 , nvl(SomeValue, 0) SomeValue
18 from t1
19 partition by (t1.Id1, t1.Id2)
20 right outer join groups g
21 on (t1.GroupName = g.group_name)
22 order by t1.ID1, t1.ID2 asc , g.group_name desc
23 ;
ID1 ID2 GROUP_NAME SOMEVALUE
---------- ---------- ---------- ----------
1 1 C 1
1 1 B 0
1 1 A 0
1 2 C 1
1 2 B 0
1 2 A 0
2 2 C 1
2 2 B 1
2 2 A 1
9 rows selected
Select
i.Id1,
i.Id2,
g.GroupName,
Coalesce(a.SomeValue, 0) As SomeValue
From
(select distinct ID1, ID2 from TableA) as i
cross join
(select distinct GroupName from TableA) as g
left outer join
tableA a
on i.ID = a.ID and g.GroupName = a.GroupName
Order By
1,
2,
3 Desc

oracle sql group count

SELECT a,b,count(*)
FROM t
GROUP BY rollup(a,b)
result:
a1, b1, 10
a1, b2, 90
a1, , 100
i need:
a1, b1, 10, 100
a1, b2, 90, 100
how?
This table simulates your situation:
SQL> create table t (a,b)
2 as
3 select 'a1', 'b1'
4 from dual
5 connect by level <= 10
6 union all
7 select 'a1', 'b2'
8 from dual
9 connect by level <= 90
10 /
Table created.
Your result with only three rows misses the grand total, so that's a little inaccurate: rollup(a,b) results in 3 grouping sets with 4 rows.
SQL> select a
2 , b
3 , count(*)
4 from t
5 group by rollup(a,b)
6 /
A B COUNT(*)
-- -- ----------
a1 b1 10
a1 b2 90
a1 100
100
4 rows selected.
With a regular group by and an analytic function on top, you can achieve your desired result set:
SQL> select a
2 , b
3 , count(*)
4 , sum(count(*)) over (partition by a)
5 from t
6 group by a
7 , b
8 /
A B COUNT(*) SUM(COUNT(*))OVER(PARTITIONBYA)
-- -- ---------- -------------------------------
a1 b2 90 100
a1 b1 10 100
2 rows selected.
Regards,
Rob.
Use:
SELECT t.a,
t.b,
COUNT(*),
(SELECT COUNT(*)
FROM YOUR_TABLE
GROUP BY t.a)
FROM YOUR_TABLE t
GROUP BY t.a, t.b