Avoid SQL Pivot returning duplicate rows - sql

I have the following SQL script which returns duplciate values in PIVOT. How do I combine those duplicate records to one row.
Please check the below image for the results set.
SELECT *
FROM (SELECT X.stockcode,
X.description,
X.pack,
X.location,
X.lname,
X.qty,
Y.stockcode AS StockCode2,
y.periodname,
Y.months,
Y.saleqty
FROM (SELECT dbo.stock_items.stockcode,
dbo.stock_items.description,
dbo.stock_items.pack,
dbo.stock_loc_info.location,
dbo.stock_locations.lname,
dbo.stock_loc_info.qty
FROM dbo.stock_locations
INNER JOIN dbo.stock_loc_info
ON dbo.stock_locations.locno = dbo.stock_loc_info.location
LEFT OUTER JOIN dbo.stock_items
ON dbo.stock_loc_info.stockcode = dbo.stock_items.stockcode
WHERE ( dbo.stock_items.status = 's' )) AS X
LEFT OUTER JOIN (SELECT dbo.dr_invlines.stockcode,
( 12 + Datepart(month, Getdate()) - Datepart(month, dbo.dr_trans.transdate) ) % 12 + 1 AS Months,
Sum(dbo.dr_invlines.quantity) AS SaleQty,
dbo.period_status.periodname
FROM dbo.dr_trans
INNER JOIN dbo.period_status
ON dbo.dr_trans.period_seqno = dbo.period_status.seqno
LEFT OUTER JOIN dbo.stock_items AS STOCK_ITEMS_1
RIGHT OUTER JOIN dbo.dr_invlines
ON STOCK_ITEMS_1.stockcode = dbo.dr_invlines.stockcode
ON dbo.dr_trans.seqno = dbo.dr_invlines.hdr_seqno
WHERE ( STOCK_ITEMS_1.status = 'S' )
AND ( dbo.dr_trans.transtype IN ( 1, 2 ) )
AND ( dbo.dr_trans.transdate >= Dateadd(m, -6, Getdate()) )
GROUP BY dbo.dr_invlines.stockcode,
Datepart(month, dbo.dr_trans.transdate),
dbo.period_status.periodname) AS Y
ON X.stockcode = Y.stockcode) z
PIVOT (Sum(saleqty) FOR [months] IN ([1],[2],[3],[4],[5],[6])) AS pivoted

EDIT: I missed the root-cause of your issue being the inclusion of the periodname column causing the percieved duplication. I am leaving this in place as general solution showing CTE usage, because it could still be useful if you then want to do extra filtering/transformation of your pivot results
One way is to take the results of the pivot query and run it through a SELECT DISTINCT query.
An example of wrapping your pivot query as a CTE and using it to feed a SELECT DISTINCT below (please note: untested, but parses as valid in my SSMS)
WITH PivotResults_CTE (
stockcode,
description,
pack,
location,
lname,
qty,
StockCode2,
periodname,
months,
saleqty
)
AS (
SELECT *
FROM (
SELECT X.stockcode
,X.description
,X.pack
,X.location
,X.lname
,X.qty
,Y.stockcode AS StockCode2
,y.periodname
,Y.months
,Y.saleqty
FROM (
SELECT dbo.stock_items.stockcode
,dbo.stock_items.description
,dbo.stock_items.pack
,dbo.stock_loc_info.location
,dbo.stock_locations.lname
,dbo.stock_loc_info.qty
FROM dbo.stock_locations
INNER JOIN dbo.stock_loc_info ON dbo.stock_locations.locno = dbo.stock_loc_info.location
LEFT OUTER JOIN dbo.stock_items ON dbo.stock_loc_info.stockcode = dbo.stock_items.stockcode
WHERE (dbo.stock_items.STATUS = 's')
) AS X
LEFT OUTER JOIN (
SELECT dbo.dr_invlines.stockcode
,(12 + Datepart(month, Getdate()) - Datepart(month, dbo.dr_trans.transdate)) % 12 + 1 AS Months
,Sum(dbo.dr_invlines.quantity) AS SaleQty
,dbo.period_status.periodname
FROM dbo.dr_trans
INNER JOIN dbo.period_status ON dbo.dr_trans.period_seqno = dbo.period_status.seqno
LEFT OUTER JOIN dbo.stock_items AS STOCK_ITEMS_1
RIGHT OUTER JOIN dbo.dr_invlines ON STOCK_ITEMS_1.stockcode = dbo.dr_invlines.stockcode ON dbo.dr_trans.seqno = dbo.dr_invlines.hdr_seqno WHERE (STOCK_ITEMS_1.STATUS = 'S')
AND (
dbo.dr_trans.transtype IN (
1
,2
)
)
AND (dbo.dr_trans.transdate >= Dateadd(m, - 6, Getdate()))
GROUP BY dbo.dr_invlines.stockcode
,Datepart(month, dbo.dr_trans.transdate)
,dbo.period_status.periodname
) AS Y ON X.stockcode = Y.stockcode
) z
PIVOT(Sum(saleqty) FOR [months] IN (
[1]
,[2]
,[3]
,[4]
,[5]
,[6]
)) AS pivoted
)
SELECT DISTINCT *
FROM
PivotResults_CTE
;
Also note, your sql included in the above may look slightly different to your original but that is only because i ran it through a reformatter to ensure i understood the structure of it.
In other words, the basic CTE wrapper for your pivot query is:
WITH PivotResults_CTE (
Field1,
Field2,
...
)
AS (
YOUR_PIVOT_QUERY_HERE
)
SELECT DISTINCT *
FROM
PivotResults_CTE
;

