Performing a JOIN with the results of a WITH clause - sql

I have one query that uses one join already, call it query1. I would like to join query1 to the results of a WITH clause. I don't know how to merge the two data sets.
Query 1:
SELECT
p.NetObjectID
, n.Caption, n.ObjectSubType
FROM Pollers p
LEFT JOIN NodesData n ON n.NodeID = p.NetObjectID
The next query is a WITH clause. I don't know how to obtain the results without a WITH because I need to do what is outlined in this post.
Query 2:
WITH ranked_DateStamp AS
(
SELECT c.NodeID, c.DateTime
, ROW_NUMBER() OVER (PARTITION BY NodeID ORDER BY DateTime DESC) AS rn
FROM CPULoad AS c
)
SELECT *
FROM ranked_DateStamp
WHERE rn = 1;
I thought I could just JOIN ranked_DateStamp ON ranked_DateStamp.NodeID = p.NodeID but it won't allow it.

You don't really need a with clause here, you can use a subquery. I would just phrase this as:
SELECT
p.NetObjectID,
n.Caption,
n.ObjectSubType,
c.DateTime
FROM Pollers p
LEFT JOIN NodesData n ON n.NodeID = p.NetObjectID
LEFT JOIN (
SELECT
NodeID,
DateTime,
ROW_NUMBER() OVER (PARTITION BY NodeID ORDER BY DateTime DESC) AS rn
FROM CPULoad AS c
) c ON c.rn = 1 and c.NodeID = p.NetObjectID
However, if you were to use a common table expression, that would look like:
WITH ranked_DateStamp AS (
SELECT
NodeID,
DateTime,
ROW_NUMBER() OVER (PARTITION BY NodeID ORDER BY DateTime DESC) AS rn
FROM CPULoad AS c
)
SELECT
p.NetObjectID,
n.Caption,
n.ObjectSubType,
c.DateTime
FROM Pollers p
LEFT JOIN NodesData n ON n.NodeID = p.NetObjectID
LEFT JOIN ranked_DateStamp c ON c.rn = 1 and c.NodeID = p.NetObjectID
Actually, a lateral join might perform equally well, or better:
SELECT
p.NetObjectID,
n.Caption,
n.ObjectSubType,
c.DateTime
FROM Pollers p
LEFT JOIN NodesData n ON n.NodeID = p.NetObjectID
OUTER APPLY (SELECT TOP (1) * FROM CPULOad c WHERE c.NodeID = p.NetObjectID ORDER BY DateTime DESC) c

Actually, I would suggest a different approach -- a lateral join:
SELECT p.NetObjectID, n.Caption, n.ObjectSubType,c.DateTime
FROM Pollers p LEFT JOIN
NodesData n
ON n.NodeID = p.NetObjectID OUTER APPLY
(SELECT TOP (1) NodeID, DateTime,
ROW_NUMBER() OVER (PARTITION BY NodeID ORDER BY DateTime DESC) AS rn
FROM CPULoad c
WHERE c.NodeId = p.NetObjectID
ORDER BY DateTime DESC
) c;
Lateral joins are very powerful and worth learning about. They are also often a bit faster than the ROW_NUMBER() approach.

Related

How to remove duplicate entries in my query?

