SQL SELECT and check column from second table - sql

I have two tables in my database. From the Table B i collect some data. I include by using JOIN the data from Table A. In this table there is a column user_id. The user id in table B is the the id from Table A. No i want to get all data from table A but without showing data if the table A id is in the table B column user_id available. I think the trick is the correct usage of JOIN but i need to check the whole column and not just one line.
SQL Query (which is probably not working)
SELECT * FROM tableB
JOIN tableA
ON tableB.user_id = tableA.id
WHERE tableB.user_id != tableA.id
Please see my example i have prepared in Excel:

To select all the data from tableA where id is not available in tableB you don't need join rather you can use not exists or not in.
I would prefer not exists
Using not exists:
SELECT * from tableA a
WHERE NOT EXISTS (
SELECT 1
FROM tableB b
WHERE b.user_id = a.id
);
using not in:
SELECT * from tableA a
WHERE id not in (SELECT user_id FROM tableB );
DB-Fiddle:
create table TableA (id int, name varchar(50), lastname varchar(50));
insert into TableA values(1,'john','smith');
insert into TableA values(2,'Paul','smith');
create table TableB (id int, user_id int, something varchar(50));
insert into TableB values(1,2,'bla');
insert into TableB values(2,3,'bla');
Query: (using not exists)
SELECT * from tableA a
WHERE NOT EXISTS (
SELECT 1
FROM tableB b
WHERE b.user_id = a.id
);
Output:
id
name
lastname
1
john
smith
Query: (using not in)
SELECT * from tableA
WHERE id not in (SELECT user_id FROM tableB );
Output:
id
name
lastname
1
john
smith
db<fiddle here

I think I get what you want. You want not exists:
SELECT *
FROM tableB b JOIN
tableA a
ON b.user_id = a.id
WHERE NOT EXISTS (SELECT 1
FROM tableA a2
WHERE b.user_id = a2.id
);
EDIT:
I wrote the above, but I didn't fully follow the JOIN conditions. You either seem to want to join on id:
SELECT *
FROM tableB b JOIN
tableA a
ON b.id = a.id
WHERE NOT EXISTS (SELECT 1
FROM tableA a2
WHERE b.user_id = a2.id
);
Or no JOIN at all:
SELECT *
FROM tableB b
WHERE NOT EXISTS (SELECT 1
FROM tableA a2
WHERE b.user_id = a2.id
);

Related

Converts a query which uses exists/not exists to a three table join