Related

Turning an outer apply into a left join when you reference parent aliases

I'm currently trying to turn an outer apply into a left join to save some complexity.
SELECT *
FROM fact_table h
OUTER APPLY (SELECT TOP 1
*
FROM dimension mcc WITH (NOLOCK)
WHERE h.product = mcc.product
AND h.country = mcc.country
AND mcc.date IN (SELECT MAX(date)
FROM dimension dd WITH (NOLOCK)
WHERE FORMAT(DATEADD(MONTH, -3, dd.date), 'yyyyMM') <= h.month_in_the_year
AND dd.product = h.product
AND dd.country = h.country)) a;
I basically use it to get the related data from Dimension linked with the latest data point that's earlier than 3 months ago.
I'm trying to turn it into a left join, but it's taking a lot more time since I don't filter the dimension before the join :
SELECT TOP 10
*
FROM fact_table h
LEFT JOIN dimension a ON h.product = a.product
AND h.country = a.country
AND a.pkid = (SELECT TOP 1
pkid
FROM dimension dd
WHERE FORMAT(DATEADD(MONTH, -3, dd.date), 'yyyyMM') <= h.month_in_the_year
ORDER BY date DESC);
Do you have an idea on how to turn it efficiently into a left join ?
It looks like you can significantly simplify this query, by simply adding an ORDER BY. I've also modified the date filter in order to leverage indexing properly.
SELECT *
FROM fact_table h
OUTER APPLY (
SELECT TOP 1 *
FROM dimension mcc
WHERE h.product = mcc.product
AND h.country = mcc.country
AND mcc.date < DATEADD(MONTH, 2, DATEFROMPARTS(LEFT(h.month_in_the_year, 4), RIGHT(h.month_in_the_year, 2), 1))
ORDER BY mcc.date DESC
) a;
To transform this into a LEFT JOIN, you need to utilize row-numbering
SELECT *
FROM (
SELECT *,
rn = ROW_NUMBER() OVER (PARTITION BY h.PrimaryKeyColumn ORDER BY mcc.date)
FROM fact_table h
LEFT JOIN dimension mcc
ON h.product = mcc.product
AND h.country = mcc.country
AND mcc.date < DATEADD(MONTH, 2, DATEFROMPARTS(LEFT(h.month_in_the_year, 4), RIGHT(h.month_in_the_year, 2), 1))
) a
WHERE rn = 1;

How to Reduce Execution Time

