Oracle query eliminate duplicates not considering Null - sql

I've performed a full outer join where I get the following:
ColumnFromTable1 ColumnFromTable2
AAA ABA
AAA Null <- remove
AAA ACC
BBB Null
CCC CDC
Null EFE
DDD FFF
Null FFF <- remove
GGG FFF
What I really want is to squash down the rows to remove duplicates such that my results would be:
ColumnFromTable1 ColumnFromTable2
AAA ABA
AAA ACC
BBB Null
CCC CDC
Null EFE
DDD FFF
GGG FFF
Basically I need to eliminate
AAA Null
Null FFF
since I have an AAA or FFF with a non-null. But I need to keep
BBB Null
Null EFE
because there is no BBB or EFE with a non-null
I've tried modifying my full outer join (which I can post if need be) and also tried wrapping these results in a sub query.
EDIT here is the query simplified for this post
select ColumnFromTable1, ColumnFromTable2 from Table1 t1
full outer join Table2 t2 on t1.common_code = t2.common_code
group by ColumnFromTable1, ColumnFromTable2

row_number() looks promising:
select c1, c2
from (
select c1, c2,
row_number() over (partition by c1 order by c2) r1,
row_number() over (partition by c2 order by c1) r2
from t)
where not ((c1 is null and r2 > 1) or (c2 is null and r1 > 1))
demo
It eliminates null values when they are not first in order.

Placing the full outer join you already computed in a CTE, you can filter it further as you want by doing:
with f (c1, c2) as (
... -- full outer join you already computed here
)
a (x) as ( -- repeated c1 with non-null c2
select c1
from f
group by c1
having count(c2) > 0
),
b (x) as ( -- repeated c2 with non-null c1
select c2
from f
group by c2
having count(c1) > 0
)
select *
from f
where not(c1 in (select x from a) and c2 is null)
and not(c2 in (select x from b) and c1 is null)

Related

how to get the data using Joins or Cross apply

I have requirement
sample data :
Table A :
ID name
1 cat
2 Dog
3 Bird
Table B :
ID name
1 aaa
1 bbb
2 ccc
2 ddd
Table C :
ID name
1 xxx
1 yyy
1 zzz
2 www
Required Output :
ID name name name
1 cat aaa xxx
1 cat bbb yyy
1 cat null zzz
2 Dog ccc www
2 Dog ddd www
3 Bird NULL NULL
I have tried with different joins
Select a.ID,a.name,b.name,c.name from #A a
full join #b b
on a.ID = b.ID
full join #c c
on b.ID = c.ID
Can anyone suggest me the best way to Proceed?
You can use window function row_number to assign sequence number within each id in the order of increasing name for table b and c and then do a full join between them. Finally, do a left join with a table:
with b1 as (
select b.*, row_number() over (partition by id order by name) as rn
from b
),
c1 as (
select c.*, row_number() over (partition by id order by name) as rn
from c
)
select a.*, t.b_name, t.c_name
from a
left join (
select coalesce(b1.id, c1.id) as id,
b1.name as b_name,
c1.name as c_name
from b1
full join c1 on b1.id = c1.id
and b1.rn = c1.rn
) t on a.id = t.id;
This assumes that you need to join the tables b and c based on id and the position (in the order of name column).

Oracle 11g - Creating a side by side comparison of table comlumn data

