Mysql 相同记录集上的相同更新SQL似乎与MariaDB不一致

Mysql 相同记录集上的相同更新SQL似乎与MariaDB不一致,mysql,sql,mariadb,Mysql,Sql,Mariadb,问题:逻辑似乎与MariaDB不符 目标:创建自引用ID列值以引用上一条记录 说明:我们有一个非常大的记录表,ID是记录的主键和时间,分布在终端之间。我们希望创建一个列来保存同一终端当前记录的前一条记录的ID值,具体取决于记录的时间,我们将使用该记录进行自连接以在同一记录集中并排选择当前记录的前一条记录,以提高查询性能,而不是在每次查询过程中动态选择以前的记录 期望值:了解实际发生的情况,以及具有最佳性能的跨数据库引擎(至少是MySQL和MariaDB)兼容的更新查询 UPDATE tst_te

问题:逻辑似乎与MariaDB不符

目标:创建自引用ID列值以引用上一条记录

说明:我们有一个非常大的记录表,ID是记录的主键和时间,分布在终端之间。我们希望创建一个列来保存同一终端当前记录的前一条记录的ID值,具体取决于记录的时间,我们将使用该记录进行自连接以在同一记录集中并排选择当前记录的前一条记录,以提高查询性能,而不是在每次查询过程中动态选择以前的记录

期望值:了解实际发生的情况,以及具有最佳性能的跨数据库引擎(至少是MySQL和MariaDB)兼容的更新查询

UPDATE tst_terminaldata SET PreviousID = NULL;

UPDATE tst_terminaldata AS TD
SET TD.PreviousID =
    (SELECT TDP.ID FROM
        (SELECT TDP2.ID, TDP2.Time, TDP2.TerminalID
                         FROM tst_terminaldata AS TDP2
                         ORDER BY 
  TDP2.Time DESC, TDP2.ID DESC /* Works for- Windows: MySQL 8.0.16, 5.7.11; Linux: MySQL 8.0.13, 8.0, 5.7.20 */
  TDP2.Time ASC, TDP2.ID ASC /* Works for- Windows: MariaDB 10.4.7; Linux: MariaDB 10.2.25, 10.1.40 */
        ) AS TDP
        WHERE TDP.TerminalID = TD.TerminalID
          AND TDP.Time < TD.Time
        LIMIT 1
    ) 
WHERE TD.PreviousID IS NULL;

SELECT TD.* FROM tst_terminaldata AS TD
      ORDER BY TD.TerminalID ASC, TD.Time DESC, TD.ID DESC;
看起来你需要在这里使用函数-

UPDATE tst_terminaldata AS TD
SET TD.PreviousID = (SELECT TerminalID, LAG(TerminalID) OVER(PARTITION BY TerminalID ORDER BY TIME
                     FROM tst_terminaldata) T
WHERE T.TerminalID = TD.TerminalID

虽然我没有尝试过,但我认为这应该有效。

您可以使用
连接
滞后()


好的,经过一些研究,我发现这是一个特定的问题,他们说这不是一个bug,但最终模拟了一个bug结果

降序排序是正确的方法,它适用于MySQL,但不适用于MariaDB

但是,在比较了各种变体之后,我发现,将ORDER BY子句从内部SELECT取出到外部SELECT会使查询MySQL和MariaDB都与性能降低兼容

但令人惊讶的是,至少在我的例子中,在双核2GB RAM的CentOS VPS上,每个跟踪设备每分钟有12条记录,大约8500条这样的设备,它看起来像是过程的一点,用前一条记录的主键值更新每一条记录,作为参考与动态连接没有任何区别,有时,动态连接的性能比直接连接高出约15%

直接连接

ID  TerminalID  Time    PreviousID
1   1   2019-07-29 13:56:37 NULL
2   1   2019-07-29 13:56:52 NULL
3   2   2019-07-29 13:57:01 NULL
4   1   2019-07-29 13:57:02 NULL
5   2   2019-07-29 13:57:08 NULL
6   1   2019-07-29 13:57:17 NULL
7   2   2019-07-29 13:57:23 NULL
8   1   2019-07-29 13:57:32 NULL
SELECT * FROM tst_terminaldata AS TD
LEFT JOIN tst_terminaldata AS TDP ON TDP.ID = TD.PreviousID
SELECT * FROM tst_terminaldata AS TD
LEFT JOIN tst_terminaldata AS TDP ON TDP.ID = (
    SELECT TDP2.ID FROM tst_terminaldata AS TDP2 
    WHERE TDP2.Time > TD.Time AND TDP2.TerminalID = TD.TerminalID 
    ORDER BY TDP2.Time DESC, TDP2.ID DESC
    LIMIT 1
)
动态连接

ID  TerminalID  Time    PreviousID
1   1   2019-07-29 13:56:37 NULL
2   1   2019-07-29 13:56:52 NULL
3   2   2019-07-29 13:57:01 NULL
4   1   2019-07-29 13:57:02 NULL
5   2   2019-07-29 13:57:08 NULL
6   1   2019-07-29 13:57:17 NULL
7   2   2019-07-29 13:57:23 NULL
8   1   2019-07-29 13:57:32 NULL
SELECT * FROM tst_terminaldata AS TD
LEFT JOIN tst_terminaldata AS TDP ON TDP.ID = TD.PreviousID
SELECT * FROM tst_terminaldata AS TD
LEFT JOIN tst_terminaldata AS TDP ON TDP.ID = (
    SELECT TDP2.ID FROM tst_terminaldata AS TDP2 
    WHERE TDP2.Time > TD.Time AND TDP2.TerminalID = TD.TerminalID 
    ORDER BY TDP2.Time DESC, TDP2.ID DESC
    LIMIT 1
)
  • MySQL和MariaDB都会忽略派生表中的排序依据(至少在您尝试的版本中是这样)
  • 优化器可以按任意顺序显示行
  • MySQL和MariaDB的优化器代码在这方面的差异大约为5.6/10.0
这应该可以解释为什么根据
排序依据和版本,您似乎会得到不同的结果

如果您每秒插入1700行,我希望您正在批处理它们并使用SSD


我不知道查询的目标是什么,但我建议在数据到达时对其进行消化,而不是在“滞后”类型的查询中苦苦挣扎。

以防万一,如果您需要保存数据的表;我觉得我应该澄清,TerminalID不是这里的焦点。您可以删除TerminalID 1以外的所有其他记录,并删除与TerminalID相关的SQL,并且将遇到相同的问题。MySQL:错误代码:1051。未知表'td'MySQL:错误代码:1064。您的SQL语法有错误;检查与MySQL服务器版本对应的手册,以获取第行“”附近要使用的正确语法3@brokenArrow,请立即重试。MariaDB::错误代码:1064。您的SQL语法有错误;检查与您的MariaDB服务器版本对应的手册,以了解使用near'的正确语法(从第2行的tst_terminaldat开始,按TerminalID ORDER BY TIME划分你可以把TerminalID放在一边。查看我在帖子上的评论。你使用的是哪个MaraiDB版本。你是正确的。但是,MySQL尊重内部ORDER BY子句。更有趣的是,MariaDB实际上并没有忽略它,只是行为不当,chang调用内部ASC DESC以相反的方式更改MariaDB的结果集。是的,我使用每分钟批插入,并且它是SSD。今天它似乎是尊重ASC还是将其转换为DESC都无关紧要。明天它可能会做一些不同的事情。不要依赖内部
ORDER BY
。(这一点在这里的几个问答中进行了更详细的讨论,加上最初在MariaDB和后来在MySQL中的一些不知名的地方)同意这一点。MySQL在固化方面似乎比MariaDB更好。