How do I understand the commas happening in join statements? - sql

I am running into a legendary code and have been struggling in breaking this down in more simpler terms. The legendary code runs perfectly. I am copying the join part of the code and not including the select for brevity. I extremely familiar with joins but I want to focus on two pieces
1.) What does FROM COURSE c, GRADE g this try to achieve? How is it know how to do it?
2.) What does this do GRADEITEM gs, STUDENT s, SECTION sec when these tables are listed with commas after the inner join occured? Aren't I missing an on for these three tables tables?
FROM COURSE c, GRADE g
INNER JOIN CEC cc
ON g.SectionID = cc.SectionID AND g.StudentID = cc.StudentID, GRADEITEM gs, STUDENT s, SECTION sec

This code:
FROM COURSE c,
GRADE g INNER JOIN
CEC cc
ON g.SectionID = cc.SectionID AND g.StudentID = cc.StudentID, GRADEITEM gs,
STUDENT s,
SECTION sec
is very arcane (which like "legendary" is a polite way of saying what I really think).
This is equivalent to:
FROM COURSE c
GRADE g
ON g.StudentID = cc.StudentID INNER JOIN
CEC cc
ON g.SectionID = cc.SectionID CROSS JOIN
GRADEITEM gs CROSS JOIN
STUDENT s CROSS JOIN
SECTION sec
The comma basically means CROSS JOIN. I rewrite the first CROSS JOIN to be the intended JOIN by splitting the ON clause.
The rest are just Cartesian products. I imagine that the WHERE clause provides more filtering.
"Arcane" and "archaic" are polite ways of describing the code. This is very poorly written code. If Cartesian products are intended, then CROSS JOIN is appropriate. That said, I'm pretty sure that JOINs are intended for all the tables -- for any useful query.

Related

How to do multiple left joins in SQL Server SQL Query

I'm trying to perform the query below on SQL Server:
SELECT
[dbo].[Machine].[MachineID],
[dbo].[Machine].[CompanyID],
[dbo].[Company].[AccountRef],
[dbo].[Machine].[ProductTypeID],
[dbo].[Machine].[SerialNo],
[dbo].[Machine].[InstallationDate],
[dbo].[Machine].[SalesTypeID],
[dbo].[SalesType].[SalesType],
[dbo].[Machine].[LeasingCompanyID],
[dbo].[LeasingCompany].[Name],
[dbo].[Machine].[QuarterlyRentalCost],
[dbo].[Machine].[Term],
[dbo].[Machine].[ExpiryDate],
[dbo].[Machine].[Scales],
[dbo].[Machine].[Chips],
[dbo].[Machine].[ContractTypeID],
[dbo].[ContractType].[ContractType],
[dbo].[Machine].[ContractCost],
[dbo].[Machine].[InvoiceDate],
[dbo].[Machine].[ServiceDueDate],
[dbo].[Machine].[ServiceNotes],
[dbo].[Machine].[modelID],
[dbo].[Machine].[Model],
[dbo].[Machine].[IMP_Machine Reference],
[dbo].[Machine].[Smart]
FROM
[dbo].[Machine], [dbo].[Company], [dbo].[SalesType], [dbo].[LeasingCompany], [dbo].[ContractType]
LEFT JOIN
[dbo].[Machine] as A ON A.[CompanyID] = [dbo].[Company].[CompanyID]
LEFT JOIN
[dbo].[Machine] as B ON B.[SalesTypeID] = [dbo].[SalesType].[SalesTypeID]
LEFT JOIN
[dbo].[Machine] as C ON C.[LeasingCompanyID] = [dbo].[LeasingCompany].[LeasingCompanyID]
LEFT JOIN
[dbo].[Machine] as D ON D.[ContractTypeID] = [dbo].[ContractType].[ContractTypeID] ;
But for some reason that i cannot see for the life of me, the destination column name in the bottom 3 join statements is reporting "The multi part identifier could not be bound".
Could anyone assist please?
Many Thanks,
You were pretty close, for readability, you dont need the extensive [dbo].[table] all over. You can define it once in the from clause with an alias, then use that alias the rest of the way through. Also, make the alias make sense to the context of the table as you will see in this below. LeasingCompany lc, SalesType st, etc.
Also, I try to have indented so I always know the first table of the join relationship and the JOIN indented on where it is being joined to. Then I keep the orientation of the from/to table aliases the same context.
Since the machine table is used once and joined to 4 different lookup tables, you can reuse the same "m" alias to each underlying. Think of a tree and branches. The root tree is your "Machine", and all the branches are the lookups.
SELECT
m.MachineID,
m.CompanyID,
c.AccountRef,
m.ProductTypeID,
m.SerialNo,
m.InstallationDate,
m.SalesTypeID,
st.SalesType,
m.LeasingCompanyID,
lc.LeasingCompany.Name,
m.QuarterlyRentalCost,
m.Term,
m.ExpiryDate,
m.Scales,
m.Chips,
m.ContractTypeID,
ct.ContractType,
m.ContractCost,
m.InvoiceDate,
m.ServiceDueDate,
m.ServiceNotes,
m.modelID,
m.Model,
m.IMP_Machine Reference,
m.Smart
FROM
Machine m
LEFT JOIN Company c
ON m.CompanyID = c.CompanyID
LEFT JOIN SalesType st
ON m.SalesTypeID = st.SalesTypeID
LEFT JOIN LeasingCompany lc
ON m.LeasingCompanyID = lc.LeasingCompanyID
LEFT JOIN ContractType ct
ON m.ContractTypeID = ct.ContractTypeID ;

