sql left join clarification - sql

I have two tables.
I need all records from table A except where there is a match in table B, then take table B's matching value
So if table A has a match with table B, then I need to exclude that value from the results (i.e take all records from table A and replace records from table A with the matching value from table B).
from my understanding I left joined the two tables on the matching column and then added a where clause to exclude the null values but I'm not sure if that is correct.
data:
tableA (col1 =ID, col2 =value)
1, 5
2, 3
3, 7
tableB (col1 =ID, col2 =value)
4, 6
2, 9
expected result:
5
9
7
This is the closest I've gotten but I'm not sure if it's correct:
select * from tableA tblA
left join tableB tblB
on tblA.matchingColumn = tblB.matchingColumn
where tblB.matchingColumn is null

This will return all the rows in TABLEA. If there are any matching value in TABLEB then it will be used as VALUE.
SELECT TBLA.ID
, COALESCE(TBLB.VALUE, TBLA.VALUE) VALUE
FROM TABLEA TBLA
LEFT JOIN TABLEB TBLB
ON TBLA.ID = TBLB.ID;
UPDATE - SQLFiddle for this thread

Perform a left join from tablea to tableb and use the value from a or b depending on match in tableb
select tablea.id, case when tableb.id is null then tablea.value else tableb.value end value
from tablea
left join tableb on tablea.id=tableb.id

Related

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.

SQL - not sure how to join tables

I'm trying to join two tables like this:
Table A
ID Value1
1 A
2 B
3 C
Table B
ID Value2
1 A
3 B
4 C
Result should be:
ID Value1 Value2
1 A A
2 B null
3 C B
4 null C
I.e. join Table A to Table B on ID. If ID doesn't exist in Table A, add the ID from Table B.
The closest I've come is:
SELECT
a.ID, a.Value1, b.Value2
FROM
TableA a
OUTER JOIN
TableB b ON a.ID = b.ID
That gives me the new rows from TableB, but the ID is null.
How can I accomplish this?
You are very close, you just need a little push in the right direction:
SELECT COALESCE(a.ID, B.ID) As ID, a.Value1, b.Value2
FROM TableA a
FULL OUTER JOIN TableB b ON a.ID=b.ID
The COALESCE function returns the first parameter it gets that is not null. since this is a full outer join, a.id will be null on one row and b.id would be null on a different row.
Try this:
SELECT *
FROM TableA A
FULL OUTER JOIN TableB B
ON A.ID = B.ID;
Just a note: you should not name your tables in SQL with spaces in them.
Remember the basic for joining different tables
SELECT column_name(s)
FROM table1
FULL OUTER JOIN table2
ON table1.column_name=table2.column_name;
For your case:
SELECT a.value1, b.value2
FROM TableA a
FULL OUTER JOIN TableB b ON a.ID=b.ID
remember full outer join
The FULL OUTER JOIN keyword returns all rows from the table (tableA) and from the table (tableB) and the FULL OUTER JOIN keyword combines the result of both LEFT and RIGHT joins.

Excel SQL - Compare Values

My first post here and looking for some help on how to compare values in 2 tables and bring back a list of the differences
This is to be done in excel via VBA, I tried a few different ways like Full join and Union but can't get it to run.
Basic Scenario is this:
Table A has 2 columns,
Column 1 = ID
Column 2 = Qty
Exact same set up in Table B
What i want to do is gather a list where the Qty is different or doesn't appear in either table
e.g.
Table A has
ID 1
Qty 3
Table B has
ID 1
Qty 2
So it would bring back one of the records
Or
Table A has ID 5 and Table B doesn't
Table B has ID 7 and Table A doesn't
Not sure on the best method to do this, i know i can do it in excel via a formula but trying to accomplish in SQL if possible
Thanks
From a SQL perspective you will be looking at two different statements:
ID and Qty combination are in A, but not B
ID and Qty combination are in B, but not A
This will look something like:
SELECT tableA.id, tableA.qty FROM tableA LEFT JOIN tableB on tableA.id = tableB.id and tableA.qty = tableB.qty WHERE tableB.qty IS NULL
SELECT tableB.id, tableB.qty FROM tableB LEFT JOIN tableA on tableBid = tableA.id and tableB.qty = tableA.qty WHERE tableA.qty IS NULL
You can union these two together to get a single statement of mismatched records like so:
SELECT tableA.id, tableA.qty FROM tableA LEFT JOIN tableB on tableA.id = tableB.id and tableA.qty = tableB.qty WHERE tableB.qty IS NULL
UNION ALL
SELECT tableB.id, tableB.qty FROM tableB LEFT JOIN tableA on tableBid = tableA.id and tableB.qty = tableA.qty WHERE tableA.qty IS NULL
And you can add a reason to each individual SQL statement so you know why the mismatch happened:
SELECT tableA.id,
tableA.qty,
"Item in Table A, but not Table B" as Reason
FROM tableA
LEFT JOIN tableB
ON tableA.id = tableB.id
AND tableA.qty = tableB.qty
WHERE tableB.qty IS NULL
UNION ALL
SELECT tableB.id,
tableB.qty,
"Item in Table B, but not Table A" as Reason
FROM tableB
LEFT JOIN tableA
ON tableBid = tableA.id
AND tableB.qty = tableA.qty
WHERE tableA.qty IS NULL

