I am importing a view from an old database as part of a software upgrade and I saw this FROM clause as I was working.
FROM
t_store_master AS STM
RIGHT OUTER JOIN
dbo.t_shipping AS SHP
INNER JOIN
t_hu_master AS HUM
INNER JOIN
t_stored_item AS STO ON HUM.hu_id = STO.hu_id
ON SHP.shipment_id = HUM.reserved_for
AND SHP.location_id = HUM.location_id
INNER JOIN
dbo.t_carrier AS CAR ON SHP.carrier_code = CAR.carrier_code
LEFT OUTER JOIN
dbo.t_order_detail AS ORD
INNER JOIN
dbo.t_order AS ORM ON ORD.order_number = ORM.order_number
INNER JOIN
dbo.t_pick_detail PDL ON ORD.order_number = PDL.order_number
ON STO.type = PDL.pick_id
ON STM.store_id = HUM.control_number_2
It is my understanding that OUTER JOIN requires an ON clause in order to be interpreted properly by the compiler. However, there must be some edge case scenarios I am unaware of because the code above returns good data without throwing any errors.
After some goggling and reading up on MSDN standards for OUTER JOINs, I am still at a loss for what the LEFT OUTER JOIN and RIGHT OUTER JOIN are doing in this query.
I do know that the multiple ON clauses beneath the INNER JOINs are necessary. There seems to be some kind of hidden or implied mapping being done between the ON clauses and the OUTER JOINs. Past that I cannot tell what the purpose of writing a query this way would be.
Could someone shed some insight on how this works and why it would be written this way?
This is allowed. It makes slightly more sense with parentheses:
FROM t_store_master STM RIGHT OUTER JOIN
(dbo.t_shipping SHP INNER JOIN
(t_hu_master HUM INNER JOIN
t_stored_item STO
ON HUM.hu_id = STO.hu_id
)
ON SHP.shipment_id = HUM.reserved_for AND
SHP.location_id = HUM.location_id
) INNER JOIN
(dbo.t_carrier CAR
ON SHP.carrier_code = CAR.carrier_code LEFT OUTER JOIN
((dbo.t_order_detail ORD INNER JOIN
dbo.t_order ORM
ON ORD.order_number = ORM.order_number
) INNER JOIN
dbo.t_pick_detail PDL
ON ORD.order_number = PDL.order_number
)
ON STO.type = PDL.pick_id
)
ON STM.store_id = HUM.control_number_2
That said, I would recommend never writing a query like this and rewriting the query ASAP if it is in production code. At the very least, add the parentheses!
Parentheses are almost never needed in the FROM clause to express JOINs (there is one case where I do happen to use them). Non-interleaved ON clauses are never needed -- or at least, I have never had occasion to use them or think they were the best way to write a query. But, both are allowed.
Related
So I've never seen a join syntax like this before, and I am curious at what it is called and what the use case is for it?
It seems to preforms similar to when you join to a subquery, without actually creating a subquery.
from member ddm
inner join trace_execution ssn on ddm.id = ssn.id
**left join dw_member_subscription dms
inner join dw_d_subscription dds on dms.id = dds.id
on ddm.id = dms.id**
JOINs can be nested like this. It is interpreted as:
from member ddm inner join
trace_execution ssn
on ddm.id = ssn.id left join
(dw_member_subscription dms inner join
dw_d_subscription dds
on dms.id = dds.id
)
on ddm.id = dms.id
I wouldn't write SQL like this in general. However, I wish the standard required parentheses to make it clear what is going on when the JOINs are not interleaved with their corresponding ON clauses.
I am trying to use a RIGHT and LEFT Join together. I have it working when only left joining one table. But I am trying to now include another table in my left join. It gives me an error saying I am missing an operator. Where am I missing a parenthesis?
FROM qSplit RIGHT JOIN (t_i360_agent AS i LEFT JOIN cmsAgent_Split AS c
ON ((i.LocalDay = c.LocalDay) AND (i.ACDID = c.LOGID))
LEFT JOIN qry_AllNewtables as qry ON (qry.custConvDate = c.LocalDay)
AND (qry.CustAgentLoginName = i.Loginname) ) ON qSplit.SPLIT = c.SPLIT
Don't use LEFT JOIN and RIGHT JOIN together. I imagine that somewhere there could be a query where it makes sense. In practice, I don't think I have ever used them both in the same query, possibly because I write queries using LEFT JOIN.
If you want everything in the agent table, then make it first! And use left join;
FROM t_i360_agent i LEFT JOIN
cmsAgent_Split c
ON i.LocalDay = c.LocalDay AND i.ACDID = c.LOGID LEFT JOIN
qry_AllNewtables qry
ON qry.custConvDate = c.LocalDay AND
qry.CustAgentLoginName = i.Loginname LEFT JOIN
qSplit
ON qSplit.SPLIT = c.SPLIT
It is much easier to follow the intention of the query this way. You are starting with the data that you think is so important that you want to keep all of it, even when JOINs have no matching rows.
Beside the suggestion by Gordon the problem was you miss a join condition for the RIGHT JOIN once I reformat the query was easy to see what was missing.
FROM qSplit
RIGHT JOIN t_i360_agent AS i
-- Need join condition
LEFT JOIN cmsAgent_Split AS c
ON (i.LocalDay = c.LocalDay)
AND (i.ACDID = c.LOGID))
LEFT JOIN qry_AllNewtables as qry
ON (qry.custConvDate = c.LocalDay)
AND (qry.CustAgentLoginName = i.Loginname)
Is there any reason on an INNER JOIN to have a condition on the main table vs in the WHERE clause?
Example in INNER JOIN:
SELECT
(various columns here from each table)
FROM dbo.MainTable AS m
INNER JOIN dbo.JohnDataRecord AS jdr
ON m.ibID = jdr.ibID
AND m.MainID = #MainId -- question here
AND jdr.SentDate IS NULL
LEFT JOIN dbo.PTable AS p1
ON jdr.RecordID = p1.RecordID
LEFT JOIN dbo.DataRecipient AS dr
ON jdr.RecipientID = dr.RecipientID
(more left joins here)
WHERE
dr.lastRecordID IS NOT NULL;
Query with condition in WHERE clause:
SELECT
(various columns here from each table)
FROM dbo.MainTable AS m
INNER JOIN dbo.JohnDataRecord AS jdr
ON m.ibID = jdr.ibID
AND jdr.SentDate IS NULL
LEFT JOIN dbo.PTable AS p1
ON jdr.RecordID = p1.RecordID
LEFT JOIN dbo.DataRecipient AS dr
ON jdr.RecipientID = dr.RecipientID
(more left joins here)
WHERE
m.MainID = #MainId -- question here
AND dr.lastRecordID IS NOT NULL;
Difference in other similar questions that are more general whereas this is specific to SQL Server.
In the scope of the question,
Is there any reason on an INNER JOIN to have a condition on the main
table vs in the WHERE clause?
This is a STYLE choice for the INNER JOIN.
From a pure style reflection point of view:
While there is no hard and fast rule for STYLE, it is generally observed that this is a less often used style choice. For example that might generally lead to more challenging maintenance such as if someone where to remove the INNER JOIN and all the subsequent ON clause conditions, it would effect the primary table result set, OR make the query more difficult to debug/understand when it is a very complex set of joins.
It might also be noted that this line might be placed on many INNER JOINS further adding to the confusion.
I was wondering if it was possible to make this query smaller
SELECT Templates.TempName
,TimeSpans.TimeSpan
,TemplateSlotTypes.[Type]
,TemplateSlots.Potition
,TemplateSlots.duration
FROM TimeSpans
INNER JOIN Templates ON Templates.TimeSpanId = TimeSpans.Id
INNER JOIN TemplateSlots ON Templates.Id = TemplateSlots.TemplateId
INNER JOIN TemplateSlotTypes ON TemplateSlots.[Type] = TemplateSlotTypes.Id
It seems that you need all the parts of this query, so there's nothing that can just be dropped from the query.
You could, however, use table aliases to reduce the amount of text:
SELECT t.TempName, tsp.TimeSpan, tst.[Type], ts.Potition, ts.duuration
FROM TimeSpans tsp
INNER JOIN Templates t ON t.TimeSpanId = tsp.Id
INNER JOIN TemplateSlots ts ON t.Id = ts.TemplateId
INNER JOIN TemplateSlotTypes tst ON ts.[Type] = tst.Id
Table aliases will make the joins less verbose, but assuming the field names are unique to the tables involved you don't need to prefix them with the table name.
SELECT TempName
,TimeSpan
,[Type]
,Potition
,duration
FROM TimeSpans ts
INNER JOIN Templates t ON t.TimeSpanId = ts.Id
INNER JOIN TemplateSlots s ON t.Id = s.TemplateId
INNER JOIN TemplateSlotTypes st ON s.[Type] = st.Id
However, I would recommend the use of a good IDE for SQL query development where it performs the joins for you and removes a lot of the typing.
Manually reducing the amount of text in a query is a false economy in the long run. It can actually make the SQL harder to read, and therefore more time consuming to modify or debug in the future.
Since SQLite does not support RIGHT OUTER JOINS I pose the following challenge (read: invitation to do my work for me):
Refactor this query so it no longer utilises SQLite-unsupported constructs like RIGHT/FULL OUTER JOINs.
SELECT strings.*, translations.text
FROM translations INNER JOIN
language ON translations.language_id = language.id RIGHT OUTER JOIN
strings ON translations.string_id = strings.id
WHERE (language.handle = 'english')
I sense it can be achieved with subqueries or by pivoting the tables and performing a LEFT OUTER JOIN but my attempts have failed; my SQL's not what it used to be.
Here's a query builder outline showing the applicable schema: http://dl.getdropbox.com/u/264612/sql-refactor.PNG
First to crack it gets an e-hug from dekz
The following is untested.
select strings.*, translations.text
from strings left outer join translations
on translations.string_id = strings.id
and translations.language_id = (select id
from language
where language.handle = 'english')
I think this will give you all strings with the matching translation text where a suitable translation exists in English. Is that what you are trying to get?
Intriguing that SQLite allows LEFT OUTER JOINs but not RIGHT OUTER JOINs. Well, since it does allow LEFT OUTER JOINs, you're right, you can just rearrange the join order:
SELECT strings.*, translations.text
FROM strings LEFT OUTER JOIN (
translations INNER JOIN language ON translations.language_id = language.id
) tr ON tr.string_id = strings.id
WHERE (language.handle = 'english')
[EDIT: Applied Blorgbeard's suggestion of naming the joined table to get the query to parse -- hope it works now!]