SQL query does not show the correct kilos, I don't know where is the problem

This is my SQL query in text format:
SELECT sum(D.Kilogramos) as Kilogramos, C.Categoria , C.Tipo S Tipo, LA.Huerta,TC.TipoCorte, LA.JefeAcopio,LA.IdLote
FROM dbo.MOV_EmpaqueDetalle D
INNER JOIN dbo.Rep_Lote LA on LA.IdLote= D.IdLote
INNER JOIN dbo.MOV_OrdenesCorte MO on MO.IdOrdenCorte = LA.IdOrdenCorte
INNER JOIN dbo.PRO_Productos P on P.IdProducto=D.IdProducto
INNER JOIN dbo.PRO_Categorias C ON C.IdCategoria=P.IdCategoria
INNER JOIN dbo.MOV_Acuerdos A on A.IdAcuerdo=LA.IdAcuerdo
INNER JOIN dbo.MOV_TiposCorte TC on A.IdTipoCorte=TC.IdTipoCorte
WHERE CAST(MO.Fecha AS DATE) BETWEEN '2018-10-23' AND '2018-10-23'
GROUP BY LA.Huerta, LA.IdLote,C.Categoria, C.Tipo, TC.TipoCorte, LA.JefeAcopio,Mo.Fecha
ORDER BY LA.Huerta
The rows in the column kilogramos of the Huerta EL DOS are wrong, the information correct must be the following:
Kilogramos Huerta
13807.00 El DOS
I have got the accumulated row in sum function, the kilograms are wrong.
Its kind of hard to provide an answer with the few information you provided, but I would suggest to check whether your GROUP BY is correct.
If you really just need the sum Kilogramos over the different Huertas your group condition should look like this: GROUP BY LA.Huerta

Convert Legacy SQL Outer JOIN *=, =* to ANSI

