Left Outer Join Including Missing Rows from 2nd Table - database

I am having some difficulty in doing a LEFT OUTER JOIN in SQLite3 and have not been able to find the resolution in StackOverflow's existing documents, but that may be just a reflection of ....
Basically, the issue is that the second table does not have some matching rows and I want the result set to contain NULL values for those rows.
The Table Structures:
(a) tblITEMS with columns:
ID (integer, primary key, not null)
DESCR (Text); i.e., description
(b) tblVALUES with columns:
ID (integer, primary key, not null)
ITEM_ID (integer, not null, foreign key to tblITEMS.ID)
VAL_DT (not null); i.e., the valuation date
VALUE (real, not null, default 0)
The Data:
(a) tblITEMS has five (5) rows.
__ID__|_DESCR_|
:----:|:-----:|
1 | Item1 |
2 | Item2 |
3 | Item3 |
4 | Item4 |
5 | Item5 |
(b) tblVALUES has four (4) rows with
(bi) two rows have the same ITEM_ID, but different VAL_DTs
(bii) two rows have ITEM_IDs different from (bi).
(c) There are zero (0) rows that match two (2) of the tblITEM IDs.
__ID__|_ITEM_ID_|__VAL_DT_____|__VALUE__|
:----:|:-------:|:-----------:|:-------:|
1 | 1 | 2000-01-01 | 10 |
2 | 2 | 2000-01-01 | 20 |
3 | 2 | 2010-01-01 | 200 |
4 | 3 | 2000-01-01 | 40 |
The desired result set:
Five (5) rows, one for each tblITEMS row, which contains NULL entries for the tblVALUES columns that do not have a matching ITEM_ID; i.e. do not exist.
SQL script that gives three (3) rows, each of which has the appropriate values from the tblVALUES table.
select a.ID, a.DESCR, b.ID as VAL_ID, b.ITEM_ID, Date(b.Val_dt) as Val_dt, b.VALUE
from
tblItems a
, tblValues b
--left outer join tblValues on a.id = b.item_id
where
a.id = b.item_id
and
b.val_dt = (Select Max(b.val_dt) from tblValues b where (a.id = b.ITEM_ID))
order by a.id
Above SQL script with the "left outer join" line uncommented yeilds a result set with:
(a) Twelve (12) rows.
(b) Four (4) duplicates of each of three (3) unique rows (same as returned by 4. above). The unique rows all have appropriate values from tblVALUES.
(c) Zero (0) rows corresponding to the tblITEMS that do not have a corresponding row in the tblVALUES table.
THE QUESTION:
How should the SQL script be modified so that the result set contains five (5) rows, one for each tblITEMS row AND contains NULL values for the tblITEM rows that do not have a corresponding row in the tblVALUES table?
Expected / desired result set:
__ID__|_DESCR_|_ITEM_ID_|__VAL_DT_____|__VALUE__|
:----:|:-----:|:-------:|:-----------:|:-------:|
1 | Item1 | 1 | 2000-01-01 | 10 |
2 | Item2 | 2 | 2010-01-01 | 200 |
3 | Item3 | 3 | 2000-01-01 | 40 |
4 | Item4 | NULL | NULL | NULL |
5 | Item5 | NULL | NULL | NULL |
Note: NULLs may be zero or a "null date".

I'm answering my own question because I've solved the problem.
I am somewhat embarrassed by how simple the solution was.
In any case, my fundamental problem was that I did not fully understand that the "JOIN" clauses are actually part of the main "FROM" clause, which means that the table on the right side of the join and its alias definition should only appear in the join clause.
The actual solution is:
Select a.ID
, a.DESCR
, b.ID AS VALUES_ID
, b.ITEM_ID
, b.VAL_DT
, b.VALUE
FROM tblITEMS a
LEFT OUTER JOIN tblVALUES b ON a.ID = b.ITEM_ID
AND b.val_dt = (Select Max(b.val_dt)
From tblVALUES b
Where a.ID = b.ITEM_ID)
Order by
a.ID
Regards,
John

Related

SQL (SSMS) join all records from one table and second table but exclude 'duplicates' from second