The following code gives me multiple lines since there can be more than one Cust_Edit_Log.Edit_Timestamp per Alarm Account. There is no other way for a duplicate to occur. How do I only get the result with the earliest Cust_Edit_Log.Edit_Timestamp date? Thank you in advance for any help you can provide.
Select
AR_Customer.Customer_Number As 'Customer_Number',
AR_Customer.Customer_Name As 'Customer_Name',
AR_Customer_System.Alarm_Account As 'Alarm_Account',
AR_Customer_Site.Address_1 As 'Site_Address_1',
Cust_Edit_Log.UserComments As 'Edit_Log_Cust_User_Comments',
Cust_Edit_Log.Edit_Timestamp As 'Edit_Log_Cust_Timestamp',
Cust_Edit_Log.UserCode As 'Edit_Log_Cust_User'
From
AR_Customer
Inner JOIN AR_Customer_Site On AR_Customer.Customer_Id = AR_Customer_Site.Customer_Id
Left Outer JOIN AR_Customer_System On AR_Customer_Site.Customer_Site_Id = AR_Customer_System.Customer_Site_Id
Left Outer Join CQB_Log_Parse Cust_Edit_Log on AR_Customer.Customer_Id = Cust_Edit_Log.Customer_Id
Where
AR_Customer.Customer_Id <> 1 And
(AR_Customer_System.Alarm_Account Like 'IN%' And
Cust_Edit_Log.UserComments Like 'Edited Customer System IN%')
Order By
AR_Customer.Customer_Number ASC
Use Partition BY:
SELECT
X.*
FROM
(
Select
AR_Customer.Customer_Number As 'Customer_Number',
AR_Customer.Customer_Name As 'Customer_Name',
AR_Customer_System.Alarm_Account As 'Alarm_Account',
AR_Customer_Site.Address_1 As 'Site_Address_1',
Cust_Edit_Log.UserComments As 'Edit_Log_Cust_User_Comments',
Cust_Edit_Log.Edit_Timestamp As 'Edit_Log_Cust_Timestamp',
Cust_Edit_Log.UserCode As 'Edit_Log_Cust_User',
ROW_NUMBER() OVER(Partition BY AR_Customer_System.Alarm_Account,Cust_Edit_Log.Edit_Timestamp ORDER BY AR_Customer_System.Alarm_Account) AS PartNO
From
AR_Customer
Inner JOIN AR_Customer_Site On AR_Customer.Customer_Id = AR_Customer_Site.Customer_Id
Left Outer JOIN AR_Customer_System On AR_Customer_Site.Customer_Site_Id = AR_Customer_System.Customer_Site_Id
Left Outer Join CQB_Log_Parse Cust_Edit_Log on AR_Customer.Customer_Id = Cust_Edit_Log.Customer_Id
Where
AR_Customer.Customer_Id <> 1 And
(AR_Customer_System.Alarm_Account Like 'IN%' And
Cust_Edit_Log.UserComments Like 'Edited Customer System IN%')
)X
WHERE X.PartNo=1
Order By X.Customer_Number ASC
One method uses row_number():
Left Outer Join
(select lp.*,
row_number() over (partition by lp.Customer_Id
order by Edit_Timestamp asc
) as seqnum
from CQB_Log_Parse lp
) Cust_Edit_Log
on AR_Customer.Customer_Id = Cust_Edit_Log.Customer_Id and
seqnum = 1
Maybe try with MIN(Cust_Edit_Log.Edit_Timestamp)
You can try using as below:
;with cte as (
Select
AR_Customer.Customer_Number As 'Customer_Number',
AR_Customer.Customer_Name As 'Customer_Name',
AR_Customer_System.Alarm_Account As 'Alarm_Account',
AR_Customer_Site.Address_1 As 'Site_Address_1',
Cust_Edit_Log.UserComments As 'Edit_Log_Cust_User_Comments',
Cust_Edit_Log.Edit_Timestamp As 'Edit_Log_Cust_Timestamp',
Cust_Edit_Log.UserCode As 'Edit_Log_Cust_User'
,row_number() over(partition by AR_Customer.Customer_Number order by Cust_Edit_Log.Edit_Timestamp) as rownum
From
AR_Customer
Inner JOIN AR_Customer_Site On AR_Customer.Customer_Id = AR_Customer_Site.Customer_Id
Left Outer JOIN AR_Customer_System On AR_Customer_Site.Customer_Site_Id = AR_Customer_System.Customer_Site_Id
Left Outer Join CQB_Log_Parse Cust_Edit_Log on AR_Customer.Customer_Id = Cust_Edit_Log.Customer_Id
Where
AR_Customer.Customer_Id <> 1 And
(AR_Customer_System.Alarm_Account Like 'IN%' And
Cust_Edit_Log.UserComments Like 'Edited Customer System IN%')
--Order By
--AR_Customer.Customer_Number ASC
)
select * from cte where rownum = 1
order by AR_Customer.Customer_Number ASC