I need to convert a legacy SQL outer Join to ANSI.
The reason for that being, we're upgrading from a legacy DB instance (2000/5 ?) to SQL 2016.
Legacy SQL query :-
SELECT
--My Data to Select--
FROM counterparty_alias ca1,
counterparty_alias ca2,
counterparty cp,
party p
WHERE cp.code *= ca1.counterparty_code AND
ca1.alias = 'Party1' AND
cp.code *= ca2.counterparty_code AND
ca2.alias = 'Party2' AND
cp.code *= p.child_code AND
cp.category in ('CAT1','CAT2')
Here, Party1 and Party2 Are the party type codes and CAT1 and CAT2 are the category codes. They're just data; I have abstracted it, because the values don't really matter.
Now, when I try to replace the *= with a LEFT OUTER JOIN, I get a huge mismatch on the Data, both in terms of the number of rows, as well as the Data itself.
The query I'm using is this :
What am I doing wrong ?
SELECT
--My Data to Select--
FROM
counterparty cp
LEFT OUTER JOIN counterparty_alias ca1 ON cp.code = ca1.counterparty_code
LEFT OUTER JOIN counterparty_alias ca2 ON cp.code = ca2.counterparty_code
LEFT OUTER JOIN party p ON cp.code = p.child_code
WHERE
ca1.alias = 'Party1' AND
ca2.alias = 'Party2' AND
cp.category in ('CAT1','CAT2')
Clearly , in all the three legacy joins , the cp (counterparty) table is on the Left hand Side of the *=. So that should translate to a LEFT OUTER JOIN WITH all the three tables. However, my solution doesn't seem to to be working
How can I fix this ? What am I doing wrong here ?
Any help would be much appreciated. Thanks in advance :)
EDIT
I also have another query like this :
SELECT
--My Data to Select--
FROM dbo.deal d,
dbo.deal_ccy_option dvco,
dbo.deal_valuation dv,
dbo.strike_modifier sm
WHERE d.deal_id = dvco.deal_id
AND d.deal_id = dv.deal_id
AND dvco.base + dvco.quoted *= sm.ccy_pair
AND d.maturity_date *= sm.expiry_date
In this case, both the dvco and d tables seem to be doing a LEFT OUTER JOIN on the same table sm. How do I proceed about this ?
Maybe join in on the same table and use an alias sm1 and sm2 ?
Or should I use sm as the central table and change the join to RIGHT OUTER JOIN on dvco and d tables ?
I think the problem with your translation is that you are using conditions on the right tables in the where clause instead of in the on clause.
When I tried to translate it, this is the translation I've got:
FROM counterparty cp
LEFT JOIN counterparty_alias ca1 ON cp.code = ca1.counterparty_code
AND ca1.alias = 'Party1'
LEFT JOIN counterparty_alias ca2 ON cp.code *= ca2.counterparty_code
AND ca2.alias = 'Party2'
LEFT JOIN party p ON cp.code = p.child_code
WHERE cp.category in ('CAT1','CAT2')
However, it's hard to know if I'm correct since you didn't provide sample data, desired results, or even a complete query.
If you're doing a conversion, it has been my experience that *= is a RIGHT OUTER JOIN and =* is a LEFT OUTER JOIN in terms of a straight conversion.
I am converting hundreds of stored procs and views now and through testing this is what matches. I run the query as the original first, then make the changes and re-run it with the ANSI compliant code.
The data returned needs to be the same for consistency in our application.
So for your second query I think it would look something like this:
FROM dbo.deal d
INNER JOIN dbo.deal_ccy_option dvco ON d.deal_id = dvco.deal_id
INNER JOIN dbo.deal_valuation dv ON d.deal_id = dv.deal_id
RIGHT OUTER JOIN dbo.strike_modifier sm ON d.maturity_date = sm.expiry_date
AND (dvco.base + dvco.quoted) = sm.ccy_pair
Thanks for the help and sorry for the late post, but I got it to work with a quick hack, using the Query Designer Tool inbuilt in SSMS. It simply refactored all my queries and put in the correct Join, Either Left or Right , and the Where condition as an AND condition on the Join itself, so I was getting the correct data result set for both pre and post, only sometimes the data sorting/ordering was a little off.
I got lost with deadlines and couldnt update with the solution earlier. Thanks again for the help. Hope this helps someone else too !!
Still a little bit unsure though why the ordering/sorting was a little off if the Join condition was the same and the filters as well, because data was a 100 % match.
To get the query Designer to Work , just select your legacy SQL, and
open the Query Designer by pressing Ctrl + Shift + Q or Goto Main Menu
ToolBar => Query => Design Query in Editor.
Thats it. This will refactor your legacy code to new ANSI standards. You wll get the converted query with the new Joins that you can copy and test. Worked 100% of the time for me, except in some cases where the sorting was not matching, which you can check by adding a simple order by clause to both pre and post to compare the data.
For reference, I cross checked with this post :
http://sqlblog.com/blogs/john_paul_cook/archive/2013/03/02/using-the-query-designer-to-convert-non-ansi-joins-to-ansi.aspx