I'm having an issue where I went all records in Table B and any non matching records in Table A but it's bringing back the matching records in Table A. There is another left join to an additional table which is brought in for reference only.
I'm using SSMS v18.
So ID will be on Table A and Table B. There will be multiple records of this ID on A and B but I don't want the duplicate records if date/time and ID is the same in Table A and in Table B.
e.g. - I've simplified the query I'm using below.
Select
a.id
a.datetime
a.emp_id
c.team_id
From
table_a as a
Left Join
table_b as b On a.id = b.id
And a.datetime <> b.datetime
Left Join
table_c On a.emp_id = c.emp_id
As there isn't NULLs I don't think I can use that. I don't believe a full outer join will return what I need.
Is there a method is solve this? A union query solution will not work as Table A and Table B do not have the same columns/column names.
Please let me know if more information is required.
EDIT - Additional
Apologies but now there's been a change of requirement where I now need to remove the matching records rather than remove just the duplicates. Is there a way around this?
Additional - Data Examples
Table A:
+----+------------------+--------+
| Id | Datetime | emp_id |
+----+------------------+--------+
| 1 | 20/04/2021 10:30 | a |
| 1 | 20/04/2021 11:15 | a |
| 2 | 21/04/2021 12:10 | b |
| 2 | 21/04/2021 13:20 | b |
| 2 | 22/04/2021 15:30 | c |
| 3 | 23/04/2021 09:45 | d |
| 4 | 23/04/2021 14:35 | e |
+----+------------------+--------+
Table B:
+----+------------------+-------------+
| Id | Datetime | other_field |
+----+------------------+-------------+
| 1 | 20/04/2021 10:30 | x |
| 2 | 21/04/2021 13:20 | y |
| 4 | 23/04/2021 14:35 | z |
+----+------------------+-------------+
Desired Output:
+----+------------------+--------+---------+
| Id | Datetime | emp_id | team_id |
+----+------------------+--------+---------+
| 1 | 20/04/2021 11:15 | a | team_01 |
| 2 | 21/04/2021 12:10 | b | team_02 |
| 2 | 22/04/2021 15:30 | c | team_01 |
| 3 | 23/04/2021 09:45 | d | team_02 |
+----+------------------+--------+---------+
So the duplicate ID & Datetime in Table B does not show in final output (regardless of any other fields)
You seem to need a right join instead of a left join. A left join will bring back all rows in table A, and all rows in table B which match the condition which you provided. You seem to want all in table B, which requires a right join.
I know some developers who have an aversion to right joins, if you feel that way, you can simply switch the order of the tables in your query to have table B listed first, left join to table A. I feel that the first solution is the easier one, though you need to be comfortable with it.
Here are my solutions, listed in the order in which I mentioned above.
Select
a.id
,a.datetime
,a.emp_id
,c.team_id
From
table_a as a
RIGHT Join -- here is my change
table_b as b On a.id = b.id
And a.datetime <> b.datetime
Left Join
table_c On a.emp_id = c.emp_id;
/*solution II*/
Select
a.id
,a.datetime
,a.emp_id
,c.team_id
From
table_b as b
Left Join
table_a as a On a.id = b.id
And a.datetime <> b.datetime
Left Join
table_c On a.emp_id = c.emp_id;
/*Updated solution, based on the comments (requirements seem to have changed)*/
Select
a.id
,a.datetime
,a.emp_id
,c.team_id
From
table_b as b
Left Join
table_a as a On a.id = b.id
Left Join
table_c On a.emp_id = c.emp_id
WHERE (a.datetime <> b.datetime OR b.datetime IS NULL);
Explanation of the updated solution: there was nothing to take into account the rows which would not match, hence the OR in the join
Please see Microsoft documentation on joins below.
https://learn.microsoft.com/en-us/sql/relational-databases/performance/joins?view=sql-server-ver15#:~:text=Joins%20indicate%20how%20SQL%20Server,be%20used%20for%20the%20join.

Cross join, display table 2 column value base on table1

I have two tables in SQL Server, Say in table1 I have two columns Key1Display and Key2Display, they are of datatype bit and used to control whether to display the values in table2, and table 2 will have 2 columns Key1 and Key2.
What I am trying to achieve is a sort of cross join, say if table 1 has 3 rows:
| Key1Display | Key2Display |
+---------------------+------------------+
| 0 | 1 |
| 1 | 0 |
| 1 | 1 |
Say in table 2 there are 2 rows
| Key1 | Key2 |
+---------------------+------------------+
| Row1Key1value | Row1Key2value |
| Row2Key1value | Row2Key2value |
Then based on these two tables, I want to have a query to display 6 (2*3) rows and 1 column of results like this:
null:Row1Key2value
Row1Key1Value:null
Row1Key1Value:Row1Key2value
null:Row2Key2value
Row1Key2Value:null
Row1Key2Value:Row2Key2value
So something like:
select
case when t1.Key1Display = 1 then coalesce(t2.Key1,'??') else 'null' end
+ ':' + case when t1.Key2Display = 1 then coalesce(t2.Key2,'??') else 'null' end
-- And so on for as many keys as you have
from table1 t1
cross join table2 t2