I have a query where I need to extract data in a regular table and put two rows of data into a single row.
I have rows that consist of
StudentID AUDIT_ACTN Audit_Date .....
aaa A 01/01/2010
aaa A 03/04/2011
aaa A 02/02/2013
aaa D 09/10/2010
aaa D 05/06/2011
aaa D 06/07/2013
aaa A 11/12/2014~
bbb A 01/01/2010
bbb A 03/04/2011
bbb A 02/02/2013
bbb D 09/10/2010
bbb D 05/06/2011
bbb D 06/07/2013
bbb A 11/12/2014~
I want output like this
StudentID AUDIT_ACTN Audit_Date StudentID AUDIT_ACTN Audit_Date
aaa A 01/01/2010 aaa D 09/10/2010
aaa A 03/04/2011 aaa D 05/06/2011
aaa A 02/02/2013 aaa D 06/07/2013
aaa A 11/12/2014 NULL NULL NULL
bbb A 01/01/2010 bbb D 09/10/2010
bbb A 03/04/2011 bbb D 05/06/2011
bbb A 02/02/2013 bbb D 06/07/2013
bbb A 11/12/2014 NULL NULL NULL
The A & D data rows are related, a= add the something to the record and d = delete something from the record (something is an indicator). These is logical in that you must add something before you delete it and you cannot add it twice, without deleting it first.
My current script is probably going down the wrong track but here goes;
select a.StudentId,a.Audit_Date,a.AUDIT_ACTN,d.StudentId,Audit_Date,d.AUDIT_ACTN,
from table a
join
(select *
from
(Select StudentId, Audit_Date,AUDIT_ACTN
from table b
Where b.AUDIT_ACTN='D'
order by Audit_Date
)
where rownum=1
) d on a.StudentId = d.StudentId
and a.AUDIT_ACTN='A'
and Select * from (Select Audit_Date
Order by a.StudentId, a.Audit_Date
I know this is wrong but where do I go from here. If anyone can help and point me in the right direction. It would be appreciated.
My current attempts bring me zero rows, when I take out the rownum it brings me a x join returning 12 rows in this case.
thanks
Roger
I think you need aggregate function like below
select
A.StudentId,A.Audit_Date,A.AUDIT_ACTN,
D.StudentId,D.Audit_Date,D.AUDIT_ACTN
from
(Select StudentId, Audit_Date,AUDIT_ACTN,ROW_NUMBER()
OVER (PARTITION BY StudentId order by audit_date) rn
from table b
Where b.AUDIT_ACTN='D'
) D
FULL OUTER JOIN
(Select StudentId, Audit_Date,AUDIT_ACTN,ROW_NUMBER()
OVER (PARTITION BY StudentId order by audit_date) rn
from table b
Where b.AUDIT_ACTN='A'
) A
on A.rn = D.rn and A.StudentId = B.StudentId
I hope this will work

Append tables in SQL

If I have the two following tables,
Table1
ItemNo Desc Order Number Qty S_Date Location
AA AA AAA A AA/AA/AAAA AAAA
BB BB BBB B BB/BB/BBBB BBBB
CC CC CCC C CC/CC/CCCC CCCC
Table 2
M_Order Item M_Date Total
XXX X XX/XX/XXXX XX
YYY Y YY/YY/YYYY YY
Can anyone advice me how to get the following table please.
Result Table
ItemNo Desc Order Number Qty S_Date Location M_Date Total
AA AA AAA A AA/AA/AAAA AAAA
BB BB BBB B BB/BB/BBBB BBBB
CC CC CCC C CC/CC/CCCC CCCC
X XXX XX/XX/XXXX XX
Y YYY YY/YY/YYYY YY
Thanks
You can do this with a full outer join trick:
select t1.*, t2.*
from table1 t1 full outer join
table2 t2
on 1 = 0;
This will give you the columns in both tables. Each row will be populated with values from only one of the tables.
Similar to Gordons answer but the use of stars in SQL is one of the biggest NO NO's there is.
Also for the actual join part itself you need to do tablename.column = tablename.column on the unique columns. 1 = 0 is a bit ambiguous. but you get the picture
Use the below query join those two tables.
I think you wish to combine the values of two [(ItemNo and Item) and (Desc and M_Desc)] columns in same column and all the other columns separately.
SELECT CAST(ItemNo AS VARCHAR(MAX)) AS ItemNo, CAST(Desc AS VARCHAR(MAX)) AS Desc, OrderNumber, Qty,
S_Date, Location, NULL AS M_Date, NULL AS Total
FROM Table1
UNION ALL
SELECT CAST(Item AS VARCHAR(MAX)) AS ItemNo, CAST(M_Order AS VARCHAR(MAX)) AS Desc, NULL AS OrderNumber, NULL AS Qty,
NULL AS S_Date, NULL AS Location, M_Date, Total
FROM Table2

Match SQL Tables and find if entry exist in each group

Thanks for the response but if the scenario is extended up to add a third Group_ID with just 2 GroupNames only the it picks up that too .
Please help me in writing a query which will match 2 tables and find the group id in which all the entries of second table match.
Table 1
Group_Id GroupNames GroupValues
111 G1 A
111 G1 B
111 G1 C
111 G2 D
111 G2 E
111 G2 F
111 G3 G
222 G1 A
222 G1 B
222 G1 C
222 G2 E
222 G2 F
222 G3 G
333 G3 G
333 G1 B
333 G1 C
Table 2:
GroupValues
B
D
G
H
OUTPUT
111
The Output of the query should be "111" since it has atleast one entry for all three Group Names "G1,G2,G3" . "222" is missing entry for Group Name G2 so it will not be returned.
Please assist.
You can do this with aggregation and a having clause:
select t1.Group_Id
from table1 t1 inner join
table2
on t2.GroupValues = t1.GroupValues
group by t1.Group_id
having count(distinct t1.GroupValues) = (select count(distinct GroupValues) from table2);
Note that the distinct is not necessary if you know there are no duplicate values.
Posting two solutions, one is little modification to what Gordon Linoff suggested and another a new one.
1) The inline view T1 add , incase if you have duplicate GroupValues within a particular Group_ID
select t1.Group_Id
from (select distinct Group_Id,GroupValues from table1 )t1 inner join
table2
on t2.GroupValues = t1.GroupValues
group by t1.Group_id
having count(distinct GroupValues) = (select count(distinct GroupValues) from table2)
2) In the SELECT bellow minus , is used for identifying the Group_ID's which are not complete (or has all the combinations of group_id and GroupValues ). These id's are removed from all the group_id's present in the table1, hence the result.
select distinct group_id
from table1
minus
select distinct Group_Id
from Table2 t2,(select distinct Group_Id from Table1 ) t1
where not exists (select 1
from Table1 t11
where t1.Group_Id = t11.Group_Id
and t11.GroupValues = t2.GroupValues)

sql script to create generate consecutive numbers depending on a column and again start with 1

I have a table with 3 columns in which one is empty.
its like this,
c1 c2 c3
1 1000
1 1001
1 1004
2 1005
2 1007
3 1009
I want to insert values to c3 like
c1 c2 c3
1 1000 1
1 1001 2
1 1004 3
2 1005 1
2 1007 2
3 1009 1
Can anybody help?
The simplest solution is the one that pilcrow describes above: for each record R, c3 is equal to the number of records that have the same c1 as R, and a c2 that is less than or equal to that of R. As a SQL statement:
UPDATE table_name t
SET c3 =
( SELECT COUNT(1)
FROM table_name
WHERE c1 = t.c1
AND c2 <= t.c2
)
;
(Replace table_name with your table-name, of course.)
This might be faster than the nested sub-select (but you will need to test it)
merge into your_table u
using
(
select c1,
c2,
row_number() over (partition by c1 order by c2) as rn
from your_table
) t on (t.c1 = u.c1 and t.c2 = u.c2)
when matched then
update
set u.c3 = rn;