Debugging SQL Query

So I've researched this question but the solutions I've found are for debugging syntax errors, whereas I am encountering logic errors. I have a query in Access that is joining a table to a query, and the query is working almost perfectly. It runs, it's displaying exactly what I want where I want it to. The only problem is that the data it is showing is wrong. It has a group by field, and then the other fields are sums of fields based on the query joins. However the sums are wrong for some groupbys and right for others, and I'm unsure why. Is there a way to get into my code and see where some of these queries are grabbing the numbers?
Here is query I'm working with:
SELECT m.Bldg, Sum([e].[TotCost]*IIf([e].[Utility]="E",1,0)) AS ECost, Sum(g.TotCost*Switch(g.Utility='G',1,True,0)) AS GCost, Sum(h.TotCost*Switch(h.Utility='H',1,True,0)) AS HCost, Sum(c.TotCost*Switch(c.Utility='C',1,True,0)) AS CCost, Sum(w.TotCost*Switch(w.Utility='W',1,True,0)) AS WCost, Sum(s.TotCost*Switch(s.Utility='S',1,True,0)) AS SCost
FROM (((((tblBldgMeters AS m LEFT JOIN qryCurrentMonthMtrHis AS e ON m.EMeters = e.MeterID) LEFT JOIN qryCurrentMonthMtrHis AS g ON m.GMeters = g.MeterID) LEFT JOIN qryCurrentMonthMtrHis AS h ON m.HMeters = h.MeterID) LEFT JOIN qryCurrentMonthMtrHis AS c ON m.CMeters = c.MeterID) LEFT JOIN qryCurrentMonthMtrHis AS w ON m.WMeters = w.MeterID) LEFT JOIN qryCurrentMonthMtrHis AS s ON m.SMeters = s.MeterID
GROUP BY m.Bldg;
The problem is that the Cost fields will sometimes be right and sometimes be wrong, and I can't understand why. It can be anywhere from a hundred to a million dollars off. Each building has several meters that have separate costs that have to be added together, and so I have a query with just the current months costs for each meter, and then a table that lists all the buildings and the corresponding meters with it.
The best approach is to isolate a data set that works and one that doesn't . Then break the aggregate and verify the data. See what the query is actually doing... Without data samples or even a copy of access I could shoot from the hip with something like:
SELECT m.Bldg,
[e].[TotCost]*IIf([e].[Utility]="E",1,0) AS ECost,
e.Utility as e_Utility
g.TotCost*Switch(g.Utility='G',1,True,0) AS GCost,
g.Utility as g_Utility,
h.TotCost*Switch(h.Utility='H',1,True,0) AS HCost,
h.Utility as h_Utility,
c.TotCost*Switch(c.Utility='C',1,True,0) AS CCost,
c.utility as c_Utility,
w.TotCost*Switch(w.Utility='W',1,True,0) AS WCost,
w.Utility as w_Utility
s.TotCost*Switch(s.Utility='S',1,True,0) AS SCost
s.Utility as s_Utility
FROM
(((((tblBldgMeters AS m
LEFT JOIN qryCurrentMonthMtrHis AS e ON m.EMeters = e.MeterID)
LEFT JOIN qryCurrentMonthMtrHis AS g ON m.GMeters = g.MeterID)
LEFT JOIN qryCurrentMonthMtrHis AS h ON m.HMeters = h.MeterID)
LEFT JOIN qryCurrentMonthMtrHis AS c ON m.CMeters = c.MeterID)
LEFT JOIN qryCurrentMonthMtrHis AS w ON m.WMeters = w.MeterID)
LEFT JOIN qryCurrentMonthMtrHis AS s ON m.SMeters = s.MeterID
You might want to separate out the TotCost fields as well. This should give you decent insight into what is actually happening in the query. That's always my go to in troubleshooting though... Break the aggregate, check the data. If the data set is to large, isolate a test group and narrow it down.

