SQL: Proper JOIN Protocol - sql

I have the following tables with the following attributes:
Op(OpNo, OpName, Date)
OpConvert(OpNo, M_OpNo, Source_ID, Date)
Source(Source_ID, Source_Name, Date)
Fleet(OpNo, S_No, Date)
I have the current multiple JOIN query which gives me the results that I want:
SELECT O.OpNo AS Op_NO, O.OpName, O.Date AS Date_Entered, C.*
FROM Op O
LEFT OUTER JOIN OpConvert C
ON O.OpNo = C.OpNo
LEFT OUTER JOIN Source D
ON C.Source_ID = D.Source_ID
WHERE C.OpNo IS NOT NULL
The problem is this. I need to join the Fleet table on the previous multiple JOIN statement to attach the relevant S_No to the multiple JOIN table. Would I still be able to accomplish this using a LEFT OUTER JOIN or would I have to use a different JOIN statement? Also, which table would I JOIN on?
Please note that I am only familiar with LEFT OUTER JOINS.
Thanks.

I guess in your case you could use INNER JOIN or LEFT JOIN (which is the same thing as LEFT OUTER JOIN in SQL Server.
INNER JOIN means that it will only return records from other tables only if there are corresponding records (based on the join condition) in the Fleet table.
LEFT JOIN means that it will return records from other tables even if there are no corresponding records (based on the join condition) in the Fleet table. All columns from Fleet will return NULL in this case.
As for which table to join, you should really join the table that makes more logical sense based on your data structure.

Yes, you can use all tables mentioned before in your join conditions. Actually, JOINS (no matter of INNER, LEFT OUTER, RIGHT OUTER, CROSS, FULL OUTER or whatever) are left- associative, i. e. they are implicitly evaluated as if they would have been included in parentheses from the left as follows:
FROM ( ( ( Op O
LEFT OUTER JOIN OpConvert C
ON O.OpNo = C.OpNo
)
LEFT OUTER JOIN Source D
ON C.Source_ID = D.Source_ID
)
LEFT OUTER JOIN Fleet
ON ...
)
This is similar to how + or - would implicitly use parentheses, i. e.
2 + 3 - 4 - 5
is evaluated as
(((2 + 3) - 4) - 5)
By the way: If you use C.OpNo IS NOT NULL, then the LEFT OUTER JOIN Source D is treated as if it were an INNER JOIN, as you are explicitly removing all the "OUTER" rows.

Related

Using inner or outer join based on input parameter - without using dynamic sql

It is possible to condition a join to outer or inner according to the value of a parameter without using dynamic sql?
I mean, if a parameter(filter value) is given then the query must
return exactly matching records (or 0 records) - acting as inner join
If this filter is not provided then it is needed to return all
records - acting as an outer join
If the filter is on the table you're outer joining to
select some_columns
from left l
left outer join right r
on( l.right_id = r.right_id )
where r.filter_column = p_some_parameter
or p_some_parameter is null
would seem to be all you need. If p_some_parameter is specified, the predicate effectively turns the outer join into an inner join. If p_some_parameter is not specified, the query remains an outer join.

Duplicate results due to multiple left outer joins

My query is as below
select DISTINCT
wftransaction.PERSONID,
pr.PRNUM,
pr.DESCRIPTION,
pr.PR1,
prline.GLDEBITACCT,
wftransaction.TRANSDATE,
prstatus.CHANGEBY
prstatus.CHANGEDATE,
prstatus.STATUS,
prstatus.MEMO
from pr
left outer join wftransaction pr.PRID = wftransaction.ORNERID and wftransaction.OWNERTABLE ='PR'
left outer join prline on pr.PRNUM = prline.PRNUM
left outer join prstatus on pr.PRNUM= prstatus.PRNUM
The result given by my query has duplicate results.Please do help me eliminate the redundant/repeating outputs.
When I put distinct this is what happens, https://i.stack.imgur.com/I2jnN.jpg,
I should only see 2 outputs with the same "STATUS" i.e.(COMPOSING) or (APPR) since they have different "GLDEBITACCT", other than that, there should be no more duplicates.
This is the picture of my Code and Result Set
i think you should be using inner join because if left join where being used all data from table A will repeatedly shows as the table B has its foreign key, or might sometimes you are lacking of WHERE clauses it depends on your query, it will be more helpful to others if you can paste the whole query and their structures with expected results.
https://www.codeproject.com/kb/database/visual_sql_joins.aspx
Good idea will be to place a simple DISTINCT clause in the query
select DISTINCT
wftransaction.PERSONID,
pr.PRNUM,
pr.DESCRIPTION,
pr.PR1,
prline.GLDEBITACCT,
wftransaction.TRANSDATE,
prstatus.CHANGEBY
prstatus.CHANGEDATE,
prstatus.STATUS,
prstatus.MEMO
from pr
left outer join wftransaction pr.PRID = wftransaction.ORNERID and wftransaction.OWNERTABLE ='PR'
left outer join prline on pr.PRNUM = prline.PRNUM
left outer join prstatus on pr.PRNUM= prstatus.PRNUM

SQl query inner join to return the available data even if the join is missing

I am still messing around with this query, which is working but is not returning the data I need.
SELECT prod.Code,
prod.Description,
prod.Groupp,
prod.Quantity,
prod.Unit,
prod.Standard,
prod.Type,
prod.Model,
prod.GroupBKM,
prod.Note,
comp.Unit,
comp.Cost
FROM dbo.Product1 prod
INNER JOIN dbo.Components comp
ON comp.Code = prod.Code
The above query is returning the data only if a comp.code=prod.code exists while I would like to get the data prod.* in any case and obviously the data relevant comp.cost, if does not exist, will be null.
I cannot get it right! Any help will be appreciated.
Replace INNER JOIN with LEFT JOIN
SELECT prod.Code,
prod.Description,
prod.Groupp,
prod.Quantity,
prod.Unit,
prod.Standard,
prod.Type,
prod.Model,
prod.GroupBKM,
prod.Note,
comp.Unit,
comp.Cost
FROM dbo.Product1 prod
LEFT JOIN dbo.Components comp
ON comp.Code = prod.Code
By definition you cannot do this with an INNER JOIN because an INNER JOIN is defined as only returning items for which a match was found.
If you want to return rows in the base SELECT even if the JOIN predicate fails, then you want a LEFT OUTER JOIN ... which is defined as precisely that.
From Wikipedia:
An outer join does not require each record in the two joined tables to
have a matching record. The joined table retains each record—even if
no other matching record exists. Outer joins subdivide further into
left outer joins, right outer joins, and full outer joins, depending
on which table's rows are retained (left, right, or both).
In your case, replace INNER JOIN with LEFT OUTER JOIN.

SQLite - remove duplicate column in a left outer join

I am doing a left outer join over 6 tables, but I dont want the query to keep the duplicated columns. In SQLite the duplicate column are renamed with underscore and added into the view.
Is it possible to remove them in the same query?
SELECT * FROM AQ_ADRESSES
LEFT OUTER JOIN AQ_CP_ADRESSES ON AQ_ADRESSES.IdAdr = AQ_CP_ADRESSES.IdAdr
LEFT OUTER JOIN AQ_ODONYMES ON AQ_ADRESSES.Seqodo = AQ_ODONYMES.Seqodo
LEFT OUTER JOIN AQ_REFERENTIEL ON AQ_ADRESSES.NoSqNoCivq = AQ_REFERENTIEL.NoSqNoCivq
LEFT OUTER JOIN AQ_MUNICIPALITES ON AQ_ADRESSES.CodeMun = AQ_MUNICIPALITES.CodeMun
LEFT OUTER JOIN AQ_ARRONDISSEMENTS ON AQ_ADRESSES.CodeArr = AQ_ARRONDISSEMENTS.CodeArr
As your join columns have the same name, you can use the using operator to define the join. In that case the "duplicate" columns will not be part of the result set
SELECT *
FROM AQ_ADRESSES
LEFT OUTER JOIN AQ_CP_ADRESSES using (IdAdr)
LEFT OUTER JOIN AQ_ODONYMES using (Seqodo)
LEFT OUTER JOIN AQ_REFERENTIEL using (NoSqNoCivq)
LEFT OUTER JOIN AQ_MUNICIPALITES using (CodeMun)
LEFT OUTER JOIN AQ_ARRONDISSEMENTS using (CodeArr)
Tested in SQLite 3.8, don't know if this works the same in earlier versions. But this behavior is required by the SQL standard.
But in general select * is considered harmful in code that is used in production.

SQL joining three tables, join precedence

I have three tables: R, S and P.
Table R Joins with S through a foreign key; there should be at least one record in S, so I can JOIN:
SELECT
*
FROM
R
JOIN S ON (S.id = R.fks)
If there's no record in S then I get no rows, that's fine.
Then table S joins with P, where records is P may or may not be present and joined with S.
So I do
SELECT
*
FROM
R
JOIN S ON (S.id = R.fks)
LEFT JOIN P ON (P.id = S.fkp)
What if I wanted the second JOIN to be tied to S not to R, like if I could use parentheses:
SELECT
*
FROM
R
JOIN (S ON (S.id = R.fks) JOIN P ON (P.id = S.fkp))
Or is that already a natural behaviour of the cartesian product between R, S and P?
All kinds of outer and normal joins are in the same precedence class and operators take effect left-to-right at a given nesting level of the query. You can put the join expression on the right side in parentheses to cause it to take effect first. Remember that you will have to move the ON clauses around so that they stay with their joins—the join in parentheses takes its ON clause with it into the parentheses, so it now comes textually before the other ON clause which will be after the parentheses in the outer join statement.
(PostgreSQL example)
In
SELECT * FROM a LEFT JOIN b ON (a.id = b.id) JOIN c ON (b.ref = c.id);
the a-b join takes effect first, but we can force the b-c join to take effect first by putting it in parentheses, which looks like:
SELECT * FROM a LEFT JOIN (b JOIN c ON (b.ref = c.id)) ON (a.id = b.id);
Often you can express the same thing without extra parentheses by moving the joins around and changing the direction of the outer joins, e.g.
SELECT * FROM b JOIN c ON (b.ref = c.id) RIGHT JOIN a ON (a.id = b.id);
When you join the third table, your first query
SELECT
*
FROM
R
JOIN S ON (S.id = R.fks)
is like a derived table to which you're joining the third table. So if R JOIN S produces no rows, then joining P will never yield any rows (because you're trying to join to an empty table).
So, if you're looking for precedence rules then in this case it's just set by using LEFT JOIN as opposed to JOIN.
However, I may be misunderstanding your question, because if I were writing the query, I would swap S and R around. eg.
SELECT
*
FROM
S
JOIN R ON (S.id = R.fks)
The second join is tied to S as you explicity state JOIN P ON (P.id = S.fkp) - no column from R is referenced in the join.
with a as (select 1 as test union select 2)
select * from a left join
a as b on a.test=b.test and b.test=1 inner join
a as c on b.test=c.test
go
with a as (select 1 as test union select 2)
select * from a inner join
a as b on a.test=b.test right join
a as c on b.test=c.test and b.test=1
Ideally, we would hope that the above two queries are the same. However, they are not - so anybody that says a right join can be replaced with a left join in all cases is wrong. Only by using the right join can we get the required result.