【问题标题】:Select the latest 3 records for each ID in a table为表中的每个 ID 选择最新的 3 条记录
【发布时间】:2015-08-21 00:11:29
【问题描述】:

我有一个包含复合主键(IDDate)的表,如下所示。

+--------+------------+--------+ |身份证 |日期 |价值 | +--------+------------+--------+ | 1 | 1433419200 | 15 | | 1 | 1433332800 | 23 | | 1 | 1433246400 | 41 | | 1 | 1433160000 | 55 | | 1 | 1432900800 | 24 | | 2 | 1433419200 | 52 | | 2 | 1433332800 | 23 | | 2 | 1433246400 | 39 | | 2 | 1433160000 | 22 | | 3 | 1433419200 | 11 | | 3 | 1433246400 | 58 | | ... | ... | ... | +--------+------------+--------+

Date 列上还有一个单独的索引。该表大小适中,目前约 600k 行,每天增长约 2k。

我想做一个 SELECT 查询,返回每个 ID 的最新 3 条记录(按 Date 时间戳排序)。对于每个给定的IDDate 的值始终是唯一的,因此这里无需担心Date 的关系。

this answer 的启发,我尝试了一种自加入方法,但运行了好几秒钟却什么也没返回:

SELECT p1.ID, p1.Date, p1.Value FROM MyTable AS p1
LEFT JOIN MyTable AS p2 
ON p1.ID=p2.ID AND p1.Date<=p2.Date
GROUP BY p1.ID
HAVING COUNT(*)<=5
ORDER BY p1.ID, p1.Date DESC;

这里有什么快速的解决方案?

【问题讨论】:

    标签: sql sqlite group-by sql-order-by greatest-n-per-group


    【解决方案1】:

    您可以查找每个 ID 的三个最近日期:

    SELECT ID, Date, Value
    FROM MyTable
    WHERE Date IN (SELECT Date
                   FROM MyTable AS T2
                   WHERE T2.ID = MyTable.ID
                   ORDER BY Date DESC
                   LIMIT 3)
    

    或者,查找每个 ID 的第三个最近日期,并将其用作限制:

    SELECT ID, Date, Value
    FROM MyTable
    WHERE Date >= IFNULL((SELECT Date
                          FROM MyTable AS T2
                          WHERE T2.ID = MyTable.ID
                          ORDER BY Date DESC
                          LIMIT 1 OFFSET 2),
                         0)
    

    这两个查询都应该从主键的索引中获得良好的性能。

    【讨论】:

    • 这是一种不同的方法,也是非常好的方法。性能提高了 10 倍!
    【解决方案2】:

    首先,这里是不等式方法的正确查询:

    SELECT p1.ID, p1.Date, p1.Value
    FROM MyTable p1 LEFT JOIN
         MyTable AS p2 
         ON p1.ID = p2.ID AND p2.Date <= p1.Date
    --------------------------^ fixed this condition
    GROUP BY p1.ID, p1.Date, p1.Value
    HAVING COUNT(*) <= 5
    ORDER BY p1.ID, p1.Date DESC;
    

    我不确定在 SQLite 中是否有快速的方法来做到这一点。在大多数其他数据库中,您可以使用 ANSI 标准 row_number() 函数。在 MySQL 中,您可以使用变量。这两个在 SQLite 中都很困难。您最好的解决方案可能是使用光标。

    以上内容可以从MyTable(Id, Date)上的索引中受益。

    【讨论】:

    • 实际上“可选”修复是必要的。我刚刚尝试过,没有额外的两个 GROUP BY 列,它仍然不会返回任何内容。在索引点上,由于IDDate构成复合主键,所以默认在它们上面都有索引。但是查询需要将近 18 秒才能在大约 600K 行的表上运行。
    【解决方案3】:
    SELECT distinct x.ID,x.Date,X.Value
    FROM ( SELECT DISTINCT ID FROM XXXTable  ) c
        CROSS APPLY (
    
        select top 3 A.ID,a.Date,Value,[Count] from (
        SELECT distinct ID,Date,Value, ROW_NUMBER()
        over (
            PARTITION BY ID
            order by Date
        ) AS [Count]  where c.ID = t.ID
    
    
        ) A  order by [Count] desc
    

    【讨论】:

      猜你喜欢
      • 2021-01-23
      • 2020-06-21
      • 1970-01-01
      • 2022-11-26
      • 1970-01-01
      • 2020-08-22
      • 2020-05-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多