SQL query help, conditional join - sql

SQL newbie here :)
Here are my tables if anyone's interested.
AHH, cant post image yet
http://img832.imageshack.us/img832/442/72098588.jpg
What I'm trying to do is query the tblPatientStats table within a date interval (#StartDate, #EndDate)
and group them accordingly in a data grid on winforms.
So each row in tblPatientStats either have a RefDoctor or RefMode or both or none at all.
So the query should return a table with the Name of the patient from tblPatient, the RefMode from tblRefMode, the Name of the RefDoctor (Title + FirstName + lastName) and SessionDate from tblPatientStats
==> yfrog dot com/0yhi2dj
Here is my attempt so far.
INSERT #Final(Name, Doctor, Mode, SessionDate)
SELECT DISTINCT (FirstNames + LastName) as Name,
(tblRefDoctor.RefDTitle + ' ' + tblRefDoctor.RefDFNames + ' ' + tblRefDoctor.RefDName) AS Doctor,
tblRefMode.RefMode AS Mode, SessionDate
FROM tblPatientStats, tblPatient
left outer join tblRefDoctor on (RefDoctor = tblRefDoctor.RefDoctor)
left outer join tblRefMode on (RefModeID = tblRefMode.RefModeID)
WHERE
tblPatientStats.RefDoctor IS NOT NULL or tblPatientStats.RefModeID IS NOT NULL
AND
tblPatient.PatientID = tblPatientStats.PatientID
AND tblPatientStats.SessionDate between #StartDate AND #EndDate
What am I doing wrong? The query times out every single time, the tables are small, less than 10K records each.
Any help would be much appreciated.

