I have run into a very strange problem where a query is returning most rows but not all of them. If I add an addition condition to my where clause, the missing row appears.
Below is my query:
SELECT T1.Table1ID,
SUM(ISNULL([T4].Qty,0)) TotalQty
FROM dbo.Table1 (NOLOCK) T1
INNER JOIN [dbo].Table2 (NOLOCK) T2 ON T1.Table2ID = T2.Table2ID
INNER JOIN [dbo].Table3 (NOLOCK) T3 ON T2.DocTypeID = T3.DocTypeID
INNER JOIN dbo.Table4 (NOLOCK) T4 ON T4.Table1ID = T1.Table1ID
WHERE T3.is1 = 0
GROUP BY T1.Table1ID
ORDER BY T1.Table1ID
This returns all rows except for the one with T1.Table1ID = 185671. If I add it as a filter like such:
SELECT T1.Table1ID,
SUM(ISNULL([T4].Qty,0)) TotalQty
FROM dbo.Table1 (NOLOCK) T1
INNER JOIN [dbo].Table2 (NOLOCK) T2 ON T1.Table2ID = T2.Table2ID
INNER JOIN [dbo].Table3 (NOLOCK) T3 ON T2.DocTypeID = T3.DocTypeID
INNER JOIN dbo.Table4 (NOLOCK) T4 ON T4.Table1ID = T1.Table1ID
WHERE T3.is1 = 0
AND T1.Table1ID = 185671
GROUP BY T1.Table1ID
ORDER BY T1.Table1ID
Now the row shows. Any ideas?
EDIT: New query after removing NOLOCK hint.
SELECT T1.Table1ID,
SUM(ISNULL([T4].Qty,0)) TotalQty
FROM dbo.Table1 T1
INNER JOIN [dbo].Table2 T2 ON T1.Table2ID = T2.Table2ID
INNER JOIN [dbo].Table3 T3 ON T2.DocTypeID = T3.DocTypeID
INNER JOIN dbo.Table4 T4 ON T4.Table1ID = T1.Table1ID
WHERE T3.is1 = 0
AND T1.Table1ID = 185671
GROUP BY T1.Table1ID
ORDER BY T1.Table1ID
Related
My Query IS
SELECT TblPharmacyBillingDetails.UPBNo, TblMasterBillingData.IPDNo, InPatRegistration.PatTitle+PatientName, TblPharmacyBillingDetails.InvoiceNo, TblPharmacyBillingDetails.InvoiceDateTime, TblPharmacyBillingDetails.BillingAmount
FROM TblPharmacyBillingDetails
INNER JOIN TblMasterBillingData ON TblPharmacyBillingDetails.UPBNo = TblMasterBillingData.UPBNo
INNER JOIN InPatRegistration ON TblMasterBillingData.IPDNo = InPatRegistration.IPDNo
but if TblMasterBillingData.IPDNo value is NULL select Data From TblMasterBillingData.OPDNo and
INNER JOIN OutPatRegistration ON TblMasterBillingData.OPDNo = OutPatRegistration.IPDNo
Method #1: Using UNION
SELECT * FROm
(
SELECT TblPharmacyBillingDetails.UPBNo,
TblMasterBillingData.IPDNo,
InPatRegistration.PatTitle+PatientName,
TblPharmacyBillingDetails.InvoiceNo,
TblPharmacyBillingDetails.InvoiceDateTime,
TblPharmacyBillingDetails.BillingAmount
FROM TblPharmacyBillingDetails
INNER JOIN TblMasterBillingData ON TblPharmacyBillingDetails.UPBNo = TblMasterBillingData.UPBNo
INNER JOIN InPatRegistration ON TblMasterBillingData.IPDNo = InPatRegistration.IPDNo
WHERE TblMasterBillingData.IPDNo IS NOT NULL
UNION ALL
SELECT TblPharmacyBillingDetails.UPBNo,
TblMasterBillingData.OPDNo,
OutPatRegistration .PatTitle + PatientName,
TblPharmacyBillingDetails.InvoiceNo,
TblPharmacyBillingDetails.InvoiceDateTime,
TblPharmacyBillingDetails.BillingAmount
FROM TblPharmacyBillingDetails
INNER JOIN TblMasterBillingData ON TblPharmacyBillingDetails.UPBNo = TblMasterBillingData.UPBNo
INNER JOIN OutPatRegistration ON TblMasterBillingData.OPDNo = OutPatRegistration.OPDNo
WHERE TblMasterBillingData.OPDNo IS NOT NULL
)Tmp
ORDER BY TblPharmacyBillingDetails.UPBNo
Method #2 Using ISNULL and LEFT JOIN
SELECT TblPharmacyBillingDetails.UPBNo,
ISNULL(TblMasterBillingData.IPDNo,TblMasterBillingData.OPDNo),
ISNULL(IP.PatTitle + IP.PatientName, OP.PatTitle + OP.PatientName),
TblPharmacyBillingDetails.InvoiceNo,
TblPharmacyBillingDetails.InvoiceDateTime,
TblPharmacyBillingDetails.BillingAmount
FROM TblPharmacyBillingDetails
INNER JOIN TblMasterBillingData ON TblPharmacyBillingDetails.UPBNo = TblMasterBillingData.UPBNo
LEFT JOIN InPatRegistration IP ON TblMasterBillingData.IPDNo = IP.IPDNo
LEFT JOIN outPatRegistration OP ON TblMasterBillingData.OPDNo = OP.OPDNo
ORDER BY TblPharmacyBillingDetails.UPBNo
You can write either case statement or ISNULL() function as shown below in the demo query.
SELECT
Orders.OrderID,
Case when Customers1.CustomerName is null then Customers2.CustomerName else Customers1.CustomerName
end as CustomerName, --way 1
ISNULL(Customers1.CustomerName, Customers2.CustomerName) as Customer, --way 2
Orders.OrderDate
FROM Orders
INNER JOIN Customers1 ON Orders.CustomerID = Customers1.CustomerID
INNER JOIN Customers2 ON Orders.CustomerID = Customers2.CustomerID
-- where your condition here
-- order by your column name
You can also check whether data is available or not in the table and join the table accordingly using if exists as shown below.
if exists(select 1 from tablename where columnname = <your values>)
I have a SQL query that I'm trying to optimize.
Is there a better way to avoid using subquery here?
Got a suggestion on using Row_number(),
posting this with some corrections
DECLARE #curdate DATETIME
SET #curdate = GETDATE()
SELECT DISTINCT
SIS.StudentID, StudentCoverage.StudentCoverageDataID,
Student.FirstName, Student.LastName,
Student.DateOfBirth, Student.Gender,
ASMT.AssessmentDate
FROM
SIS (NOLOCK)
INNER JOIN
SISMaster (NOLOCK) ON SISMaster.SISID = SIS.SISID
INNER JOIN
Assessment ASMT ON SIS.StudentID = ASMT.StudentId
INNER JOIN
StudentCoverage (NOLOCK) ON StudentCoverage.StudentID = SIS.StudentID
INNER JOIN
Organization (NOLOCK) ON StudentCoverage.OrgID = Organization.OrganizationID
INNER JOIN
Student (NOLOCK) ON Student.StudentID = SIS.StudentID
INNER JOIN
StudentCoverageData (NOLOCK) ON StudentCoverageData.StudentCoverageID = StudentCoverage.StudentCoverageID
AND StudentCoverageData.StudentCoverageDataID = (SELECT TOP 1 StudentCoverageData.StudentCoverageDataID
FROM StudentCoverage
INNER JOIN StudentCoverageData ON StudentCoverageData.StudentCoverageID = StudentCoverage.StudentCoverageID
WHERE StudentCoverage.StudentId = SIS.StudentID
AND StudentCoverageData.Active = 1
AND StudentCoverageData.EffectiveDate <= #curdate
AND (StudentCoverageData.ExitDate IS NULL OR StudentCoverageData.ExitDate > #curdate)
ORDER BY StudentCoverageData.AsOfDate DESC)
All Tables in your subquery is exists in inner join clause, so you could rewrite your query like this:
;WITH temps AS
(
DECLARE #curdate DATETIME = GETDATE()
SELECT
SIS.StudentID, StudentCoverage.StudentCoverageDataID,
Student.FirstName, Student.LastName,
Student.DateOfBirth, Student.Gender,
ASMT.AssessmentDate,
ROW_NUMBER() OVER (PARTITION BY StudentCoverageData.StudentCoverageDataID ORDER BY StudentCoverageData.AsOfDate) AS RowIndex
FROM
SIS (NOLOCK)
INNER JOIN
SISMaster (NOLOCK) ON SISMaster.SISID = SIS.SISID
INNER JOIN
StudentCoverage (NOLOCK) ON StudentCoverage.StudentID = SIS.StudentID
INNER JOIN
Organization (NOLOCK) ON StudentCoverage.OrgID = Organization.OrganizationID
INNER JOIN
Student (NOLOCK) ON Student.StudentID = SIS.StudentID
INNER JOIN
StudentCoverageData (NOLOCK) ON StudentCoverageData.StudentCoverageID = StudentCoverage.StudentCoverageID
WHERE StudentCoverageData.Active = 1
AND StudentCoverageData.EffectiveDate <= #curdate
AND (StudentCoverageData.ExitDate IS NULL OR StudentCoverageData.ExitDate > #curdate)
)
SELECT * FROM temps t
WHERE t.RowIndex = 1
I have a sales query by date range, where the date range is defined by user input. I would like to divide the results by day. i.e.: say the user input the date range from 01/01/16 - 01/15/16, I would like the break the results for each day.
I'm using DATENAME(DD,T1.[DocDate]) to break it, and it is kind of working, but the results are no accurate. I figure I have to use the same break in the Returns subquery. Please see the full query below:
Thank you
SELECT
'2016' as 'Year',
t4.remarks as 'Department',
DATENAME(DD,T1.[DocDate]) as 'Day',
sum(t0.[quantity])-(ISNULL(p.quantity,0)) as 'Quantity',
sum(t0.linetotal - t0.linetotal*t1.discprcnt/100)-(ISNULL(p.total,0)) as 'Total',
sum(T0.[GrssProfit])-(ISNULL(p.profit,0)) as 'Profit $',
(sum(T0.[GrssProfit])-(ISNULL(p.profit,0)))/(sum(t0.linetotal - t0.linetotal*t1.discprcnt/100)-(ISNULL(p.total,0)))*100 as 'Profit%'
FROM INV1 T0 with (nolock)
INNER JOIN OINV T1 with (nolock) on t0.docentry = t1.docnum
INNER JOIN OSLP T2 with (nolock) on t0.SlpCode = t2.SlpCode
LEFT JOIN OHEM T3 with (nolock) on t0.slpcode = t3.SalesPrson
LEFT JOIN OUDP T4 with (nolock) on t3.dept = t4.Code
--BEGINS QUERY FOR THE RETURNS--
left join (select t9.name as 'dept',sum(t5.quantity) as 'quantity',sum(t5.linetotal - t5.linetotal*t6.discprcnt/100) as 'total',sum(t5.grssprofit) as 'profit'
from [dbo].[rin1] t5 with (nolock)
inner join orin t6 with (nolock) on t5.docentry = t6.docentry
INNER JOIN OSLP T7 with (nolock) on t5.SlpCode = t7.SlpCode
LEFT JOIN OHEM T8 with (nolock) on t5.slpcode = t8.SalesPrson
LEFT JOIN OUDP T9 with (nolock) on t8.dept = t9.Code
INNER JOIN OITM T10 with (nolock) on t5.itemcode = t10.itemcode
where t5.docdate between '[%1]' and '[%2]' and t10.invntitem = 'Y'
and (t5.linetotal - (t5.linetotal*t6.discprcnt/100)) <> '0'
group by t9.name) p on p.dept = t4.name
--ENDS QUERY FOR THE RETURNS--
WHERE t1.docdate between '[%1]' and '[%2]'
and t4.remarks is not null
and t4.remarks = 'perfume provider'
and (t0.linetotal - (t0.linetotal*t1.discprcnt/100)) <> '0'
group by DATENAME(DD,T1.[DocDate]),t4.remarks,p.quantity,p.total,p.profit
Instead of a sub-query for your returns (with the same kind of group by as your Invoices query), you should use a UNION instead. Your Group-By is fine, but like you mentioned, your subquery is going to result in bad data.
Any time you have distinct "starting points" for your data, a Union is the way to go.
I have a performance issue regarding a quite simple query that run for more than 30 minutes:
SELECT P.pID
,COUNT(T1.ID) AS NB1
,COUNT(T2.ID) AS NB2
,COUNT(T3.ID) AS NB3
,COUNT(T4.ID) AS NB4
,COUNT(T5.ID) AS NB5
FROM MainTable P
LEFT OUTER JOIN Table1 T1 ON P.pID = T1.pID
LEFT OUTER JOIN Table2 T2 ON P.pID = T2.pID
LEFT OUTER JOIN Table3 T3 ON P.pID = T3.pID
LEFT OUTER JOIN Table4 T4 ON P.pID = T4.pID
LEFT OUTER JOIN Table5 T5 ON P.pID = T5.pID
GROUP BY P.pID
Where as each query would reply in few ms:
ex.
SELECT P.pID
,COUNT(T1.ID) AS NB1
FROM MainTable P
LEFT OUTER JOIN Table1 T1 ON P.pID = T1.pID
GROUP BY P.pID
If I don't use any aggregation (COUNT or anything else) the query run in few ms:
ex.
SELECT P.pID
FROM MainTable P
LEFT OUTER JOIN Table1 T1 ON P.pID = T1.pID
LEFT OUTER JOIN Table2 T2 ON P.pID = T2.pID
LEFT OUTER JOIN Table3 T3 ON P.pID = T3.pID
LEFT OUTER JOIN Table4 T4 ON P.pID = T4.pID
LEFT OUTER JOIN Table5 T5 ON P.pID = T5.pID
GROUP BY P.pID
Obviously all indexes are set etc...
The only "slowing down" element is that pID is a varchar (50) but I can't change it and in my opinion this is not be the main problem here.
I used a workaround including union all that work fine but I really wondering why these is so long and how could I optimize this as aggregating over multiple left join are really common stuff in reporting project and should not be so slow.
thank you for your help.
[EDIT]
thx to ARION i got a nice query working really nice.
But my main concern is about understanding what's wrong in the sql engine writing the query with multiple left join.
Tables descr would be :
Table P (500 rows)
pID varchar(50) NOT NULL as primary key
p.* doesn't matter
Table Tn (between 2000 and 8000 rows)
Tn.ID int NOT NULL as primary key
pID varchar(50) NOT NULL as Foreign key
[EDIT] Thanks to Erland Sommarskog on social.msdn.microsoft.com that point me my error of analyse.
-- detail about the answer
Keep in mind :
LEFT JOIN forms cartesian product
I was wrong assuming that the Cartesian product might have been filtered as I always refere to the same table.
thanks
Maybe something like this:
SELECT
P.pID,
(SELECT COUNT(*) FROM Table1 T1 WHERE P.pID = T1.pID) AS NB1,
(SELECT COUNT(*) FROM Table2 T2 WHERE P.pID = T2.pID) AS NB2,
(SELECT COUNT(*) FROM Table3 T3 WHERE P.pID = T3.pID) AS NB3,
(SELECT COUNT(*) FROM Table4 T4 WHERE P.pID = T4.pID) AS NB4,
(SELECT COUNT(*) FROM Table5 T5 WHERE P.pID = T5.pID) AS NB5
FROM MainTable P
You could also rewrite the query by first grouping by (in subqueries), then joining:
SELECT
P.pID,
T1.NB1,
T2.NB2,
T3.NB3,
T4.NB4,
T5.NB5
FROM MainTable P
LEFT JOIN
(SELECT pID, COUNT(*) AS NB1 FROM Table1 GROUP BY pID) AS T1
ON T1.pID = P.pID
LEFT JOIN
(SELECT pID, COUNT(*) AS NB2 FROM Table2 GROUP BY pID) AS T2
ON T2.pID = P.pID
LEFT JOIN
(SELECT pID, COUNT(*) AS NB3 FROM Table3 GROUP BY pID) AS T3
ON T3.pID = P.pID
LEFT JOIN
(SELECT pID, COUNT(*) AS NB4 FROM Table4 GROUP BY pID) AS T4
ON T4.pID = P.pID
LEFT JOIN
(SELECT pID, COUNT(*) AS NB5 FROM Table5 GROUP BY pID) AS T5
ON T5.pID = P.pID
This would be useful if you want to include in the results other aggregates, besides the COUNT(*), without having to run more correlated subqueries.
I don't want to use "not in" this sql query. How can I do it? thanks
SELECT
T2.Sno,
T2.Name,
T1.description,
T2.UserCode
FROM
Table1 AS T1 (nolock)
INNER JOIN T2 (nolock)
ON T1.UserCode = T2.UserCode
WHERE
g.xid= #p_xid
and T2.Sno not in (select Gid from T3 (nolock))
Assuming there is no row in T2 where Sno is null and in T3 where Gid is null:
SELECT
T2.Sno,
T2.Name,
T1.description,
T2.UserCode
FROM
Table1 AS T1 WITH (nolock)
INNER JOIN T2 WITH (nolock)
LEFT JOIN T3 WITH (NOLOCK)
ON T2.Sno = T3.Gid
ON T1.UserCode = T2.UserCode
WHERE
g.xid= #p_xid
and T3.Gid IS NULL
If you have multiple T3 rows per T2.Sno = T3.Gid, you'll need DISTINCT in a JOIN.
Without DISTINCT, it's a different query
With DISTINCT, it's an extra step.
I'd use NOT EXISTS which avoids this.
SELECT
T2.Sno,
T2.Name,
T1.description,
T2.UserCode
FROM
Table1 AS T1 (nolock)
INNER JOIN T2 (nolock)
ON T1.UserCode = T2.UserCode
WHERE
g.xid= #p_xid
and not exists (select * from T3 (nolock) where T3.Gid = T2.Sno)