【问题标题】:How to get only one record from left table against each record from right table如何从左表中仅获取一条记录与右表中的每条记录
【发布时间】:2019-01-14 07:39:41
【问题描述】:

我有两张表,一张用于某种设备,另一张用于它们的位置(使用此表来记录位置历史记录)。表的架构是:

  1. tbl_Devices:

    • DeviceSrNumber (bigint, not null, pk)
    • 其他一些列
  2. 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

但我需要以更简洁的方式使用连接,因为据我所知,子查询效率不高,不推荐在大型数据库中使用。

注意:它只是大型项目的一小部分,将处理数百万条记录。

【问题讨论】:

  • 你有Indexestbl_DeviceLoation吗?
  • 是的。我在 SrNumber 上有聚集索引(bigint,not null,identity,PK)并且还检查了“Location”列。但这和我们的问题有关系吗?
  • CHECK约束 而不是 INDEX,因此您只有一个索引。
  • 子查询性能取决于查询和优化器。与每个优化问题一样,应该在实践中衡量它和其他选项。当然,对于特定的实现,有记录和观察/假设的趋势——在这里,我们可以怀疑某些重复的代码可能没有得到很好的优化,并可能通过连接来寻求 tbl_DeviceLocation 的单一提及。你是怎么尝试的? PS 通过后期编辑进行澄清,而不是 cmets。另外对于代码问题,请提供minimal reproducible example

标签: sql-server join subquery query-performance sqlperformance


【解决方案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-c​​ubeᵀᴹ
  • 我冒昧地纠正了 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

【讨论】:

  • 亲爱的@muhdam​​ean,这行不通,因为我们需要列出所有设备及其位置。
  • 我尝试使用另一种方法,它对我有用,请尝试一下。 @RaiArslan
猜你喜欢
  • 1970-01-01
  • 2023-03-10
  • 1970-01-01
  • 2021-08-31
  • 1970-01-01
  • 2021-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多