I suspect the issue is because of the cartesian join on
tblPatientStats, tblPatient
Whilst there is a join condition in the where clause there is an issue with the precedence of the boolean operators. This is in order Not, And, Or so I think you need brackets around the 'Or' ed conditions.
The WHERE condition on the original query with brackets applied to show the effective operator precedence is
WHERE
tblPatientStats.RefDoctor IS NOT NULL or
(tblPatientStats.RefModeID IS NOT NULL
AND tblPatient.PatientID = tblPatientStats.PatientID
AND tblPatientStats.SessionDate between #StartDate AND #EndDate)
This is almost certainly not the desired semantics and will likely bring back too many rows.
I've moved the join condition between tblPatientStats and tblPatient up into the JOIN clauses and added brackets to the Or ed conditions.
FROM tblPatientStats
inner join tblPatient on tblPatient.PatientID = tblPatientStats.PatientID
left outer join tblRefDoctor on RefDoctor = tblRefDoctor.RefDoctor
left outer join tblRefMode on RefModeID = tblRefMode.RefModeID
WHERE
(tblPatientStats.RefDoctor IS NOT NULL or tblPatientStats.RefModeID IS NOT NULL)
AND tblPatientStats.SessionDate between #StartDate AND #EndDate

Related

How to do operations between a column and a subquery

I would like to know how I can do operations between a column and a subquery, what I want to do is add to the field Subtotal what was obtained in the subquery Impuestos, the following is the query that I am using for this case.
Select
RC.PURCHID;
LRC.VALUEMST as 'Subtotal',
isnull((
select sum((CONVERT(float, TD1.taxvalue)/100)*LRC1.VALUEMST ) as a
FROM TAXONITEM TOI1
inner join TAXDATA TD1 ON (TD1.TAXCODE = TOI1.TAXCODE and RC.DATAAREAID = TD1.DATAAREAID)
inner join TRANS LRC1 on (LRC1.VEND = RC.RECID)
WHERE TOI1.TAXITEMGROUP = PL.TAXITEMGROUP and RC.DATAAREAID = TOI1.DATAAREAID
), 0) Impuestos
from VEND RC
inner join VENDTABLE VTB on VTB.ACCOUNTNUM = RC.INVOICEACCOUNT
inner join TRANS LRC on (LRC.VEND = RC.RECID)
inner join PURCHLINE PL on (PL.LINENUMBER =LRC.LINENUM and PL.PURCHID =RC.PURCHID)
where year (RC.DELIVERYDATE) =2021 and RC.PURCHASETYPE =3 order by RC.PURCHID;
Hope someone can give me some guidance when doing operations with subqueries.
A few disjointed facts that may help:
When a SELECT statement returns only one row with one column, you can enclose that statement in parenthesis and use it as a plain value. In your case, let's say that select sum(......= TOI1.DATAAREAID returns 500. Then, your outer select's second column is equivalent to isnull(500,0)
You mention in your question "subquery Impuestos". Keep in mind that, although you indeed used a subquery as we mentioned earlier, by the time it was enclosed in parentheses it is not treated as a subquery (more accurately: derived table), but as a value. Thus, the "Impuestos" is only a column alias at this point
I dislike and avoid subqueries before the from, makes things much harder to read. Here is a solution with apply which will keep your code mostly intact:
Select
RC.PURCHID,
LRC.VALUEMST as 'Subtotal',
isnull(subquery1.a, 0) as Impuestos
from VEND RC
inner join VENDTABLE VTB on VTB.ACCOUNTNUM = RC.INVOICEACCOUNT
inner join TRANS LRC on (LRC.VEND = RC.RECID)
inner join PURCHLINE PL on (PL.LINENUMBER =LRC.LINENUM and PL.PURCHID =RC.PURCHID)
outer apply
(
select sum((CONVERT(float, TD1.taxvalue)/100)*LRC1.VALUEMST ) as a
FROM TAXONITEM TOI1
inner join TAXDATA TD1 ON (TD1.TAXCODE = TOI1.TAXCODE and RC.DATAAREAID = TD1.DATAAREAID)
inner join TRANS LRC1 on (LRC1.VEND = RC.RECID)
WHERE TOI1.TAXITEMGROUP = PL.TAXITEMGROUP and RC.DATAAREAID = TOI1.DATAAREAID
) as subquery1
where year (RC.DELIVERYDATE) =2021 and RC.PURCHASETYPE =3 order by RC.PURCHID;

SQL sum aggregate function gives wrong results

My data is given below
Right answer is Sum = 601,050.00
But SQL sum aggregate function gives me wrong answer that is 5078150.00000
15,000.00 27,950.00 24,750.00 11,550.00 7,400.00 7,500.00 14,650.00 12,500.00 32,800.00 35,700.00 94,100.00 10,100.00 19,700.00 22,100.00 35,450.00 28,050.00 50,150.00 69,750.00 13,800.00 3,600.00 18,600.00 2,350.00 7,200.00 21,600.00 7,700.00 4,500.00 2,500.00
select sum(SO_SalesOrder.OrderTotal),l.Name as [Store Name]
From SO_SalesOrder inner join BASE_Location l on
SO_SalesOrder.LocationId = l.LocationId
inner join SO_SalesOrder_Line on SO_SalesOrder.SalesOrderId =
SO_SalesOrder_Line.SalesOrderId
inner join BASE_Product on BASE_Product.ProdId =
SO_SalesOrder_Line.ProdId
inner join BASE_Category on BASE_Category.CategoryId =
BASE_Product.CategoryId
where SO_SalesOrder.OrderDate >= '2018-02-01' and
SO_SalesOrder.OrderDate <= '2018-02-28' and BASE_Category.Name = '1MHNZ'
group by l.Name
There is likely to be a problem with one (or more) of your joins, maybe you have duplicate rows or the joining conditions are not OK.
Remove the group by l.Name, the SUM() aggregate and see if the returned values for SO_SalesOrder.OrderTotal are what you are expecting them to be (you might need to filter with a particular l.Name in a WHERE clause). It's very likely you will see duplicate amounts, or amounts you are not considering when arriving to the value 601,050.00.
If so, try joining the tables 1 by 1 and see which ones are making your rows go comando.
In my opinion your problem depends on the logic of the query.
You have a master-detail relationship between SO_SalesOrder and SO_SalesOrder_line joined by SalesOrderId column.
So if you have three lines in your order you will sum up three times the same OrderTotal.
try with something like this:
select sum(SO_SalesOrder.OrderTotal) Total, l.Name as [Store Name]
From SO_SalesOrder
join BASE_Location l on SO_SalesOrder.LocationId = l.LocationId
where SO_SalesOrder.OrderDate >= '2018-02-01' and SO_SalesOrder.OrderDate <= '28-02-2018'
and exists (
select 0 x
From SO_SalesOrder_Line
join BASE_Product on BASE_Product.ProdId = SO_SalesOrder_Line.ProdId
join BASE_Category on BASE_Category.CategoryId = BASE_Product.CategoryId
where BASE_Category.Name = '1MHNZ'
and SO_SalesOrder_Line.SalesOrderId = SO_SalesOrder.SalesOrderId
)
group by l.Name
P.S.
Check also the dates columns, if they contains also time fraction you should reconsider your upper bound filter.
I suggest you to use and SO_SalesOrder.OrderDate < '01-03-2018' instead of <= 28-02

How to work with Multiple Joins without Duplicate Data SQL

I'm having an issue working with SQL data where once I have completed muptiple joins I am getting duplicate data.
Here is the code written for
SELECT RPPlannedLabor.PeriodHrs, RPPlannedLabor.StartDate, (RPAssignment.WBS1 + ' ' + PR.Name) AS 'WBS1', RPAssignment.WBS2, EM.FirstName, EM.LastName, EM.TKGroup, (EM.FirstName + ' ' + EM.LastName) AS 'Full Name'
FROM RPPlannedLabor
LEFT OUTER JOIN RPAssignment
ON RPPlannedLabor.AssignmentID = RPAssignment.AssignmentID
AND RPAssignment.WBS1 IS NOT NULL
LEFT OUTER JOIN EM
ON RPAssignment.ResourceID = EM.Employee
AND EM.Status = 'a'
LEFT OUTER JOIN PR
ON ((RPAssignment.WBS1 = PR.WBS1)
AND (ISNULL(RPAssignment.WBS2,0) = ISNULL(PR.WBS2,0))
AND (ISNULL(RPAssignment.WBS3,0) = ISNULL(PR.WBS3,0)))
AND PR.Sublevel = 'Y'
Any help would be greatly appreciated :)
I'd have to guess your isnull portions in the join is finding a bunch of null fields and cross joining, but thats just a guess. Data issues like this can't really be solved on a code help forum, best I can do is teach you to trouble shoot.
Run this and get the row count:
SELECT count(1)
FROM RPPlannedLabor
Run this
SELECT count(1)
FROM RPPlannedLabor
LEFT OUTER JOIN RPAssignment
ON RPPlannedLabor.AssignmentID = RPAssignment.AssignmentID
AND RPAssignment.WBS1 IS NOT NULL
Compare with first query...if count increase, your duplicate is on this first join.
Doesn't increase? Keep iterating, run this:
SELECT count(1)
FROM RPPlannedLabor
LEFT OUTER JOIN RPAssignment
ON RPPlannedLabor.AssignmentID = RPAssignment.AssignmentID
AND RPAssignment.WBS1 IS NOT NULL
LEFT OUTER JOIN EM
ON RPAssignment.ResourceID = EM.Employee
AND EM.Status = 'a'
Compare to your count above. Are there more records or is it the same? more records means this last join we added is causing them. If not...my guess is this here is causing the duplicates:
LEFT OUTER JOIN PR
ON ((RPAssignment.WBS1 = PR.WBS1)
AND (ISNULL(RPAssignment.WBS2,0) = ISNULL(PR.WBS2,0))
AND (ISNULL(RPAssignment.WBS3,0) = ISNULL(PR.WBS3,0)))
AND PR.Sublevel = 'Y'
If you are joining on fields with isnull functions, odds are there are nulls and potentially more than one...but I might be off as your data issue could be anywhere.

