Complex SQL issue in DB2 - sql

I have a table TABLE_CLIENT_BOOK which contains USER_ID and BOOK_CODE. This table shows which all books are with a particular USER_ID.
USER_ID BOOK_CODE
------------------------------
1 123
1 124
1 567
2 123
2 432
3 432
3 567
-------------------------------
I have another table TABLE_BOOK which contains details about each book.
BOOK_CODE DETAILS
----------------------------------
123 abcd
124 pqrs
432 xyzw
567 lmnop
568 efgh
----------------------------------
I want to write a query between the two tables which will spit out USER_ID and BOOK_CODE where BOOK_CODE column should have the id of all books from TABLE_BOOK which that user doesn't have. For e.g. user 1 doesn't have books 432 and 568, user 2 doesn't have 124, 567 and 568 and user 3 doesn't have 123, 124, 568.
So the result table of the query would be:
USER_ID BOOK_CODE
----------------------------
1 432
1 568
2 124
2 567
2 568
3 123
3 124
3 568
-----------------------------
This report is to advertise the books which user doesn't have.
How do I achieve this in SQL in DB2 9 ?
Thanks for reading!

Do a CROSS JOIN to get all user/book combinations. Use NOT EXISTS to exclude already existing combinations:
select distinct tcb.USER_ID, tb.BOOK_CODE
from TABLE_CLIENT_BOOK tcb
cross join TABLE_BOOK tb
where not exists (select * from TABLE_CLIENT_BOOK tcb2
where tcb2.USER_ID = tcb.USER_ID
and tcb2.BOOK_CODE = tb.BOOK_CODE)
order by tcb.USER_ID, tb.BOOK_CODE
Alternatively, EXCEPT:
select tcb.USER_ID, tb.BOOK_CODE
from TABLE_CLIENT_BOOK tcb
cross join TABLE_BOOK tb
EXCEPT
select USER_ID, BOOK_CODE
from TABLE_CLIENT_BOOK
order by tcb.USER_ID, tb.BOOK_CODE
No DISTINCT needed here. EXCEPT removes the duplicates.
Executes as:
SQL>select distinct tcb.USER_ID, tb.BOOK_CODE
SQL&from TABLE_CLIENT_BOOK tcb
SQL& cross join TABLE_BOOK tb
SQL&where not exists (select * from TABLE_CLIENT_BOOK tcb2
SQL& where tcb2.USER_ID = tcb.USER_ID
SQL& and tcb2.BOOK_CODE = tb.BOOK_CODE)
SQL&order by tcb.USER_ID, tb.BOOK_CODE;
USER_ID BOOK_CODE
=========== ===========
1 432
1 568
2 124
2 567
2 568
3 123
3 124
3 568
8 rows found
SQL>select tcb.USER_ID, tb.BOOK_CODE
SQL&from TABLE_CLIENT_BOOK tcb
SQL& cross join TABLE_BOOK tb
SQL&EXCEPT
SQL&select USER_ID, BOOK_CODE
SQL&from TABLE_CLIENT_BOOK
SQL&order by tcb.USER_ID, tb.BOOK_CODE;
USER_ID BOOK_CODE
=========== ===========
1 432
1 568
2 124
2 567
2 568
3 123
3 124
3 568
8 rows found

Related

Is there a loop function which can automatize this?

