如何从左表中仅获取一条记录与右表中的每条记录
Posted
技术标签:
【中文标题】如何从左表中仅获取一条记录与右表中的每条记录【英文标题】:How to get only one record from left table against each record from right table 【发布时间】:2019-01-14 07:39:41 【问题描述】:我有两张表,一张用于某种设备,另一张用于它们的位置(使用此表来记录位置历史记录)。表的架构是:
tbl_Devices:
DeviceSrNumber (bigint, not null, pk) 其他一些列tbl_DeviceLocation
SrNumber(bigint,not null,pk,identity) DeviceSrNumber(bigint,非空) 位置(varchar(300),非空) AddDateTime(日期时间,非空)我需要的是DeviceSrNumber
,最后一个Location
,最后一个位置集AddDateTime
我现在在做什么:
SELECT
DeviceSrNumber,
(
SELECT TOP (1) Location
FROM tbl_DeviceLocation
WHERE (DeviceSrNumber = d.DeviceSrNumber)
ORDER BY AddDateTime DESC
) AS 'Location',
(
SELECT TOP (1) AddDateTime
FROM tbl_DeviceLocation
WHERE (DeviceSrNumber = d.DeviceSrNumber)
ORDER BY AddDateTime DESC
) AS 'AddDateTime '
FROM
tbl_Devices AS d
但我需要以更简洁的方式使用连接,因为据我所知,子查询效率不高,不推荐在大型数据库中使用。
注意:它只是大型项目的一小部分,将处理数百万条记录。
【问题讨论】:
你有Indexes
tbl_DeviceLoation
吗?
是的。我在 SrNumber 上有聚集索引(bigint,not null,identity,PK)并且还检查了“Location”列。但这和我们的问题有关系吗?
CHECK
是 约束 而不是 INDEX,因此您只有一个索引。
子查询性能取决于查询和优化器。与每个优化问题一样,应该在实践中衡量它和其他选项。当然,对于特定的实现,有记录和观察/假设的趋势——在这里,我们可以怀疑某些重复的代码可能没有得到很好的优化,并可能通过连接来寻求 tbl_DeviceLocation 的单一提及。你是怎么尝试的? PS 通过后期编辑进行澄清,而不是 cmets。另外对于代码问题,请提供minimal reproducible example。
【参考方案1】:
你可以从这个开始:
SELECT *
FROM (
SELECT
d.DeviceSrNumber
, dl.Location LastLocation
, dl.AddDateTime LastLocationSetDateTime
, ROW_NUMBER() OVER(PARTITION BY d.DeviceSrNumber ORDER BY AddDateTime DESC) RN
FROM
tbl_Devices d
JOIN tbl_DeviceLocation dl ON dl.DeviceSrNumber = d.DeviceSrNumber
) D
WHERE
RN = 1
更新
SELECT
d.DeviceSrNumber
, MAX(dl.Location) LastLocation
, MAX(dl.AddDateTime) LastLocationSetDateTime
FROM
tbl_Devices d
JOIN tbl_DeviceLocation dl ON dl.DeviceSrNumber = d.DeviceSrNumber
GROUP BY d.DeviceSrNumber
ORDER BY AddDateTime DESC
【讨论】:
亲爱的@iSR5,我不希望在过去 24 小时内添加位置,我需要每个设备的位置及其时间,而不用担心它们是否在过去 24 小时内更新。 @RaiArslan 然后删除 WHERE 子句,这将获取所有设备及其位置和时间。 再次澄清一下,我想要最后一个位置,而不是分配给设备的所有位置。 @RaiArslan 重新检查查询,我已更新。这是你需要的吗? 亲爱的@iSR5,我不想使用子查询。它产生的结果与我的初始查询相同。请让我知道是否有任何方法可以使用连接。如果是,请告诉我。【参考方案2】:这样你的查询会更有效率:
SELECT D.DeviceSrNumber,
L.Location,
L.AddDateTime
FROM tbl_Devices D
OUTER APPLY
(SELECT TOP (1) DL.Location, DL.AddDateTime
FROM tbl_DeviceLocation DL
WHERE D.DeviceSrNumber = DL.DeviceSrNumber
ORDER BY DL.AddDateTime DESC
) L;
【讨论】:
你最后错过了什么。 您还没有错过任何其他事情吗?例如:L.Location
?? L??
仍然需要别名@yper-crazyhat-cubeᵀᴹ
我冒昧地纠正了 cmets 中提到的问题,但请自行查看。该方法本身是有道理的,它只是有一些问题,我相信现在所有的问题都已经解决了。如有必要,请随时进一步编辑
非常感谢!!!是的,我同时在几个项目中,并没有足够专注于正确回复。很高兴有几个人意识到并改进了响应!!!【参考方案3】:
试试这个,它将使用最新的 AddDateTime 选择一个数据
SELECT top(1) D.DeviceSrNumber, L.Location,L.AddDateTime
FROM tbl_Devices AS D
JOIN tbl_DeviceLoation AS L ON D.DeviceSrNumber = L.DeviceSrNumber
WHERE D.DeviceSrNumber = L.DeviceSrNumber order by L.AddDateTime desc
更新
我试过这个,它根据最新记录选择不同的记录。希望这能解决它
WITH CTE AS
(
SELECT D.DeviceSrNumber, L.Location,L.AddDateTime , RN= ROW_NUMBER() OVER (PARTITION BY D.DeviceSrNumber ORDER BY D.DeviceSrNumber)
FROM tbl_Devices AS D LEFT JOIN tbl_DeviceLoation AS L ON D.DeviceSrNumber = L.DeviceSrNumber
WHERE D.DeviceSrNumber = L.DeviceSrNumber group by D.DeviceSrNumber, L.Location,L.AddDateTime )
SELECT * FROM CTE where RN=1 order by CTE.AddDateTime desc
【讨论】:
亲爱的@muhdamean,这行不通,因为我们需要列出所有设备及其位置。 我尝试使用另一种方法,它对我有用,请尝试一下。 @RaiArslan以上是关于如何从左表中仅获取一条记录与右表中的每条记录的主要内容,如果未能解决你的问题,请参考以下文章