How to represent aggregated null values as 0 in sqlserver [duplicate]

I feel like I was always taught to use LEFT JOINs and I often see them mixed with INNERs to accomplish the same type of query throughout several pieces of code that are supposed to do the same thing on different pages. Here goes:
SELECT ac.reac, pt.pt_name, soc.soc_name, pt.pt_soc_code
FROM
AECounts ac
INNER JOIN 1_low_level_term llt on ac.reac = llt.llt_name
LEFT JOIN 1_pref_term pt ON llt.pt_code = pt.pt_code
LEFT JOIN 1_soc_term soc ON pt.pt_soc_code = soc.soc_code
LIMIT 100,10000
Thats one I am working on:
I see a lot like:
SELECT COUNT(DISTINCT p.`case`) as count
FROM FDA_CaseReports cr
INNER JOIN ae_indi i ON i.isr = cr.isr
LEFT JOIN ae_case_profile p ON cr.isr = p.isr
This seems like the LEFT may as well be INNER is there any catch?
Is there any catch? Yes there is -- left joins are a form of outer join, while inner joins are a form of, well, inner join.
Here's examples that show the difference. We'll start with the base data:
mysql> select * from j1;
+----+------------+
| id | thing |
+----+------------+
| 1 | hi |
| 2 | hello |
| 3 | guten tag |
| 4 | ciao |
| 5 | buongiorno |
+----+------------+
mysql> select * from j2;
+----+-----------+
| id | thing |
+----+-----------+
| 1 | bye |
| 3 | tschau |
| 4 | au revoir |
| 6 | so long |
| 7 | tschuessi |
+----+-----------+
And here we'll see the difference between an inner join and a left join:
mysql> select * from j1 inner join j2 on j1.id = j2.id;
+----+-----------+----+-----------+
| id | thing | id | thing |
+----+-----------+----+-----------+
| 1 | hi | 1 | bye |
| 3 | guten tag | 3 | tschau |
| 4 | ciao | 4 | au revoir |
+----+-----------+----+-----------+
Hmm, 3 rows.
mysql> select * from j1 left join j2 on j1.id = j2.id;
+----+------------+------+-----------+
| id | thing | id | thing |
+----+------------+------+-----------+
| 1 | hi | 1 | bye |
| 2 | hello | NULL | NULL |
| 3 | guten tag | 3 | tschau |
| 4 | ciao | 4 | au revoir |
| 5 | buongiorno | NULL | NULL |
+----+------------+------+-----------+
Wow, 5 rows! What happened?
Outer joins such as left join preserve rows that don't match -- so rows with id 2 and 5 are preserved by the left join query. The remaining columns are filled in with NULL.
In other words, left and inner joins are not interchangeable.
Here's a rough answer, that is sort of how I think about joins. Hoping this will be more helpful than a very precise answer due to the aforementioned math issues... ;-)
Inner joins narrow down the set of rows returns. Outer joins (left or right) don't change number of rows returned, but just "pick up" additional columns if possible.
In your first example, the result will be rows from AECounts that match the conditions specified to the 1_low_level_term table. Then for those rows, it tries to join to 1_pref_term and 1_soc_term. But if there's no match, the rows remain and the joined in columns are null.
An INNER JOIN will only return the rows where there are matching values in both tables, whereas a LEFT JOIN will return ALL the rows from the LEFT table even if there is no matching row in the RIGHT table
A quick example
TableA
ID Value
1 TableA.Value1
2 TableA.Value2
3 TableA.Value3
TableB
ID Value
2 TableB.ValueB
3 TableB.ValueC
An INNER JOIN produces:
SELECT a.ID,a.Value,b.ID,b.Value
FROM TableA a INNER JOIN TableB b ON b.ID = a.ID
a.ID a.Value b.ID b.Value
2 TableA.Value2 2 TableB.ValueB
3 TableA.Value3 3 TableB.ValueC
A LEFT JOIN produces:
SELECT a.ID,a.Value,b.ID,b.Value
FROM TableA a LEFT JOIN TableB b ON b.ID = a.ID
a.ID a.Value b.ID b.Value
1 TableA.Value1 NULL NULL
2 TableA.Value2 2 TableB.ValueB
3 TableA.Value3 3 TableB.ValueC
As you can see, the LEFT JOIN includes the row from TableA where ID = 1 even though there's no matching row in TableB where ID = 1, whereas the INNER JOIN excludes the row specifically because there's no matching row in TableB
HTH
Use an inner join when you want only the results that appear in both tables that matches the Join condition.
Use a left join when you want all the results from Table A, but if Table B has data relevant to some of Table A's records, then you also want to use that data in the same query.
Use a full join when you want all the results from both Tables.
For newbies, because it helped me when I was one: an INNER JOIN is always a subset of a LEFT or RIGHT JOIN, and all of these are always subsets of a FULL JOIN. It helped me understand the basic idea.