It's just my startup in SQL, I have 4.5 GB database from that I am trying to select records from specific dates and its 25 seconds execute. Is there a way to make it faster?
My query:
select a.*,b.ModelName
from Validation a
left Join ModelMaster b
on SUBSTRING(a.STB,11,2) = b.ProductCode and
Convert(int,substring(a.STB,9,2)) = b.BrandCode
Where STB in (Select STB From Scan_Data_For_Century) and
a.date between '2015-01-14' and '2015-01-14'
You can try query like this:
SELECT a.*,
b.ModelName
FROM Scan_Data_For_Century sd
INNER JOIN Validation a
ON a.STB = sd.STB AND a.date between '2015-01-14' and '2015-01-14'
LEFT JOIN ModelMaster b
ON SUBSTRING(a.STB,11,2) = b.ProductCode AND Convert(int,substring(a.STB,9,2)) = b.BrandCode
I know, that must be a comment. Please, try query. If it wont help I will remove my answer.
Try this:
;with cte as (
select
*,
ProductCode = SUBSTRING( STB, 11, 2 ),
BrandCode = Convert( int, substring( STB, 9, 2 ) )
from Validation
)
select
a.*,
c.ModelName
from
cte a
inner join Scan_Data_For_Century b on ( b.STB = a.STB )
left Join ModelMaster c on ( c.ProductCode = b.ProductCode ) and ( c.BrandCode = b.BrandCode )
Where
( a.date = '2015-01-14' )
Also indexes on:
Validation.STB
Validation.Date
Scan_Data_For_Century.STB
ModelMaster.ProductCode
ModelMaster.BrandCode
might be helpfull

Top 1 Item from the List in SQL