I have these lines of code which you can find below and I want to automatize this (most probably using a loop function) by just giving the input: 'CITY', 'COUNTY', 'PARTNERS', 'PRODUCT'. Is there any way to do this?
Also the names of temporary tables ('CUSTOMERS_MND_0', CUSTOMERS_MND_1', 'CUSTOMERS_MND_2', 'CUSTOMERS_MND_3', 'CUSTOMERS_MND_4') are not important, so those can be renamed as the variable names.
The issue I'm facing is that "UNIQUE_KEY" is not unique and I need to have it unique in the final db. Please find an example.
Initial DB:
UNIQUE_KEY
CITY
COUNTY
PARTNERS
PRODUCT
111
VIENNA
A
W
1
111
NAPLES
B
X
7
112
VIENNA
B
Y
3
113
NAPLES
B
W
4
113
NAPLES
A
W
4
114
VIENNA
A
W
1
115
VIENNA
B
W
4
115
NAPLES
A
W
4
115
VIENNA
B
X
7
115
VIENNA
B
Y
3
116
NAPLES
B
W
4
116
NAPLES
A
W
4
116
VIENNA
A
W
1
FINAL DB:
UNIQUE_KEY
CITY
COUNTY
PARTNERS
PRODUCT
111
VIENNA
A
W
1
112
VIENNA
B
Y
3
113
NAPLES
B
W
4
114
VIENNA
A
W
1
115
VIENNA
B
Y
3
116
VIENNA
A
W
1
SELECT AA.*
INTO #CUSTOMERS_MND_1
FROM #CUSTOMERS_MND_0 AA
INNER JOIN (SELECT UNIQUE_KEY,
MAX(CITY) AS MAXCITY
FROM #CUSTOMERS_MND_0
GROUP BY UNIQUE_KEY) BB ON AA.UNIQUE_KEY = BB.UNIQUE_KEY
AND AA.CITY = BB.MAXCITY;
DROP TABLE #CUSTOMERS_MND_0;
SELECT AA.*
INTO #CUSTOMERS_MND_2
FROM #CUSTOMERS_MND_1 AA
INNER JOIN (SELECT UNIQUE_KEY,
MAX(COUNTY) AS MAXCOUNTY
FROM #CUSTOMERS_MND_1
GROUP BY UNIQUE_KEY) BB ON AA.UNIQUE_KEY = BB.UNIQUE_KEY
AND AA.COUNTY = BB.MAXCOUNTY;
DROP TABLE #CUSTOMERS_MND_1;
SELECT AA.*
INTO #CUSTOMERS_MND_3
FROM #CUSTOMERS_MND_2 AA
INNER JOIN (SELECT UNIQUE_KEY,
MAX(PARTNERS) AS MAXPARTNERS
FROM #CUSTOMERS_MND_2
GROUP BY UNIQUE_KEY) BB ON AA.UNIQUE_KEY = BB.UNIQUE_KEY
AND (AA.PARTNERS = BB.MAXPARTNERS
OR AA.PARTNERS IS NULL
AND BB.MAXPARTNERS IS NULL);
DROP TABLE #CUSTOMERS_MND_2;
SELECT AA.*
INTO #CUSTOMERS_MND_4
FROM #CUSTOMERS_MND_3 AA
INNER JOIN (SELECT UNIQUE_KEY,
MAX(PRODUCT) AS MAXPRODUCT
FROM #CUSTOMERS_MND_3
GROUP BY UNIQUE_KEY) BB ON AA.UNIQUE_KEY = BB.UNIQUE_KEY
AND (AA.PRODUCT = BB.MAXPRODUCT
OR AA.PRODUCT IS NULL
AND BB.MAXPRODUCT IS NULL);
DROP TABLE #CUSTOMERS_MND_3;
It works, but for more joins it is not time efficient.
my guess is you want the max city per unique key, within that city the max county, within that county the max partners and within that partner the max product in which case generating a row number and selecting row number 1 gives the same result as your code
create table #CUSTOMERS_MND_0
(unique_key int,city varchar(10),county varchar(10),partners varchar(10),product int);
go
insert into #CUSTOMERS_MND_0 values
(111,'vienna','a','w',7),(111,'naples','b','x',7),(111,'vienna','b','w',6),(111,'vienna','b','x',5)
go
with cte as
(select *, row_number() over (partition by unique_key order by city desc , county desc, partners desc,product desc) rn
from
#CUSTOMERS_MND_0
)
select * from cte where rn = 1;
your code plus
select * from #CUSTOMERS_MND_3
unique_key city county partners product
----------- ---------- ---------- ---------- -----------
111 vienna b x 5
my code
unique_key city county partners product rn
----------- ---------- ---------- ---------- ----------- --------------------
111 vienna b x 5 1
Now we have simplified code we can think about automation. You say you want to supply known columns as part of this process but unique key will always be required and source tables is known so--see https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=355f06fb7d51ba8fed9ea4e6d1e23d07 for an example of how using a temp table to hold the columns and the priority order in this case by identity. Then read up on dynamic sql..

Merging two views in PostgreSQL

I have two views in below format.
ProductId Version IsAvailable
123 1 Yes
124 1 No
125 1 Yes
126 1 No
ProductId Version IsShippable
123 1 Yes
124 1 Yes
125 1 No
127 1 Yes
I need to merge these two tables into a single table:
ProductId Version IsAvailable IsShippable
123 1 Yes Yes
124 1 No Yes
125 1 Yes No
126 1 No Null
127 1 Null Yes
How can I write the query to achieve this?
Use full outer join between 2 views like below:
select a.ProductId, a.Version, IsAvailable, IsShippable
from tableA a
full outer join tableB b on a.productid =b.productid and a.version=b.version

Query for 1 to many table

So I have a situation which can be referred to the table below:
TABLE A TABLE B TABLE C
ID TOTAL_PRICE ORDER_ID ID ID ORDER ID
1 10 101 101 1001 101
2 20 101 103 1002 101
3 25 103 1003 103
4 10 103 1004 103
With all these tables I'm expecting of this result:
EXPECTED OUTPUT
ID TOTAL_PRICE ORDER_ID ID
1 10 101 1001
2 20 101 1002
3 25 103 1003
4 10 103 1004
And the result that I get follows:
REAL OUTPUT
ID TOTAL_PRICE ORDER_ID ID
1 10 101 1001
1 10 101 1002
2 20 101 1001
2 20 101 1002
3 25 103 1003
3 25 103 1004
4 10 103 1003
4 10 103 1004
My sql as follows and I'm using SQL Oracle:
SELECT a.id, a.total_price, a.order.id, c.id
FROM a,b,c
WHERE a.order_id=b.id AND b.id=c.order_id
With that situation, how can I solve the problem in order to get the expected output? Thank you.
It seems that you want to assign one id from table c for each row from table a given your matching tables criteria. For this you could use row_number to enumerate your rows inside tables a and c and then assign this information with the row number respectively:
select
a.id, a.total_price, a.order_id, c.id
from (select *, row_number over (partition by order_id order by id) as rn from a) a
join b on a.order_id = b.id
join (select id, order_id, row_number over (partition by order_id order by id) as rn from c) on b.id = c.order_id
where a.rn = c.rn

Update multiple rows with different values in Oracle

I am trying to update multiple rows using an inner view in oracle.
The select statement for updating this view is:
select count(distinct a.numCount) as numCount, a.accNum as accNum ,
s.unitNum as unitNum
from tableA a,tableS s where a.accNum is not null and s.fk_id=
(select id from tableD where sid=a.accNum )
group by a.accNum ,s.unitNum ;
Update statement that I am trying is below:
update
(select count(distinct a.numCount) as numCount, a.accNum as accNum ,
s.unitNum as unitNum
from tableA a,tableS s where a.accNum is not null and s.fk_id=
(select id from tableD where sid=a.accNum )
group by a.accNum ,s.unitNum ) k
set k.unitNum=k.numCount;
I am trying to update unitNum with value of numCount.
The above query is not working when used as a view.
Is there another way to update this in Oracle.
Please suggest.
Structure of the tables are as below:
TableA
accNum numCount
-----------------------
111 1
222 5
333 2
111 1
111 1
222 5
222 2
TableS
fk_id unitNum
-----------------------
123 0
768 0
734 0
TableD
ID sid
-----------------------
123 222
768 111
734 333
Output should be as below:
TableS
fk_id unitNum
-----------------------
123 3
768 3
734 1
Please suggest
update tableS s
set unitNum=
(select count(distinct a.numCount) as numCount
from tableA a, tableD d
where s.fk_id=d.id and d.sid=a.accNum
);

SQL display data which is not mapped in another table

I am looking for a query to a result where I can see only userid1 data whose provider is not mapped in Table 2; Here is my table definition and data;
Table 1
userid providerid
1 101
1 104
1 106
1 107
2 102
2 103
2 104
Table 2
providerid
101
102
103
104
105
106
107
108
109
110
query required as per the o/p:-
userid providernotavailable
1 102
1 103
1 105
1 108
1 109
1 110
2 101
2 105
2 106
2 107
2 108
2 109
2 110
You can use the not in operator:
SELECT *
FROM table1
WHERE providerid NOT IN (SELECT providerid FROM table2)
Multiple versions: fist with NOT IN
SELECT table1.*
FROM table1
WHERE table1.providerId NOT IN ( SELECT table2.providerId FROM table2 )
Second with NOT EXISTS:
SELECT table1.*
FROM table1
WHERE NOT EXISTS ( SELECT 1
FROM table2
WHERE table1.providerId = table2.providerId )
This next one is a bit strange: we do a LEFT JOIN and we check that the second table hasn't matched:
SELECT table1.*
FROM table1
LEFT JOIN table2
ON table1.providerId = table2.providerId
WHERE table2.providerId IS NULL
Which of the three versions above performs better depends (mostly) on the cardinality of the two tables.
Sorry, I misunderstood the question. This should do the trick.
The first part of the query selects all the pairs userId - providerId. The second part removes all the ones present in table1.
SELECT U.userId, P.providerId
FROM (SELECT DISTINCT table1.userId FROM table1) U, table2 P
MINUS
SELECT table1.* FROM table1
EDIT
this is the table structure i have used to get the output
SQL> desc tab1
Name Null? Type
---------------------------- -------- ----------------
USERID NUMBER(38)
PROVIDERID NUMBER(38)
SQL> desc tab2
Name Null? Type
---------------------------- -------- ----------------
PROVIDERID NUMBER(38)
SQL> select distinct(a.userid),b.providerId from tab1 a, tab2 b
minus
select * from tab1 ;
USERID PROVIDERID
---------- ----------
1 102
1 103
1 105
1 108
1 109
1 110
2 101
2 105
2 106
2 107
2 108
2 109
2 110
13 rows selected.
SELECT a.userid, b.providerid FROM table1 a, table2 b
WHERE a.providerid IS NOT NULL
MINUS
SELECT userid, providerid FROM table1 a
WHERE a.providerid IS NOT NULL;
The query bring the desired output...
Thanks all for ur efforts.