Is there a way to do a multi table query and get result just from specific tables?

I am trying to do a multi query but I don't want to use sub queries i.e:
SELECT column1
FROM table1
WHERE
EXISTS (SELECT column1 FROM table2 WHERE table1.column1 = table2.column1);)
I thought of using a JOIN but so far my best result was this:
SELECT *
FROM table1
JOIN table2 ON table1.t1id = table2.t2id
WHERE table1.id = 5;
This would be good except of the fact that I get a duplicate column (the id in table 1 and 2 are foreign keys).
How do I remove the duplicate column if possible?
UPDATE:
Table1:
tableA_ID, TABLEB_ID
1, 1
1, 4
3, 2
4, 3
TableA: ID, COL1, COL2
1, A, B
2, A, B
3, A, B
4, A, B
TableB: ID, Col3, COL4
1, C, D
2, C, D
3, C, D
4, C, D
I want to get all or some of the columns from TableA according to a condition
Sample: Lets say the condition is that tableA_ID = 1 which will result in the 2 first rows in the table then I want to get all or some of the columns in TableA that respond to the ID that I got from Table1.
Sample: The result from before was [{1,1}{1,4}] which means I want from TableA the results:
TableA.ID, TableA.COL1, TableA.COL2
1,A,B
4,A,B
The actual results I get is:
Table1.tableA_ID, Table1.TABLEB_ID, TableA.ID, TableA.COL1, TableA.COL2
1,1,1,A,B
1,4,4,A,B
Is this what you're looking for?
select a.id, a.column1, b.column2
from table1 a
left join table2 b on a.id = b.otherid;
You can't change the column list of a query based on the values it returns. It just isn't the way that SQL is designed to operate. At best, you can return all of the columns from the second table and ignore the ones that aren't relevant based on other values in that row.
I'm not even sure how a variable column list would work. In your scenario, you're looking for two discrete values separately. But that's not the only scenario: what if the condition is tableA_ID in (1,2). Would you want different numbers of columns in different rows as part of a single result set?
Getting just the columns you want (just from specific tables, as you say) is the easy part (btw -- don't use '*' if you can help it -- topic for another discussion):
SELECT
A.ID,
A.COL1,
A.COL2
FROM
TABLE1 Bridge
LEFT JOIN TABLEA A
ON Bridge.TABLEA_ID = A.ID
LEFT JOIN TABLEB B
ON Bridge.TABLEB_ID = B.ID
Getting the rows you want will be the harder part (influenced by your choice of joins, among several other things).
I think you'll need to select only the fields of table A and use a distinct clause. Rest of your query will remain as it is. i.e.
SELECT distinct table1.*
FROM table1
JOIN table2 ON table1.t1id = table2.t2id
WHERE table1.id = 5;

Return single row when 2 records in right table

I have two related sql server tables ... TableA and TableB.
***TableA - Columns***
TableA_ID INT
VALUE VARCHAR(100)
***TableB - Columns***
TableB_ID INT
TableA_ID INT
VALUE VARCHAR(100)
For every single record in TableA there are always 2 records in TableB. Therefore TableA has a one-to-many relationship with TableB.
How could I write a single sql statement to join these tables and return a single row for each row in TableA that includes:
a column for the VALUE column in the first related row in table B
a column for the VALUE column in the second related row in table B?
For exactly 2 related records, that's easy. Join table B twice:
SELECT
A.TableA_ID,
A.VALUE AS VALUE_A
B1.VALUE AS VALUE_B1
B2.VALUE AS VALUE_B2
FROM
TableA AS A
INNER JOIN TableB B1 ON B1.TableA_ID = A.TableA_ID
INNER JOIN TableB B2 ON B2.TableA_ID = A.TableA_ID
WHERE
B1.TableB_ID < B2.TableB_ID
If you have a column in table B that determines what is "first value" and what is "second value", this gets even easier (and it would work like this for N columns in table B, just add more joins):
SELECT
A.TableA_ID,
A.VALUE AS VALUE_A
B1.VALUE AS VALUE_B1
B2.VALUE AS VALUE_B2
FROM
TableA AS A
INNER JOIN TableB B1 ON B1.TableA_ID = A.TableA_ID AND B1.Type = '1'
INNER JOIN TableB B2 ON B2.TableA_ID = A.TableA_ID AND B2.Type = '2'
A composite index on TableB over (TableA_ID, Type) helps this join.