I have the following scenario
I am getting Output like this
Month Product Name Amount
Jan-2014 A-Prodcut 50
Jan-2014 B-Product 45
Jan-2014 C-Product 55
Feb-2014 A-Prodcut 60
Feb-2014 B-Product 48
Feb-2014 C-Product 80
I want Output like this :
Jan-2014 C-Product 55
Feb-2014 C-Product 80
I want top product from each month How to achieve this
Here is my query
;With cte as
(
SELECT #EndDate AS TheMonth, 0 as [Counter]
UNION ALL
SELECT DATEADD(mm,-[Counter] -1,#EndDate), Counter + 1 AS TheMonth
from cte
WHERE DATEADD(mm,-[Counter] -1,#EndDate) >= #StartDate
)
SELECT
left(DATENAME(MM,TheMonth),3) +'-'+ cast(year(TheMonth) as varchar) AS [Month-Year],
ISNULL(IM.product_name,'N/A') as 'Product Name',
isnull(sum((ism.selling_price * siim.qty) + (((tm.tax_amount*(ism.selling_price * siim.qty))/100))),0) AS Amount
FROM
cte
LEFT OUTER JOIN RS_Sell_Order_Master AS SM on MONTH(invoice_date) = MONTH(TheMonth)
AND YEAR(invoice_date) = YEAR(TheMonth)
AND sm.is_approved = 1
LEFT OUTER JOIN RS_Sells_Invoice_Info_Master AS SIIM ON SM.sell_order_no = SIIM.sell_order_no
LEFT OUTER JOIN RS_Inventory_Master AS IM ON SIIM.product_id = IM.product_id
LEFT OUTER JOIN RS_Tax_Master AS TM ON TM.tax_id = SIIM.tax_id
LEFT OUTER JOIN RS_Inventory_Selling_Master AS ISM ON ISM.selling_product_id = SIIM.selling_product_id
GROUP BY
IM.product_name,
TheMonth
Use Row_number() in the select query ie.
SELECT MONTH, PRODUCT_NAME, AMOUNT
FROM (
SELECT MONTH, PRODUCT_NAME, AMOUNT,
ROW_NUMBER ( )
OVER (PARTITION BY Month order by amount Desc) AS RANK
)
WHERE RANK = 1
Hope this helps..
Try this:
SELECT *
FROM
(
SELECT [Month-Year], [Product Name], Amount, ROW_NUMBER() OVER (Partition BY [Month-Year] ORDER BY Amount DESC) as rn
FROM
(
SELECT
left(DATENAME(MM,TheMonth),3) +'-'+ cast(year(TheMonth) as varchar) AS [Month-Year],
ISNULL(IM.product_name,'N/A') as 'Product Name',
isnull(sum((ism.selling_price * siim.qty) + (((tm.tax_amount*(ism.selling_price * siim.qty))/100))),0) AS Amount
FROM
cte
LEFT OUTER JOIN RS_Sell_Order_Master AS SM on MONTH(invoice_date) = MONTH(TheMonth)
AND YEAR(invoice_date) = YEAR(TheMonth)
AND sm.is_approved = 1
LEFT OUTER JOIN RS_Sells_Invoice_Info_Master AS SIIM ON SM.sell_order_no = SIIM.sell_order_no
LEFT OUTER JOIN RS_Inventory_Master AS IM ON SIIM.product_id = IM.product_id
LEFT OUTER JOIN RS_Tax_Master AS TM ON TM.tax_id = SIIM.tax_id
LEFT OUTER JOIN RS_Inventory_Selling_Master AS ISM ON ISM.selling_product_id = SIIM.selling_product_id
GROUP BY
IM.product_name,
TheMonth
) a
)b
WHERE rn = 1
Usually OUTER APPLY or CROSS APPLY is a way to go in theese situations.
So your SELECT would be like this:
;With cte as
(
SELECT #EndDate AS TheMonth, 0 as [Counter]
UNION ALL
SELECT DATEADD(mm,-[Counter] -1,#EndDate), Counter + 1 AS TheMonth
from cte
WHERE DATEADD(mm,-[Counter] -1,#EndDate) >= #StartDate
)
SELECT
left(DATENAME(MM,TheMonth),3) +'-'+ cast(year(TheMonth) as varchar) AS [Month-Year],
'Product Name', Amount
FROM
cte
OUTER APPLY ( SELECT TOP 1
ISNULL(IM.product_name,'N/A') as 'Product Name',
isnull(sum((ism.selling_price * siim.qty) +
(((tm.tax_amount*(ism.selling_price * siim.qty))/100))),0) AS Amount
FROM RS_Sell_Order_Master AS SM
LEFT OUTER JOIN RS_Sells_Invoice_Info_Master AS SIIM
ON SM.sell_order_no = SIIM.sell_order_no
LEFT OUTER JOIN RS_Inventory_Master AS IM
ON SIIM.product_id = IM.product_id
LEFT OUTER JOIN RS_Tax_Master AS TM
ON TM.tax_id = SIIM.tax_id
LEFT OUTER JOIN RS_Inventory_Selling_Master AS ISM
ON ISM.selling_product_id = SIIM.selling_product_id
WHERE MONTH(invoice_date) = MONTH(TheMonth) AND YEAR(invoice_date) = YEAR(TheMonth) AND sm.is_approved = 1
ORDER BY Amount DESC
) x
GROUP BY
'Product Name',
TheMonth

If Exists inside a CTE in SQl Server

I just want to know that How to write a CTE containing If Exists in SQl Server ?
I had tried to write a CTE below where i am Using If Exists Statement to select weather the record exist or not .In case if the record does not exist then i am assigning default value but i am getting error
'Incorrect syntax near the keyword 'if'
.' Please help me to fix this error and guide me to write this CTE.
Please find below the CTE which i had written:-
Alter procedure St_Proc_GetTeamProductionReport
#mindate DateTime,
#maxdate DateTIme,
#userID varchar(50)
as
Begin
set NoCount on;
with
ProductionCTE(CalendarDate,RoleID,UserID,UserECode,UserName,ImmediateSupervisor,NatureOfWorkName,RegionProjectName,CountyName,WorkTypeName,TaskName,VolumneProcessed,TimeSpent,Comment)
as
(
if exists
(
select P.CalendarDate,U.RoleID,U.UserID,U.UserECode,U.UserName,U.ImmediateSupervisor,N.NatureofWorkName,
R.RegionProjectName,C.Countyname,W.WorktypeName,T.TaskName,P.VolumeProcessed,P.Timespent,P.Comment
from production P inner join NatureOfWork N
on N.NatureofWorkID=P.natureofworkid
inner join dbo.RegionAndProjectInfo R
on R.RegionProjectID=P.RegionProjectID
inner join county C
on C.countyid=P.countyid
inner join worktype W
on W.Worktypeid=P.worktypeID
inner join task T
on T.taskid=P.TaskID
inner join UserInfo U
on U.Userid=P.userid
where P.userid=#userID and ( convert(varchar, P.CalendarDate, 101) ) between (
convert(varchar, #mindate, 101) ) and ( convert(varchar, #maxdate, 101) )
)
else
(
Select '2012-09-14 13:41:52' as CalendarDate,
2 as RoleID,'938' as UserID,
(select Userecode from Userinfo where userid=#userID) as UserECode,
(select UserName from Userinfo where userid=#userID)as UserName,
(select ImmediateSupervisor from Userinfo where userid=#userID)as ImmediateSupervisor,
'BP' as NatureOfWorkName,
'CO Processing' as RegionProjectName,
'Adams' as CountyName,
'Quality' as WorkTypeName,
'Corrections ' as TaskName,
5 as VolumneProcessed,
'01:00' as TimeSpent,
'test' as Comment
)
union all
select P.CalendarDate,U.RoleID,U.UserID,U.UserECode,U.UserName,U.ImmediateSupervisor,N.NatureofWorkName,
R.RegionProjectName,C.Countyname,W.WorktypeName,T.TaskName,P.VolumeProcessed,P.Timespent,P.Comment
from production P inner join NatureOfWork N
on N.NatureofWorkID=P.natureofworkid
inner join dbo.RegionAndProjectInfo R
on R.RegionProjectID=P.RegionProjectID
inner join county C
on C.countyid=P.countyid
inner join worktype W
on W.Worktypeid=P.worktypeID
inner join task T
on T.taskid=P.TaskID
inner join UserInfo U
on U.Userid=P.userid
inner join ProductionCTE
on U.ImmediateSupervisor=ProductionCTE.UserECode
where P.IsTaskCompleted=1 and ( convert(varchar, P.CalendarDate, 101) ) between (
convert(varchar, #mindate, 101) ) and ( convert(varchar, #maxdate, 101) )
)
select distinct CONVERT(VARCHAR,CalendarDate,20) as CalendarDate,UserECode,UserName,NatureOfWorkName,RegionProjectName,CountyName,WorkTypeName,TaskName,VolumneProcessed,TimeSpent,Comment from ProductionCTE where RoleID=1
end
GO
When i am removing this If Exist statement then the CTE is working fine but after adding the IF Exists statement it is having error.
You can't use IF EXISTS in this way. A common table expression must only contain a single select statement, it's not possible to say if condition SELECT THIS else SELECT THAT.
It looks like you are trying to add an additional created row to a resultset returned by a query. You could achieve this by implementing the CTE as a table-valued function that would return the single new row.
http://msdn.microsoft.com/en-gb/library/ms191165(v=sql.105).aspx
If you want to construct a UNION where you only get a result from the second SELECT if the first SELECT returns no rows, you can achieve this using RANK(). So, you can place your real query in the first SELECT and the desired default values in the second, and achieve the results you want.
I don't have your tables and data, so I'm not going to attempt to re-write your query. But I'll illustrate with this:
;With WithDefaults as (
select name,0 as Rank from sys.objects
union all
select 'abc',1 --Default if no results
), Ranked as (
select *,RANK() OVER (ORDER BY Rank) as Rnk from WithDefaults
)
select * from Ranked where Rnk = 1
Returns (on a mostly empty DB I tried it on) 98 results, of which none had the name abc. If we force the first SELECT to return no results:
select name,0 as Rank from sys.objects where 1 = 2
We now get a single row result with the name abc.
Hopefully, you can see how this could apply to your original query.
You can't use IF EXISTS in CTE. But you can modify logic of function
Example:
alter procedure St_Proc_GetTeamProductionReport
#mindate DateTime,
#maxdate DateTIme,
#userID varchar(50)
as
Begin
set NoCount on;
;with
ProductionCTE(CalendarDate,RoleID,UserID,UserECode,UserName,ImmediateSupervisor,NatureOfWorkName,RegionProjectName,CountyName,WorkTypeName,TaskName,VolumneProcessed,TimeSpent,Comment)
as
(
select P.CalendarDate,U.RoleID,U.UserID,U.UserECode,U.UserName,U.ImmediateSupervisor,N.NatureofWorkName,
R.RegionProjectName,C.Countyname,W.WorktypeName,T.TaskName,P.VolumeProcessed,P.Timespent,P.Comment, 1 AS cnt
from production P inner join NatureOfWork N
on N.NatureofWorkID=P.natureofworkid
inner join dbo.RegionAndProjectInfo R
on R.RegionProjectID=P.RegionProjectID
inner join county C
on C.countyid=P.countyid
inner join worktype W
on W.Worktypeid=P.worktypeID
inner join task T
on T.taskid=P.TaskID
inner join UserInfo U
on U.Userid=P.userid
where P.userid=#userID and ( convert(varchar, P.CalendarDate, 101) ) between (
convert(varchar, #mindate, 101) ) and ( convert(varchar, #maxdate, 101) )
UNION ALL
Select '2012-09-14 13:41:52' as CalendarDate,
2 as RoleID,'938' as UserID,
(select Userecode from Userinfo where userid=#userID) as UserECode,
(select UserName from Userinfo where userid=#userID)as UserName,
(select ImmediateSupervisor from Userinfo where userid=#userID)as ImmediateSupervisor,
'BP' as NatureOfWorkName,
'CO Processing' as RegionProjectName,
'Adams' as CountyName,
'Quality' as WorkTypeName,
'Corrections ' as TaskName,
5 as VolumneProcessed,
'01:00' as TimeSpent,
'test' as Comment, 0
), ProductionCTE2 AS
(
SELECT TOP(SELECT CASE WHEN COUNT(*) = 0 THEN 1 ELSE COUNT(*) END FROM ProductionCTE WHERE cnt = 1)
CalendarDate,RoleID,UserID,UserECode,UserName,ImmediateSupervisor,NatureofWorkName,
RegionProjectName,Countyname,WorktypeName,TaskName,VolumeProcessed,Timespent,Comment
FROM ProductionCTE2
union all
select P.CalendarDate,U.RoleID,U.UserID,U.UserECode,U.UserName,U.ImmediateSupervisor,N.NatureofWorkName,
R.RegionProjectName,C.Countyname,W.WorktypeName,T.TaskName,P.VolumeProcessed,P.Timespent,P.Comment
from production P inner join NatureOfWork N
on N.NatureofWorkID=P.natureofworkid
inner join dbo.RegionAndProjectInfo R
on R.RegionProjectID=P.RegionProjectID
inner join county C
on C.countyid=P.countyid
inner join worktype W
on W.Worktypeid=P.worktypeID
inner join task T
on T.taskid=P.TaskID
inner join UserInfo U
on U.Userid=P.userid
inner join ProductionCTE
on U.ImmediateSupervisor=ProductionCTE.UserECode
where P.IsTaskCompleted=1 and ( convert(varchar, P.CalendarDate, 101) ) between (
convert(varchar, #mindate, 101) ) and ( convert(varchar, #maxdate, 101) )
)
select distinct CONVERT(VARCHAR,CalendarDate,20) as CalendarDate,UserECode,UserName,NatureOfWorkName,RegionProjectName,CountyName,WorkTypeName,TaskName,VolumneProcessed,TimeSpent,Comment
from ProductionCTE2
where RoleID=1
end

Obtaining only first result from a LEFT JOIN

I'm trying to get the first result of a LEFT JOIN for each row of a SELECT statement.
Because now right now, if I have 100 rows in the joined table, I'll get 100 times the same row from the SELECT. I'd just need the first joined row so that way I wouldn't get any duplicates.
I can't use GROUP BY because I have to get more than only one row from the table.
Here's a basic version of my query:
SELECT bg.PatientID, DATEDIFF(hour, bg.CreateDate, GETDATE()) TimeToTarget
FROM BloodGlucose bg
LEFT JOIN IVProtocol i ON i.PatientID = bg.PatientID
WHERE bg.BGValue >= i.TargetLow AND bg.BGValue <= i.TargetHigh
ORDER BY bg.PatientID ASC
I tried using DISTINCT but since the data from bg.CreateDate isn't always the same it returns duplicates.
I just need the FIRST row of that left joined table.
Any ideas/suggestions?
Thanks!
;WITH x AS
(
SELECT
bg.PatientID,
TimeToTarget = DATEDIFF(hour, bg.CreateDate, GETDATE()),
rn = ROW_NUMBER() OVER (PARTITION BY bg.PatientID ORDER BY bg.CreatedDate DESC)
FROM dbo.BloodGlucose AS bg
LEFT JOIN dbo.IVProtocol AS i
ON i.PatientID = bg.PatientID
WHERE bg.BGValue >= i.TargetLow
AND bg.BGValue <= i.TargetHigh
)
SELECT PatientID, TimeToTarget
FROM x
WHERE rn = 1
ORDER BY PatientID;
To join to other results:
;WITH x AS
(
... same as above ...
)
SELECT x.PatientID, x.TimeToTarget, y.Something
FROM x INNER JOIN dbo.SomethingElse AS y
ON x.PatientID = y.PatientID
WHERE x.rn = 1
ORDER BY x.PatientID;
SELECT bg.PatientID, DATEDIFF(hour, bg.CreateDate, GETDATE()) TimeToTarget
FROM BloodGlucose bg
cross apply (
select top 1 *
from IVProtocol i
where i.PatientID = bg.PatientID
order by SOME_CRITERA
) i
WHERE bg.BGValue >= i.TargetLow AND bg.BGValue <= i.TargetHigh
ORDER BY bg.PatientID ASC
Cross apply is a handy tool for such situations. It works like a join but you can use variables inside the subquery.