Difference in two tables display difference in rows in single column - sql

I have two tables which return 3 columns (Account #, Amount, Site Number)
Sample:
Table # 1
111111, 200, 14
111111,-200, 14
111111, 400, 15
111111, -400, 15
Table # 2
111111, 201, 14
111111,-200, 14
111111, 400, 15
111111, -400, 15
I am trying to graft a query that will no only show me the differences between the two tables like minus or where not exists but allow me pivot the data that is different from the one table to a column in the return
Something like this:
Act#: TblA Amount TblB Amount Site
111111, 200, 201, 14
When I use minus it simply gives me back the row in the top table that is different so if I did:
select * from TblA
MINUS
select * from TblB
Result:
111111, 200, 14
I know there must be way to do this and any help would be great!

Maybe the following queries will help. Principle: find the differences between the tables (contents of A minus contents of B, and vice versa),
then use these in a join, in order to "collapse" the result set. Using your test data (Oracle 12c):
(
select c1, c2, c3, 'in table#1' location from table#1
minus
select c1, c2, c3, 'in table#1' from table#2
)
union all
(
select c1, c2, c3, 'in table#2 (not in table#1)' from table#2
minus
select c1, c2, c3, 'in table#2 (not in table#1)' from table#1
);
-- result
C1 C2 C3 LOCATION
---------- ---------- ---------- ---------------------------
111111 200 14 in table#1
111111 201 14 in table#2 (not in table#1)
If there are no duplicates for the C1/C3 combinations, the following JOIN will give you the required result. (Maybe this will be "good enough" for your situation ...)
select
A.c1
--, B.c1
, A.c2
, B.c2
, A.c3
--, B.c3
from
(
select * from table#1
minus
select * from table#2
) A join (
select * from table#2
minus
select * from table#1
) B on A.c1 = B.c1 and A.c3 = B.c3
;
-- result
C1 C2 C2 C3
---------- ---------- ---------- ----------
111111 200 201 14
Dbfiddle here.

You could use FULL JOIN:
SELECT *
FROM tabA a
FULL JOIN tabB b
ON a.id = b.id -- here should be PK or UNIQUE col
WHERE NOT EXISTS (SELECT a.Account, a.Amount, a.Site_Number FROM dual
INTERSECT
SELECT b.Account, b.Amount, b.Site_Number FROM dual);
db<>fiddle demo

Related

postgresql - count distinct combination of three columns- order doesn't matter

I'm trying to count distinct combinations of three columns, order of the columns doesn't matter
sample :
a a a
a a b
a b a
b b a
b a b
the result I'm getting :
a a a 1
a a b 1
a b a 1
b b a 1
b a b 1
desired result
aaa 1
aab 2
bba 2
You can use an ordered array
select v[1], v[2], v[3], count(*) n
from tbl t
cross join lateral (
select array_agg(col order by col) v
from (
values (c1),(c2),(c3)
) t(col)
) s
group by v[1], v[2], v[3];
db<>fiddle
Maybe you can use checksums for getting the required result eg if it is really just combinations 'a' and 'b' that you are dealing with, you could convert the letters to integers (by calling the ASCII() function) and add these up so that you get a checksum.
TABLE
create table t (c1, c2, c3 ) as
select 'a', 'a', 'a' union all
select 'a', 'a', 'b' union all
select 'a', 'b', 'a' union all
select 'b', 'b', 'a' union all
select 'b', 'a', 'b' ;
Checksums
select c1, c2, c3, ascii( c1 ) + ascii( c2 ) + ascii( c3 ) as checksum
from t ;
-- output
c1 c2 c3 checksum
a a a 291
a a b 292
a b a 292
b b a 293
b a b 293
If this works for you, then you can use window functions eg
select c1, c2, c3, rc_ as rowcount
from (
select c1, c2, c3
, count(*) over ( partition by ascii( c1 ) + ascii( c2 ) + ascii( c3 ) order by 1 ) rc_
, row_number() over ( partition by ascii( c1 ) + ascii( c2 ) + ascii( c3 ) order by 1 ) rn_
from t
) sq
where rc_ = rn_ ;
-- output
c1 c2 c3 rowcount
a a a 1
a b a 2
b a b 2
See dbfiddle.
If you are dealing with strings that cannot easily converted to integers, you could create a mapping between the strings and integers, and implement the map_ as a view (so that it is easy to use in subsequent queries) eg
MAP
-- {1} find all distinct elements
-- {2} map each element to an integer
create view map_
as
select val_, rank() over ( order by val_ ) weight_
from (
select distinct val_
from (
select distinct c1 val_ from t union all
select distinct c2 from t union all
select distinct c3 from t
) all_elements
) unique_elements ;
Once you have this map, you can use its values for creating checksums (maybe also in a view) ...
Checksums
create view t_checksums_
as
select c1, c2, c3, c1weight + c2weight + c3weight as checksum
from (
select
c1, ( select weight_ from map_ where c1 = map_.val_ ) c1weight
, c2, ( select weight_ from map_ where c2 = map_.val_ ) c2weight
, c3, ( select weight_ from map_ where c3 = map_.val_ ) c3weight
from t
) valandweight ;
... and then, you can use the same query as before, for obtaining the final result - see dbfiddle.

Remove select subquery from column list to main query

Query 1 (Before):
select ta.C1,
(SELECT tb.C1 from T2 tb WHERE tb.C2 = ta.C2)
from T1 ta
WHERE ta.C3=30025239;
I want to remove the subquery from column level.
I modified the code to add join
Query 2 (After):
select ta.C1, tb.C1
from T1 ta left outer join
T2 tb
on tb.C2 = ta.C2
WHERE ta.C3=30025239;
But if subquery returns blank (no value) then Query 1 returns data for ta.C1 and null for tb.C1 whereas Query 2 will return blank (no result).
I want result of Query 2 as same as Query 1
Why do you think your two queries are giving different responses? They are equivalent:
WITH t1 AS (SELECT 1 c1, 10 c2, 30025239 c3 FROM dual UNION ALL
SELECT 2 c1, 20 c2, 30025239 c3 FROM dual UNION ALL
SELECT 3 c1, 30 c2, 30025238 c3 FROM dual),
t2 AS (SELECT 100 c1, 10 c2 FROM dual UNION ALL
SELECT 300 c1, 30 c2 FROM dual)
select 'query 1' qry, ta.C1 ta_c1, (SELECT tb.C1 from T2 tb WHERE tb.C2 = ta.C2) tb_c1
from T1 ta WHERE ta.C3=30025239
UNION ALL
select 'query 2' qry, ta.C1, tb.C1
from T1 ta left outer join T2 tb on tb.C2 = ta.C2
WHERE ta.C3=30025239;
QRY TA_C1 TB_C1
------- ---------- ----------
query 1 1 100
query 1 2
query 2 1 100
query 2 2
The only difference with query 2 is that you're no longer benefiting from the scalar subquery caching you're getting in query 1. That may not matter if the ta.c2 column is unique or doesn't contain many repeated values.
Both the queries are same. However in question queries and their output columns are different. There may be more conditions in your query that changes the output.

How to write conditional select insert statement to fetch record from a table, and based on some particular column's values

for eg:
Table1:
c1,c2,c3 (1,10,'123')
Table2
c1,c2,c3,c4
Now I want to select record from table1 and if c3 column value starting two digit is 12 then I have to populate AA, and if 34 then BB in Table2 c4 column:
Table1 -- (1,10,'123')
Table2 -- (1,10,'123','AA')
Table1 -- (1,10,'3444')
Table2 -- (1,10,'3444','BB')
OP has provided additional information in Comments under the original post. In particular, c3 is VARCHAR2 and always begins with either 12 or 34.
The following should work. If c3 does not begin with 12 or 34, then the value inserted in column c4 will be NULL. That is the default behavior of CASE expressions, so it doesn't need to be coded explicitly; if instead something like 'ZZ' is desired in that case, then we can add an else clause to CASE. (The OP said c3 always begins with either 12 or 34; if so, this is a moot point.)
insert into table2 ( c1, c2, c3, c4 )
select c1, c2, c3,
case when c3 like '12%' then 'AA'
when c3 like '34%' then 'BB'
end
from table1
;
Demo:
SQL> create table table1 ( c1 number, c2 number, c3 varchar2(25) );
Table created.
SQL> insert into table1 ( c1, c2, c3 ) values ( 1, 10, '123' );
1 row created.
SQL> insert into table1 ( c1, c2, c3 ) values ( 1, 10, '3444' );
1 row created.
SQL> commit;
Commit complete.
SQL> select * from table1;
C1 C2 C3
---------- ---------- -------------------------
1 10 123
1 10 3444
2 rows selected.
Then:
SQL> create table table2 ( c1 number, c2 number, c3 varchar2(25), c4 varchar2(10) );
Table created.
SQL> select * from table2;
no rows selected
SQL> insert into table2 ( c1, c2, c3, c4 )
2 select c1, c2, c3,
3 case when c3 like '12%' then 'AA'
4 when c3 like '34%' then 'BB'
5 end
6 from table1
7 ;
2 rows created.
SQL> select * from table2;
C1 C2 C3 C4
---------- ---------- ------------------------- ----------
1 10 123 AA
1 10 3444 BB
2 rows selected.

SQL - find rows that have the same set of values in a column

I have a table and I want to select all rows that have exactly the same set of values that appear in a column and return them as a pair of a certain column.
For example, let's say I have a table named Table:
C1 C2
1 1
1 2
1 3
2 1
3 1
3 2
3 3
4 1
4 2
When I run my query, it should return the row:
1 3
because these are the two values in C1 that have the same set of values in column C2 (1,2,3).
I have an incorrect query below that returns all rows that have at least one matching value in C2 and I can't figure out how to correct it.
SELECT DISTINCT T1.C1, T2.C1
FROM Table T1, Table T2
WHERE T1.C1 != T2.C1
AND T1.C2 = T2.C2
AND T1.C1 < T2.C1
GROUP BY s1.suppId, s2.suppId;
Any help would be greatly appreciated, thanks.
As you do not specify, this one will handle the case of two sets of matching C2 values, outputting two rows - one for each matched set.
with thetable as (
SELECT 1 C1, 1 c2 from dual union
SELECT 1 C1, 2 c2 from dual union
SELECT 1 C1, 3 c2 from dual union
SELECT 2 C1, 1 c2 from dual union
SELECT 3 C1, 1 c2 from dual union
SELECT 3 C1, 2 c2 from dual union
SELECT 3 C1, 3 c2 from dual union
SELECT 4 C1, 1 c2 from dual union
SELECT 4 C1, 2 c2 from dual union
-- added fourrows to give a second set of matches
SELECT 5 C1, 1 c2 from dual union
SELECT 5 C1, 2 c2 from dual union
SELECT 6 C1, 1 c2 from dual union
SELECT 6 C1, 2 c2 from dual )
SELECT LIST_C2, List_c1
FROM (
SELECT list_c2
,LISTAGG(c1,',') WITHIN GROUP (ORDER BY c1) list_c1
FROM (
SELECT c1, LISTAGG(c2,',') WITHIN GROUP (ORDER BY c2) list_c2
FROM thetable
GROUP BY c1
)
group by list_c2
)
-- only bring back where we had more than one c1
WHERE instr(list_c1,',') != 0
Showing the following two sets of C2 values and their matching lists of C1s
LIST_C2 LIST_C1
"1,2" "4,5,6"
"1,2,3" "1,3"
listagg is the rescue (instead of count as commented by #Juan)
with lst as (
select c1, LISTAGG(c2, '; ') WITHIN GROUP (ORDER BY c2) c2_lst
from tst
group by c1
)
select lst1.c1 c1a, lst2.c1 c1b
from lst lst1
inner join lst lst2
on lst1.c2_lst = lst2.c2_lst and
lst1.c1 < lst2.c1
;
gives as requested
1 3
SQLFiddleDemo
Also be prepared, this works only small data (the concatenated key is limited with 4000 bytes).
UPDATE
listagg concatenates all values in the group. To get only distinct values (i.e. set of values) an additional query with DISTINCT must be performed.
WITH lst AS
( SELECT DISTINCT c1,c2 FROM tst
),
lst_dist AS
(SELECT c1,
LISTAGG(c2, '; ') WITHIN GROUP (
ORDER BY c2) c2_lst
FROM lst
GROUP BY c1
)
SELECT lst1.c1 c1a,
lst2.c1 c1b
FROM lst_dist lst1
INNER JOIN lst_dist lst2
ON lst1.c2_lst = lst2.c2_lst
AND lst1.c1 < lst2.c1
You can do this without list agg, using a self join:
select t1.c1, t2.c1
from (select t.*, count(*) over (partition by c1) as cnt
from table t
) t1 join
(select t.*, count(*) over (partition by c1) as cnt
from table t
) t2
on t1.c2 = t2.c2 and t1.c1 < t1.c2 and t1.cnt = t2.cnt
group by t1.c1, t2.c1
having count(*) = max(t1.cnt);
Note: this assumes that there are no duplicate rows in the table. A slight variation can work in that case as well.
This joins the rows on the second column and then aggregates by the first. Along the way, it makes sure that the number of matching columns is the same in the two table and that all columns match.

Select only rows that have unique fields

What is an SQL command that checks for rows that have rows with no duplicate fields in them.
ex:
A A A B B B should not be in the resulting table.
Only rows such as A B C D E F
i.e. given data like:
A A A B B B
A B C D E F
A A B G H Q
Should return A B C D E F
There is no simple command to do this.
is seems an unusual requirement and possibly an indication that the table is not in first normal form if all columns are interchangeable.
The following works in Microsoft SQL Server
;With YourData AS
(
select 'A' as C1, 'A' as C2, 'A' as C3, 'B' as C4, 'B' as C5, 'B' as C6 UNION ALL
select 'A' as C1, 'B' as C2, 'C' as C3, 'D' as C4, 'E' as C5, 'F' as C6
)
SELECT *
FROM YourData
WHERE 1 =
( SELECT TOP 1 COUNT(*) AS Cnt
FROM (
SELECT C1 AS C
UNION ALL
SELECT C2
UNION ALL
SELECT C3
UNION ALL
SELECT C4
UNION ALL
SELECT C5
UNION ALL
SELECT C6
) D
GROUP BY C
ORDER BY Cnt DESC
)
Select distinc * returns unique ROWS not unique values from fields.
You should compare each column's value with others. (Assuming column types are the same). For example, for a 4 column table you should do smoething like:
SELECT Col1, Col2, Col3, Col4 FROM MyTable WHERE
Col1 NOT IN (Col2,Col3,Col4) AND
Col2 NOT IN (Col3,Col4) AND
Col3 <> Col4
SELECT DISTINCT * FROM tablename
SELECT DISTINCT col FROM tabl
SELECT * FROM
mytable
WHERE mytable.col1 != mytable.col2 != mytable.col3 ...