【问题标题】:SQL - Keep only the first and last record of each daySQL - 只保留每天的第一条和最后一条记录
【发布时间】:2015-01-13 01:36:48
【问题描述】:

我有一个存储简单日志数据的表:

CREATE TABLE chronicle (
    id INT auto_increment PRIMARY KEY, 
    data1 VARCHAR(256),
    data2 VARCHAR(256),
    time DATETIME
);

该表已接近 100 万条记录,因此我想开始合并数据。

我希望能够每天获取每个DISTINCT(data1, data2) 的第一条和最后一条记录,并删除所有其余的。

我知道如何只提取数据并以我想要的任何语言对其进行处理,然后删除带有巨大 IN (...) query 的记录,但直接使用 SQL 似乎是一个更好的选择(我错了吗?)

我尝试了几个查询,但除了 JOIN 之外,我对 SQL 不太擅长。

这是我目前所拥有的:

SELECT id, Max(time), Min(time)
FROM   (SELECT id, data1 ,data2, time, Cast(time AS DATE) AS day
        FROM chronicle) AS initial
GROUP BY day;

这让我得到了每天的第一次和最后一次,但它并没有被数据分开(即我得到每天的最后一条记录,而不是每天每组不同数据的最后一条记录。)另外,id 仅用于 Min(time)。

我在这个特定问题上找到的信息仅用于查找当天的最后一条记录,而不是数据集的每条最后一条记录。

重要提示:我想要每个 DISTINCT(data1, data2) 每天的第一条/最后一条记录,而不仅仅是表格中每天的第一条/最后一条记录。每天将有超过 2 条记录。

解决方案: 感谢 Jonathan Dahan 和 Gordon Linoff 我的解决方案:

SELECT o.data1, o.data2, o.time FROM chronicle AS o JOIN (
    SELECT Min(id) as id FROM chronicle GROUP BY DATE(time), data1, data2
    UNION SELECT Max(id) as id FROM test_chronicle GROUP BY DATE(time), data1. data2
) AS n ON o.id = n.id;

从这里开始,只需引用同一个表即可删除行。

【问题讨论】:

  • 可以依赖 id 作为顺序吗?意思是 id 10 的日期/时间总是等于或早于 id 11?
  • 您想删除旧记录的原因是什么?是否与查询性能不佳有关?还是磁盘空间使用情况?
  • @JonathanDahan 他们肯定不会是连续的,因为正在删除记录。但我认为你实际上的意思是单调递增。据我所知,如果 id_a ,mysql 的 auto_increment 默认情况下是单调递增的。
  • @JonathanDahan 两者。有很多重复记录。
  • 好吧,为了性能,你可以简单地添加一个索引来解决问题。对于空间,是的,您需要删除记录。

标签: mysql sql greatest-n-per-group


【解决方案1】:

这将提高搜索日期时的性能。

ALTER TABLE chronicle
ADD INDEX `ix_chronicle_time` (`time` ASC);

这将删除记录:

CREATE TEMPORARY TABLE #tmp_ids (
  `id` INT NOT NULL,
  PRIMARY KEY (`id`)
);

INSERT INTO #tmp_ids (id)
SELECT
    min(id)
FROM
    chronicle
GROUP BY
    CAST(day as DATE),
    data1,
    data2
UNION
SELECT
    Max(id)
FROM
    chronicle
GROUP BY
    CAST(day as DATE),
    data1,
    data2;

DELETE FROM
    chronicle
WHERE
    ID not in (select id FROM #tmp_ids)
    AND date <= '2015-01-01'; -- if you want to consider all dates, then remove this condition

【讨论】:

  • 谢谢。这与@Gordan Linoff 的回答有同样的问题:我得到了每天的第一条/最后一条记录,而不是每天每组不同数据的最后一条记录。我在底部添加了一个重要说明。
  • 您的解决方案并不完全适合我,但这个概念有效。我不知道所有值上的 GROUP BY 就是所需要的。谢谢。
  • 是的,同样重要的是要注意,当您进行分组时,您不能选择不在分组中的任何内容,否则您会得到不正确的结果。唯一的例外是当您使用诸如 min()、max()、avg()、sum()、count() 等聚合函数时。一些 mysql 安装允许执行错误的 group bys(遗留支持),而其他人只强制执行良好的分组。
【解决方案2】:

你的想法是对的。您只需要重新加入即可获取原始信息。

SELECT c.*
FROM chronicle c JOIN
     (SELECT date(time) as day, min(time) as mint, max(time) as maxt
      FROM chronicle
      GROUP BY date(time)
     ) cc
     ON c.time IN (cc.mint, cc.maxt);

请注意,join 条件不需要明确包含day,因为它是time 的一部分。当然,如果你愿意,你可以添加date(c.time) = cc.day

建议您创建一个新表,而不是删除原始表中的行。事情是这样的:

create table ChronicleByDay like chronicle;

insert into ChronicleByDay
    SELECT c.*
    FROM chronicle c JOIN
         (SELECT date(time) as day, min(time) as mint, max(time) as maxt
          FROM chronicle
          GROUP BY date(time)
         ) cc
         ON c.time IN (cc.mint, cc.maxt);

这样,您可以在需要时获得更详细的信息。

【讨论】:

  • 谢谢!这确实解决了获取最小值和最大值的问题,但仍然存在另一个问题:我得到了每天的第一条/最后一条记录,而不是每天每个不同数据集的最后一条记录。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-11
  • 1970-01-01
  • 2016-02-14
相关资源
最近更新 更多