Where is the signature value read from in this query?

I have the following SQL query, and need to figure out where the "signatures" data is actually being read from. It's not from the 'claims' table, and doesn't seem to be from the 'questionnaire_answers' table. I believe it will be a boolean value, if that helps at all.
I'm reasonably proficient at SQL, but the joins have left me a bit confused.
(There's some PHP, but it's not relevant to the question).
$SQL="SELECT surveyor, COUNT(signed_total) AS 'total', SUM(signed_total) AS 'signed_total' FROM (
SELECT DISTINCT claims.claim_id, CONCAT(surveyors.user_first_name, CONCAT(' ', surveyors.user_surname)) AS 'surveyor', CASE WHEN signatures.claim_id IS NOT NULL THEN 1 ELSE 0 END AS 'signed_total' FROM claims
INNER JOIN users surveyors ON claims.surveyor_id = surveyors.user_id
LEFT OUTER JOIN signatures ON claims.claim_id = signatures.claim_id
INNER JOIN questionnaire_answers ON questionnaire_answers.claim_id = claims.claim_id
WHERE (claims.claim_type <> ".$conn->qstr(TYPE_DESKTOP).")
AND (claims.claim_type <> ".$conn->qstr(TYPE_AUDIT).")
AND (claims.claim_cancelled_id <= 0)
AND (claims.date_completed BETWEEN '".UK2USDate($start_date)." 00:00:00' AND '".UK2USDate($end_date)." 23:59:59')
) AS tmp
GROUP BY surveyor
ORDER BY surveyor ASC
";
Thank you!
signatures is a table (see LEFT OUTER JOIN signatures in your query).
As written in FROM clause :
FROM claims
INNER JOIN users surveyors ON claims.surveyor_id = surveyors.user_id
LEFT OUTER JOIN signatures ON claims.claim_id = signatures.claim_id
The LEFT keyword means that the rows of the left table are preserved; So all rows from claims table are considered and NULL marks are added as placeholders for the attributes from the nonpreserved side of the join which is signatures table here.
So CASE WHEN signatures.claim_id IS NOT NULL THEN 1 ELSE 0 END AS 'signed_total' basically checks that if a match between these two tables exists based on claim_id then signed_total column should have value 1 else 0.
Hope that helps!!

Using COALESCE with JOIN on a different database column