Limiting result sets by future date - SQL

The Query below produces a record for each Entry in the SP_ScheduleEvent Table.
SELECT m.MaterialId, m.MaterialTitle, se.EventDateTime, c.ChannelName
FROM GB_Material m
LEFT OUTER JOIN SP_ScheduleEvent se on se.MaterialName = m.MaterialName
INNER JOIN SP_Schedule s on s.ScheduleID = se.ScheduleID
INNER JOIN GB_Channel c on c.ChannelID = s.ChannelID
WHERE LOWER(m.MaterialName) like '%foo%' OR LOWER(m.MaterialTitle) like '%foo%'
I want to limit the result set by the nearest future EventDateTime.
So per material name i would like to see one EventDateTime, which should be the nearest future date to the current time.
And lastly, a record may not exist in the SP_ScheduleEvent table for a particular materialname, in which case there should be null returned for the EventDateTime column
SQLFiddle
How would i go about doing this?
First, your LEFT JOIN is immaterial, because the subsequent joins make it an INNER JOIN. Either use LEFT JOIN throughout the FROM statement or switch to INNER JOIN.
I think you can use ROW_NUMBER():
SELECT t.*
FROM (SELECT m.MaterialId, m.MaterialName, m.MaterialTitle, se.EventDateTime,
ROW_NUMBER() over (PARTITION BY m.MaterialId OVER se.EventDateTime DESC) as seqnum
FROM GB_Material m INNER JOIN
SP_ScheduleEvent se
on se.MaterialName = m.MaterialName INNER JOIN
SP_Schedule s
on s.ScheduleID = se.ScheduleID INNER JOIN
GB_Channel c
on c.ChannelID = s.ChannelID
WHERE se.EventDateTime > getdate() AND
(LOWER(m.MaterialName) like '%foo%' OR LOWER(m.MaterialTitle) like '%foo%')
) t
WHERE seqnum = 1
ORDER BY se.EventDateTime;
Use the ROW_NUMBER() function:
WITH cte AS (
SELECT m.MaterialId, m.MaterialTitle, se.EventDateTime, c.ChannelName,
ROW_NUMBER() OVER (PARTITION BY m.MaterialId ORDER BY EventDateTime ASC) AS rn
FROM GB_Material m
LEFT OUTER JOIN SP_ScheduleEvent se on se.MaterialName = m.MaterialName
LEFT OUTER JOIN SP_Schedule s on s.ScheduleID = se.ScheduleID
LEFT OUTER JOIN GB_Channel c on c.ChannelID = s.ChannelID
WHERE LOWER(m.MaterialName) like '%foo%' OR LOWER(m.MaterialTitle) like '%foo%'
AND se.EventDateTime > GETDATE()
)
SELECT * FROM cte
WHERE rn=1

How to use a column from joined table in a join with a subquery