Do I misunderstand joins?

I'm trying to learn the the ansi-92 SQL standard, but I don't seem to understand it completely (I'm new to the ansi-89 standard as well and in databases in general).
In my example, I have three tables kingdom -< family -< species (biology classifications).
There may be kingdoms without species nor families.
There may be families without species nor kindgoms.
There may be species without kingdom or families.
Why this may happen?
Say a biologist, finds a new species but he has not classified this into a kingdom or family, creates a new family that has no species and is not sure about what kingdom it should belong, etc.
here is a fiddle (see the last query): http://sqlfiddle.com/#!4/015d1/3
I want to make a query that retrieves me every kingdom, every species, but not those families that have no species, so I make this.
select *
from reino r
left join (
familia f
right join especie e
on f.fnombre = e.efamilia
and f.freino = e.ereino
) on r.rnombre = f.freino
and r.rnombre = e.ereino;
What I think this would do is:
join family and species as a right join, so it brings every species, but not those families that have no species. So, if a species has not been classified into a family, it will appear with null on family.
Then, join the kingdom with the result as a left join, so it brings every kingdom, even if there are no families or species classified on that kingdom.
Am I wrong? Shouldn't this show me those species that have not been classified? If I do the inner query it brings what I want. Is there a problem where I'm grouping things?
You're right on your description of #1... the issue with your query is on step #2.
When you do a left join from kingdom to (family & species), you're requesting every kingdom, even if there's no matching (family & species)... however, this won't return you any (family & species) combination that doesn't have a matching kingdom.
A closer query would be:
select *
from reino r
full join (
familia f
right join especie e
on f.fnombre = e.efamilia
and f.freino = e.ereino
) on r.rnombre = f.freino
and r.rnombre = e.ereino;
Notice that the left join was replaced with a full join...
however, this only returns families that are associated with a species... it doesn't return any families that are associated with kingdoms but not species.
After re-reading your question, this is actually want you wanted...
EDIT: On further thought, you could re-write your query like so:
select *
from
especie e
left join familia f
on f.fnombre = e.efamilia
and f.freino = e.ereino
full join reino r
on r.rnombre = f.freino
and r.rnombre = e.ereino;
I think this would be preferrable, because you eliminate the RIGHT JOIN, which are usually frowned upon for being poor style... and the parenthesis, which can be tricky for people to parse correctly to determine what the result will be.
In case this helps:
Relationally speaking, [OUTER JOIN is] a kind of shotgun marriage: It
forces tables into a kind of union—yes, I do mean union, not join—even
when the tables in question fail to conform to the usual requirements
for union. It does this, in effect, by padding one or
both of the tables with nulls before doing the union, thereby making
them conform to those usual requirements after all. But there's no
reason why that padding shouldn't be done with proper values instead
of nulls, as in this example:
SELECT SNO , PNO
FROM SP
UNION
SELECT SNO , 'nil' AS PNO
FROM S
WHERE SNO NOT IN ( SELECT SNO FROM SP )
The above is equivalent to:
SELECT SNO , COALESCE ( PNO , 'nil' ) AS PNO
FROM S NATURAL LEFT OUTER JOIN SP
Source:
SQL and Relational Theory: How to Write Accurate SQL Code By C. J. Date
If you want the query rewritten with only the slightest change from what you have, you can change the LEFT join to a FULL join. You can further remove the redundant parenthesis and the r.rnombre = f.freino from the ON condition:
select *
from reino r
full join --- instead of LEFT JOIN
familia f
right join especie e
on f.fnombre = e.efamilia
and f.freino = e.ereino
on r.rnombre = e.ereino;
---removed the: r.rnombre = f.freino
Try to use this:
select *
from reino r
join especie e on (r.rnombre = e.ereino)
join familia f on (f.freino = e.ereino and f.fnombre = e.efamilia)
could it be, that you interchanged efamilia and enombre in table especie?