I am working on a problem statement where I need to retain records based on a logic which is being applied on three tables.The tables are listed as below.
Table_A:
id
phone_number
account_name
123
80001
1001
Table_B
id
phone_number
account_name
124
80002
1002
Table_C
id
phone_number
account_name
125
80003
1003
I wrote a query as shown below:
select /*+PARALLEL(su,8)*/
su.ID
from TableA su
where ( EXISTS (select /*+ PARALLEL(sa,8) PARALLEL(su,8)*/
PHONE_NUMBER
from TableB sa
where PHONE_NUMBER=su.PHONE_NUMBER
)
or EXISTS (select /*+ PARALLEL(sa,8) PARALLEL(su,8)*/
PHONE_NUMBER
from TableC sa
where PHONE_NUMBER=su.PHONE_NUMBER
)
)
and ( EXISTS (select /*+ PARALLEL(sa,8) PARALLEL(su,8)*/
ACCOUNT_NAME
from TableB sa
where ACCOUNT_NAME=su.ACCOUNT_NAME
)
or EXISTS (select /*+ PARALLEL(sa,8) PARALLEL(su,8)*/
ACCOUNT_NAME
from TableC sa
where ACCOUNT_NAME=su.ACCOUNT_NAME
)
)
and NOT EXISTS (select /*+ PARALLEL(sa,8) PARALLEL(su,8)*/
ID
from TableC sa
where ID=su.ID
)
and NOT EXISTS (select /*+ PARALLEL(sa,8) PARALLEL(su,8)*/
ID
from TableB sa
where ID=su.ID
);
My requirement here is that for a record of table A if the phone_number and account_name are either in TableB or TableC and the ID is not present in any of these two tables then the record should be present.The existing code works in most of the scenarios but there is a specific scenario where it is not working where for a specific record of table A the acccount_name and phone_number is present for two different records in either of TableB or TableC.I want to exclude these kind of records from my output hence I tried converting my query to a three way join so that the join in on specific records.
The query which I wrote is as below-
select su.id
from TableA su,
TableB sa,
TableC sb
where ( su.PHONE_NUMBER=sa.PHONE_NUMBER
OR su.PHONE_NUMBER=sb.PHONE_NUMBER
)
and (su.ACCOUNT_NAME=sa.ACCOUNT_NAME
OR su.ACCOUNT_NAME=sb.ACCOUNT_NAME
)
and NOT EXISTS (select /*+ PARALLEL(sa,8) PARALLEL(su,8)*/
id
from TableC sa
where id=su.id
)
and NOT EXISTS (select /*+ PARALLEL(sa,8) PARALLEL(su,8)*/
id
from TableB sa
where id=su.id
)
This query is returning a number of duplicates and I tried using distinct as well but I am still getting incorrect results.
Could anyone suggest what is wrong on my query? Please advice.
Thank you!
I am still not sure if I understand what you require. Here is a query that finds TableA rows where there exists a row in TableB or TableC with the same ACCOUNT_NAME / PHONE_NUMBER pair but a different ID.
select *
from tablea a
where exists
(
select null
from tableb b
where b.phone_number = a.phone_number
and b.account_name = a.account_name
and b.id <> a.id
)
or exists
(
select null
from tablec c
where c.phone_number = a.phone_number
and c.account_name = a.account_name
and c.id <> a.id
)
order by id;
If you want to see the other IDs, you must join instead. I suggest a union of TableB and TableC for this:
select a.*, bc.table_name, bc.id as other_table_id
from tablea a
join
(
select 'TableB' as table_name, id, phone_number, account_name from tableb
union all
select 'TableC' as table_name, id, phone_number, account_name from tablec
) bc on bc.phone_number = a.phone_number
and bc.account_name = a.account_name
and bc.id <> a.id
order by a.id, bc.table_name, bc.id;

Full Outer Join failing to return all records from both tables