What I'm trying to do is this:
SELECT *
FROM MainTable m
INNER JOIN JoinedTable j on j.ForeignID = m.ID
INNER JOIN (SELECT TOP 1 *
FROM SubQueryTable sq
WHERE sq.ForeignID = j.ID
ORDER BY VersionColumn DESC)
So basically, from SubQueryTable, I only want to retrieve a single row which has the maximum value for VersionColumn for all rows with a certain ID that I can get from JoinedTable.
T-SQL doesn't let me do this, what's a good way to solve this problem?
What I'm trying to prevent is loading the entire SubQueryTable and doing the filtering when it's too late as in....
SELECT *
FROM MainTable m
INNER JOIN JoinedTable j on j.ForeignID = m.ID
INNER JOIN (SELECT TOP 1 *
FROM SubQueryTable sq
ORDER BY VersionColumn DESC) sj ON sj.ForeignID = j.ID
I fear this second version performs the very slow subquery first and only filters it when it has loaded all the rows, but I want to filter sooner.
Any thoughts?
This will perform well if you have index on VersionColumn
SELECT *
FROM MainTable m
INNER JOIN JoinedTable j on j.ForeignID = m.ID
CROSS APPLY (SELECT TOP 1 *
FROM SubQueryTable sq
WHERE sq.ForeignID = j.ID
ORDER BY VersionColumn DESC) sj
Answer :
Hi,
Below query I have created as per your requirement using Country, State and City tables.
SELECT * FROM (
SELECT m.countryName, j.StateName,c.CityName , ROW_NUMBER() OVER(PARTITION BY c.stateid ORDER BY c.cityid desc) AS 'x'
FROM CountryMaster m
INNER JOIN StateMaster j on j.CountryID = m.CountryID
INNER JOIN dbo.CityMaster c ON j.StateID = c.StateID
) AS numbered WHERE x = 1
Below is your solution and above is only for your reference.
SELECT * FROM (
SELECT m.MainTablecolumnNm, j.JoinedTablecolumnNm,c.SubQueryTableColumnName , ROW_NUMBER()
OVER(PARTITION BY sj.ForeignID ORDER BY c.sjID desc) AS 'abc'
FROM MainTable m
INNER JOIN JoinedTable j on j.ForeignID = m.ID
INNER JOIN SubQueryTable sj ON sj.ForeignID = j.ID
) AS numbered WHERE abc = 1
Thank you,
Vishal Patel

How would I switch this from performing an AND operation to an OR?