Setting 0 when NO rows from select statement with multiple tables

Let's say I have this table
(Simplified, my query have more tables involved)
ConsignmentItem
ID |Item code| Name | Quantity
1 | 00000 | A | 3
2 | 11111 | B | 2
And this other table
PickingItem
ID |ConsignmentID|Quantity
1 | 1 | 1
What my query does is to join both tables and print the amount of products ordered and the amount of products already registered. I would like to get as result the following table
Item Code| Name | Quantity_Ordered | Quantity_Registered
00000 | A | 3 | 1
11111 | B | 2 | 0
My query works whenever the item exist on "PickingItem", if it doesn't it prints the same "Quantity_Registered" as the above row, using my query I get as result the following table
Item Code| Name | Quantity_Ordered | Quantity_Registered
00000 | A | 3 | 1
11111 | B | 2 | 1(this is wrong)
This is the query i'm using
SELECT C.Barcode AS 'Item Code',C.ProductName AS 'Name', C.Quantity AS 'Quantity_Ordered', ISNULL(P.Quantity,0) AS 'Quantity_Registered'
FROM PICKING.OrderPickingItem P
JOIN PICKING.OrderPicking OP ON P.PickingID = OP.PickingID
JOIN ORDERS.ConsignmentItem C ON OP.ConsignmentID = C.ConsignmentID
WHERE P.PickingID = 1 --For testing
Anyone know what could I do to, if the product doesn't exist on OrderPickingItem, then set P.Quantity = 0 for that specific row?
EDIT:
Structure of the tables
OrderPickingItem
PickingItemID PK
PickingID FK
ConsignmentItemID FK
Quantity
--other not used columns for this query
OrderPicking
PickingID PK
ConsignmentID FK
--other not used columns for this query
ConsignmentItem
ConsignmentItemID PK
ConsignmentID FK
Barcode
Quantity
ProductName
--other not used columns for this query
You are obviously looking for an outer join:, you want to show ConsignmentItem records even when there is no matching picking.
select
C.Barcode AS "Item Code",
C.ProductName AS "Name",
C.Quantity AS "Quantity_Ordered",
ISNULL(P.Quantity, 0) AS "Quantity_Registered"
from ORDERS.ConsignmentItem c
left join PICKING.OrderPicking op on OP.ConsignmentID = C.ConsignmentID
left join PICKING.OrderPickingItem P on P.PickingID = OP.PickingID
and P.ConsignmentItemID = C.ConsignmentItemID;
Do an outer join and something like ifnull(p.id, 0) for the quantity in the select clause.

Postgres: Query that can filter during table join

I have a postgres database with duplicated entries on one of the table. I would like to show the created_by columns
Table1
id | number
1 | 123
2 | 124
3 | 125
4 | 126
Table2
id | number | created_on
1 | 123 | 3/29
2 | 123 | 4/3
3 | 124 | 3/31
4 | 124 | 4/1
On table 2 number are duplicated. I would like to form a single query to list the following:
id | number | created_on
1 | 123 | 4/3
2 | 124 | 4/1
For duplicated entries only the latest entry will be included. How could I form that SQL query?
SELECT DISTINCT ON (Table1.number) Table1.id, Table2.number, Table2.create_on FROM Table1
JOIN Table2 ON Table1.number=Table2.number
ORDER BY Table2.create_on;
Actually I tried putting 'DISTINCT ON' and 'ORDER BY' in a single query (with JOIN) it gives me the following error:
SELECT DISTINCT ON expressions must match initial ORDER BY expressions
The columns in DISTINCT ON() have to be the first ones in the ORDER BY query, also if you want the latest created_on date you should order by created_on DESC
SELECT DISTINCT ON (Table1.number) Table1.id, Table2.number, Table2.created_on
FROM Table1
JOIN Table2
ON Table1.number=Table2.number
ORDER BY Table1.number,Table2.created_on DESC;
http://sqlfiddle.com/#!12/5538a/2
As you said in the comment: created_on=date_trunc('day', now()), so the data type of the field created_on is timestamp. Here is what you can do:
SELECT table_1.id, table_1.number, max(created_on) as created_on
FROM table_1
inner join table_2 using(number)
group by table_1.id, table_1.number

Resources