MariaDB 使用 ORDER BY 和 LIMIT 自连接

问题描述 投票:0回答:1

我在编写要在以下系统上运行的 SQL 查询时遇到问题:

  • 操作系统:Debian 11(牛眼)
  • 数据库:MariaDB 10.5.15(为bullseye打包的版本)

表架构(带有一些示例数据):

DROP TABLE IF EXISTS reference_log;

CREATE TABLE reference_log
(
  id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  reference_number VARCHAR(20) NOT NULL,
  reference_date DATE NOT NULL,
  log_date DATE NOT NULL
) Engine=InnoDB;

INSERT INTO reference_log (
    id,
    reference_number,
    reference_date,
    log_date
) VALUES (
    1, 
    '123',
    '2024-04-01',
    '2024-04-14'
);

INSERT INTO reference_log (
    id,
    reference_number,
    reference_date,
    log_date
) VALUES (
    2, 
    '123',
    '2024-04-01',
    '2024-04-15'
);

INSERT INTO reference_log (
    id,
    reference_number,
    reference_date,
    log_date
) VALUES (
    3, 
    '123',
    '2024-04-01',
    '2024-05-01'
);

INSERT INTO reference_log (
    id,
    reference_number,
    reference_date,
    log_date
) VALUES (
    4, 
    '123',
    '2024-05-01',
    '2024-05-06'
);

行具有以下属性,这些属性由应用程序而不是数据库强制执行(即没有约束):

  • 较早的 id 将始终具有相同或较早的 log_date(行按 log_date 的升序插入)。
  • 元组(reference_number,log_date)始终是唯一的。
  • 元组(reference_number,reference_date)不是唯一的,并且会有很多重复项,但是这些行将始终按reference_date的升序插入。
  • 对于给定的参考编号,日志日期中可能存在间隙。例如,参考号可能包含记录日期 2024-01-01、2024-01-02、2024-01-05 的行。
  • reference_date 将始终小于或等于 log_date。

其他相关统计数据:

  • 任何给定的 log_date 将有大约 7,000 行。
  • 总共超过200万行。

我正在查找给定日志日期的所有参考编号,其中前一个实例具有较早的参考日期。前一个实例被定义为具有相同参考号和较低 id 的行。如果有多个(通常是这种情况),则使用 id 最高的那个。对于前一个实例,任何组合都是可能的,即

  • 没有以前的实例。
  • 前一个实例具有相同的参考日期。
  • 前一个实例具有不同的参考日期(由于上面列出的属性,这始终是较早的日期)。

使用示例数据,我预计会出现以下情况:

  • 对于 2024-05-01 的 log_date 没有返回任何行(之前的实例具有相同的 reference_date)
  • 针对 2024-05-06 的日志日期返回 1 行(之前的实例具有较早的引用日期)

我可以轻松获得日志日期的所有参考编号:

SELECT reference_number
FROM reference_log
WHERE
  log_date = '2024-05-01'

我还可以获取特定参考号(“123”)和 ID(50)的前一个实例:

SELECT reference_number
FROM reference_log
WHERE
  reference_number = '123'
  AND id < 50
ORDER BY id DESC
LIMIT 1

我正在努力解决的是如何组合这两个查询。如果我不需要 ORDER BY 和 LIMIT,那么自联接将很简单,尽管速度很慢(由于“检查每个记录的范围”,并且有超过 200 万行,因此执行需要 4 分钟以上):

SELECT
  rl1.reference_number
FROM reference_log rl1, reference_log rl2
WHERE
  rl1.log_date = '2024-05-01'
  AND rl2.reference_number = rl1.reference_number
  AND rl2.id < rl1.id
  AND rl2.reference_date < rl1.reference_date

有没有办法使用单个查询来获取我需要的内容?

唯一的其他选择是获取给定 log_date 的每一行,然后发出另一个查询来查找前一个实例。然而,每个 log_date 通常有大约 7,000 个参考号,因此这意味着发出 7,000 个查询。这也意味着我无法直接针对数据库测试查询(我更喜欢在可能的情况下这样做,因为我可以确保任何错误都不是应用程序中的错误造成的)。

sql mariadb
1个回答
0
投票

你可以尝试更简单的方法 - 使用窗口函数

select *
from(
select *
  ,coalesce(lag(reference_date)
      over(partition by reference_number order by id)
     ,reference_date)prev_ref_date
from reference_log
)logprev
where reference_date<>prev_ref_date

输出是

id 参考号 参考日期 日志日期 prev_ref_date
4 123 2024-05-01 2024-05-06 2024-04-01
© www.soinside.com 2019 - 2024. All rights reserved.