Right now this query searches a table called article for entries who's title and abstract field's contain a certain keyword. The set it returns are articles who's title AND abstract contain the keyword, but I would like to change it so it returns articles who's title OR abstract contains the keyword. How would I accomplish this? By simply changing the inner joins to an outer?
BEGIN
with articlesearch as (
SELECT top 1000 FT_TBL.articleID, FT_TBL.title,FT_TBL.abstract,FT_TBL.publicationdate,
(select j.journalID from journal j where FT_TBL.journalID=j.journalID) as sourceID,
(select j.journalname from journal j where FT_TBL.journalID=j.journalID) as sourcename,
(select j2.medabbr from journal j2 where FT_TBL.journalID=j2.journalID) as medabbr,
(select j1.impactfactor from journal j1 where FT_TBL.journalID=j1.journalID) as impactfactor,
KEY_TBL.RANK,
ROW_NUMBER() OVER (ORDER BY KEY_TBL.RANK desc) AS RowNumber
FROM article AS FT_TBL
INNER JOIN
CONTAINSTABLE(article,title,#keyword) AS KEY_TBL
ON FT_TBL.articleID = KEY_TBL.[KEY]
INNER JOIN
CONTAINSTABLE(article,abstract,#keyword) AS KEY_TBL2
ON FT_TBL.articleID = KEY_TBL2.[KEY]
where FT_TBL.inactive=0
ORDER BY RANK DESC
)
SELECT articleID, sourcename,title,abstract,publicationdate,medabbr
FROM articlesearch
WHERE RowNumber BETWEEN #RowStart AND #RowEnd ORDER BY publicationdate desc;
END
The following version changes the inner joins to left outer joins and adds a where clause to get what you want:
with articlesearch as (
SELECT top 1000 FT_TBL.articleID, FT_TBL.title,FT_TBL.abstract,FT_TBL.publicationdate,
(select j.journalID from journal j where FT_TBL.journalID=j.journalID) as sourceID,
(select j.journalname from journal j where FT_TBL.journalID=j.journalID) as sourcename,
(select j2.medabbr from journal j2 where FT_TBL.journalID=j2.journalID) as medabbr,
(select j1.impactfactor from journal j1 where FT_TBL.journalID=j1.journalID) as impactfactor,
KEY_TBL.RANK,
ROW_NUMBER() OVER (ORDER BY KEY_TBL.RANK desc) AS RowNumber
FROM article AS FT_TBL
left outer JOIN
CONTAINSTABLE(article,title,#keyword) AS KEY_TBL
ON FT_TBL.articleID = KEY_TBL.[KEY]
left outer join
CONTAINSTABLE(article,abstract,#keyword) AS KEY_TBL2
ON FT_TBL.articleID = KEY_TBL2.[KEY]
where FT_TBL.inactive=0 and (key_tbl.[key] is not null or key_tbl2.[key] is not null)
ORDER BY RANK DESC
)
SELECT articleID, sourcename,title,abstract,publicationdate,medabbr
FROM articlesearch
WHERE RowNumber BETWEEN #RowStart AND #RowEnd ORDER BY publicationdate desc;
Looks like you could change the INNER JOIN on each to a LEFT JOIN and that would get you what you want. Though I'll admit I'm not familiar with CONTAINSTABLE
You'll need to change the INNER JOIN to an OUTER, as DigitalD notes, but then you also need to filter your results where at least one of the joins isn't empty.
-- snip
----
-- /snip
FROM article AS FT_TBL
LEFT OUTER JOIN
CONTAINSTABLE(article,title,#keyword) AS KEY_TBL
ON FT_TBL.articleID = KEY_TBL.[KEY]
LEFT OUTER JOIN
CONTAINSTABLE(article,abstract,#keyword) AS KEY_TBL2
ON FT_TBL.articleID = KEY_TBL2.[KEY]
WHERE FT_TBL.inactive=0
AND (KEY_TBL.[KEY] IS NOT NULL OR KEY_TBL2.[KEY] IS NOT NULL)
ORDER BY RANK DESC
)
-- snip
----
-- /snip

How would that be possible to make this SQL Query simpler/shorter?

It should return some fields from the SystemTable and the LoadStatus column of the latest record in the ProcessHistory table. The relationship is 1 to many:
SELECT ST.[SystemDetailID], ST.[SystemName], LH.LatestLoadStatus
FROM [SystemTable] AS ST
LEFT OUTER JOIN
(
SELECT LHInner.LoadStatus AS LatestLoadStatus, LHInner.SystemDetailID FROM [dbo].[LoadHistory] AS LHInner
WHERE LHInner.LoadHistoryID in
(
SELECT LatestLoadHisotoryID FROM
(
SELECT MAX(LoadHistoryID) as LatestLoadHisotoryID, SystemDetailID FROM [dbo].[LoadHistory]
GROUP BY SystemDetailID
) l
)
) AS LH ON ST.SystemDetailID = LH.SystemDetailID
Thanks,
This is a greatest-n-per-group query.
One Approach
SELECT ST.[SystemDetailID],
ST.[SystemName],
LH.LatestLoadStatus
FROM [SystemTable] AS ST
OUTER APPLY (SELECT TOP 1 *
FROM [dbo].[LoadHistory] LH
WHERE ST.SystemDetailID = LH.SystemDetailID
ORDER BY LoadHistoryID DESC) LH
You can also use row_number
WITH LH
AS (SELECT *,
ROW_NUMBER() OVER (PARTITION BY SystemDetailID
ORDER BY LoadHistoryID DESC) RN
FROM [dbo].[LoadHistory])
SELECT ST.[SystemDetailID],
ST.[SystemName],
LH.LatestLoadStatus
FROM [SystemTable] AS ST
LEFT JOIN LH
ON LH.SystemDetailID = ST.SystemDetailID
AND LH.RN = 1
SELECT ST.[SystemDetailID], ST.[SystemName], LH.LatestLoadStatus
FROM [SystemTable] AS ST
INNER JOIN [dbo].[LoadHistory] AS LH
ON ST.SystemDetailID = LH.SystemDetailID
AND LH.LoadHistoryID IN
(SELECT MAX(LoadHistoryID) as LoadHistoryID
FROM [dbo].[LoadHistory]
GROUP BY SystemDetailID )