Trying to populate the location column of a query and was hoping that the use of the COALESCE function would help me get what I want.
SELECT OrderItem.Code AS ItemCode, MAX(COALESCE(OrderItem.Location, [Picklist].[dbo].[ItemData].InventoryLocation)) AS Location, SUM(OrderItem.Quantity) AS Quantity, MAX(Store.StoreName) AS Store
FROM OrderItem
INNER JOIN [Order] ON OrderItem.OrderID = [Order].OrderID
INNER JOIN [Store] ON [Order].StoreID = [Store].StoreID
LEFT JOIN [AmazonOrder] ON [AmazonOrder].OrderID = [Order].OrderID
JOIN [Picklist].[dbo].[ItemData] ON [Picklist].[dbo].[ItemData].[InventoryNumber] = [OrderItem].[Code]
WHERE (CASE WHEN [Order].[LocalStatus] = 'Recently Downloaded' AND [AmazonOrder].FulfillmentChannel = 2 THEN 1
WHEN [Order].[LocalStatus] = 'Recently Downloaded' AND [Store].StoreName != 'Amazon' THEN 1 ELSE 0 END ) = 1
GROUP BY OrderItem.Code
ORDER BY ItemCode
There will not be a location when the Store is Amazon so I need to Join on another table in another database. I don't believe I'm using this correctly. Also I do get the right Location results returned if I use :
SELECT InventoryLocation From [Picklist].[dbo].[ItemData] WHERE InventoryNumber = 'L1201-2W-EA'
Perhaps this is more like the query that you want:
SELECT oi.Code AS ItemCode, COALESCE(oi.Location, id.InventoryLocation) AS Location,
oi.Quantity, s.StoreName AS Store
FROM OrderItem oi INNER JOIN
[Order] o
ON oi.OrderID = o.OrderID INNER JOIN
[Store]
ON o.StoreID = s.StoreID LEFT JOIN
AmazonOrder ao
ON ao.OrderID = o.OrderID JOIN
[Picklist].[dbo].[ItemData] id
ON id.InventoryNumber = oi.[Code]
WHERE o.LocalStatus = 'Recently Downloaded' AND
(ao.FulfillmentChannel = 2 OR s.StoreName <> 'Amazon')
ORDER BY ItemCode
Here are the changes:
Removed the aggregation. It does not seem to be part of the question.
Introduced table aliases, so the query is easier to write and to read.
Simplified the logic in the where clause.
As the comment above says, the max seems somewhat strange, an arbitrary aggregation no doubt due to one of the joins bringing back more information than you might of expected.
Then the statement has a few issues:
The coalesce is using two fields, neither if which is in a left join, only the AmazonOrder is left joined, so that seems a bit strange, that would only work if the first field in the coalesce (OrderItem.Location) is nullable - which it might be, there is no schema posted.
The left join itself is an inner join in disguise at present - within the where clause you have given explicit conditions on a field from that table - AND [AmazonOrder].FulfillmentChannel = 2 - if the record was actually missing the left join would return null for that field, and the where clause would then drop it out of the results. If you want this to properly work as a left join, any condition on fields from that table must move into the join condition, or the where clause itself must allow for that field being null (explicitly or using a coalesce.)
SELECT OrderItem.Code AS Code,
CASE WHEN (LEN(ISNULL(MAX([OrderItem].[Location]),'')) = 1)
THEN MAX([OrderItem].[Location])
ELSE MAX([Picklist].[dbo].[ItemData].InventoryLocation)
END AS Location,
SUM(OrderItem.Quantity) AS Quantity,
MAX(Store.StoreName) AS Store
FROM OrderItem
INNER JOIN [Order] ON OrderItem.OrderID = [Order].OrderID
INNER JOIN [Store] ON [Order].StoreID = [Store].StoreID
LEFT JOIN [AmazonOrder] ON [AmazonOrder].OrderID = [Order].OrderID
LEFT JOIN [Picklist].[dbo].[ItemData] ON [Picklist].[dbo].[ItemData].[InventoryNumber] = [OrderItem].[Code] OR
[Picklist].[dbo].[ItemData].[MediaCreator] = [OrderItem].[Code]
WHERE [Order].LocalStatus = 'Recently Downloaded' AND (AmazonOrder.FulfillmentChannel = 2 OR Store.StoreName <> 'Amazon')
GROUP BY OrderItem.Code
ORDER BY OrderItem.Code
Decided to go with case statement on location column route because I could not get COALESCE to work for me. Schema, some not all data, at SQLFiddle.
I guess if someone gets COALESCE to work I'll change the answer?
#Gordon Linoff I used the re-written WHERE clause because it looked cleaner than using the CASE statement. It worked and guessed there was a simpler way to go about it but was more worried about getting COALESCE to work. As for the Aliases sometimes I like to use them but in this case since there was a lot of tables I like to code out what I'm actually working in. Just my preference .