I have a pair of tables I need to join, I want to return any record that's in tableA, tableB or both. I think I need a FULL OUTER JOIN
This query return 1164 records
SELECT name FROM tableA
WHERE reportDay = '2022-Apr-05'
And this one return 3339 records
SELECT name FROM tableB
WHERE reportDay = '2022-Apr-05'
And this one returns 3369 records (so there must be 30 records in tableA that aren't in tableB)
select distinct name FROM tableA where reportDay = '2022-Apr-05'
union distinct
select distinct name FROM tableB where reportDay = '2022-Apr-05'
I want to obtain a list of all matching records in either table. The query above returns 3369 records, so a FULL OUTER JOIN should also return 3369 rows (I think). My best effort so far is shown below. It returns 1164 rows and returns what looks to me to be a left join between tableA and tableB.
SELECT tableA.name.*, tableB.name.*
FROM tableA
FULL OUTER JOIN tableB
ON (tableA.name = tableB.name and tableB.reportDay = '2022-Apr-05')
WHERE tableA.reportDay = '2022-Apr-05'
Help appreciated. (if this looks question looks familiar, it's a follow-on question to this one )
UPDATE - Sorry (#forpas) to keep moving the goalposts - I'm trying to match test data to real-data scenario's.
DROP TABLE tableA;
DROP TABLE tableB;
CREATE TABLE tableA (name VARCHAR(10),
reportDay DATE,
val1 INTEGER,
val2 INTEGER);
CREATE TABLE tableB (name VARCHAR(10),
reportDay DATE,
test1 INTEGER,
test2 INTEGER);
INSERT INTO tableA values ('A','2022-Apr-05',1,2),
('B','2022-Apr-05',3,4), ('C','2022-Apr-05',5,6),
('A','2022-Apr-06',1,2), ('B','2022-Apr-06',3,4),
('C','2022-Apr-06',5,6), ('Z','2022-Apr-04',5,6),
('Z','2022-Apr-06',5,6) ;
INSERT INTO tableB values ('A','2022-Apr-03',5,6),
('B','2022-Apr-04',11,22), ('B','2022-Apr-05',11,22),
('C','2022-Apr-05',33,44), ('D','2022-Apr-05',55,66),
('B','2022-Apr-06',11,22), ('C','2022-Apr-06',33,44),
('D','2022-Apr-06',55,66), ('Q','2022-Apr-06',5,6);
SELECT tableA.*, tableB.*
FROM tableA
FULL OUTER JOIN tableB
ON (tableA.name = tableB.name and tableB.reportDay = '2022-Apr-05'
AND tableA.reportDay = '2022-Apr-05' )
For this data, I'd hope to see 4 rows of data 'A' from tableA only, 'B' and 'C' from both tables, and 'D' from table B only. I'm after the 5th April records only! The query (shown above) suggested by #forpas works except that the 'A' record in tableA doesn't get returned.
UPDATE - FINAL EDIT AND ANSWER!
Ok, the solution seem to be to concetenate the two fields together before joining....
SELECT a.*, b.*
FROM tableA a FULL OUTER JOIN tableB b
ON (b.name || b.reportDay) = (a.name || a.reportDay)
WHERE (a.reportDay = '2022-Apr-05' OR a.reportDay IS NULL)
AND (b.reportDay = '2022-Apr-05' OR b.reportDay IS NULL);
The condition for the date should be placed in a WHERE clause:
SELECT a.*, b.*
FROM tableA a FULL OUTER JOIN tableB b
ON b.name = a.name AND a.reportDay = b.reportDay
WHERE '2022-Apr-05' IN (a.reportDay, b.reportDay);
or:
SELECT a.*, b.*
FROM tableA a FULL OUTER JOIN tableB b
ON b.name = a.name
WHERE (a.reportDay = '2022-Apr-05' OR a.reportDay IS NULL)
AND (b.reportDay = '2022-Apr-05' OR b.reportDay IS NULL);
See the demo.

Optional on condition in SQL Server

I want to update a table based on another table.
I want to update table b using table A. One Id can have multiple serial number in table B. If I want to update all the serial number for a ID then I will pass null in table A, If I want to update only particular serial number then I will pass that serial number. So Serial number is like optional. How to achieve this?
This covers all cases:
SELECT B.ID, B.SerialNumber
FROM TableA A
JOIN TableB B ON A.ID=B.ID AND (A.SerialNumber=B.SerialNumber OR A.SerialNumber IS NULL)
Demo:
WITH TableA AS
(
SELECT * FROM (VALUES
(1,'AB'),
(1,'BC'),
(2,NULL),
(3,'AB')
)T(ID,SerialNumber)
), TableB AS
(
SELECT * FROM (VALUES
(1,'AB'),
(1,'BC'),
(2,'AB'),
(2,'BC'),
(3,'AB'),
(3,'BC'),
(3,'DE')
)T(ID,SerialNumber)
)
SELECT B.ID, B.SerialNumber
FROM TableA A
JOIN TableB B ON A.ID=B.ID AND (A.SerialNumber=B.SerialNumber OR A.SerialNumber IS NULL)
Result
ID SerialNumber
----------- ------------
1 AB
1 BC
2 AB
2 BC
3 AB
You can join both tables :
SELECT [A].[ID]
,[B].[SerialNumber]
,[A].[Values]
FROM [TableA] A
LEFT JOIN [TableB] B ON ([B].[ID] = [A].[ID] AND ([B].[SerialNumber] = [A].[SerialNumber] OR [A].[SerialNumber] IS NULL))

Sorting data from one table based on another table

Here are my tables:
create table tableA (id int, type varchar(5))
insert into tableA (ID,TYPE)
values
(101,'A'),
(101,'C'),
(101,'D'),
(102,'A'),
(102,'B'),
(103,'A'),
(103,'C')
create table tableB (id int, type varchar(5), isActive bit)
insert into tableB (id, type, isActive)
Values
(101,'A',1),
(101,'B',0),
(101,'C',0),
(101,'D',1),
(102,'A',1),
(102,'B',0),
(102,'C',1),
(102,'D',1),
(103,'A',1),
(103,'B',1),
(103,'C',1),
(103,'D',1)
Now, I want to do two things here:
1) Find the rows that are present in tableA but isActive = 0 in tableB. (done)
select A.* from tableA A
join tableB B
on A.id = B.id and A.type = B.type
where B.isactive = 0
2) Find the rows that are missing in tableA but isActive = 1 in tableB.
For example ID 103, type B is active in tableB but is missing in tableA.
I don't care about existance of type D in tableA because I am only checking the last entry in tableA which is C.
Also, ABCD is in order for all IDs, they can be active or inactive.
Thanks for your help!
My effort: (not working)
select A.* from tableA A
where exists (
select B.* from tableA A
join tableB B
on a.id = b.id
where b.isActive = 1
order by b.id,b.type
)
SQLFiddle
I think you are looking for something like the following:
select B.* from tableB B
left join tableA A
on B.id = A.id and B.type = A.type
where B.isActive = 1
and A.id is null
order by B.id, B.type
By using the left join, it means that rows in tableB that have no rows to join with in tableA will have all A.* columns null. This then allows you to add the where clause to check where the tableA records are null thus determining what is contained within tableB that is active and not in tableA

select a value where it doesn't exist in another table

I have two tables
Table A:
ID
1
2
3
4
Table B:
ID
1
2
3
I have two requests:
I want to select all rows in table A that table B doesn't have, which in this case is row 4.
I want to delete all rows that table B doesn't have.
I am using SQL Server 2000.
You could use NOT IN:
SELECT A.* FROM A WHERE ID NOT IN(SELECT ID FROM B)
However, meanwhile i prefer NOT EXISTS:
SELECT A.* FROM A WHERE NOT EXISTS(SELECT 1 FROM B WHERE B.ID=A.ID)
There are other options as well, this article explains all advantages and disadvantages very well:
Should I use NOT IN, OUTER APPLY, LEFT OUTER JOIN, EXCEPT, or NOT EXISTS?
For your first question there are at least three common methods to choose from:
NOT EXISTS
NOT IN
LEFT JOIN
The SQL looks like this:
SELECT * FROM TableA WHERE NOT EXISTS (
SELECT NULL
FROM TableB
WHERE TableB.ID = TableA.ID
)
SELECT * FROM TableA WHERE ID NOT IN (
SELECT ID FROM TableB
)
SELECT TableA.* FROM TableA
LEFT JOIN TableB
ON TableA.ID = TableB.ID
WHERE TableB.ID IS NULL
Depending on which database you are using, the performance of each can vary. For SQL Server (not nullable columns):
NOT EXISTS and NOT IN predicates are the best way to search for missing values, as long as both columns in question are NOT NULL.
select ID from A where ID not in (select ID from B);
or
select ID from A except select ID from B;
Your second question:
delete from A where ID not in (select ID from B);
SELECT ID
FROM A
WHERE NOT EXISTS( SELECT 1
FROM B
WHERE B.ID = A.ID
)
This would select 4 in your case
SELECT ID FROM TableA WHERE ID NOT IN (SELECT ID FROM TableB)
This would delete them
DELETE FROM TableA WHERE ID NOT IN (SELECT ID FROM TableB)
SELECT ID
FROM A
WHERE ID NOT IN (
SELECT ID
FROM B);
SELECT ID
FROM A a
WHERE NOT EXISTS (
SELECT 1
FROM B b
WHERE b.ID = a.ID)
SELECT a.ID
FROM A a
LEFT OUTER JOIN B b
ON a.ID = b.ID
WHERE b.ID IS NULL
DELETE
FROM A
WHERE ID NOT IN (
SELECT ID
FROM B)