mysql - 如何将 *sorted* SELECT 结果存储在另一个表中?

标签 mysql sql innodb

在我的项目中,我经常需要将 SELECT 的结果存储在另一个表中(我们称之为“结果集”)。原因是在 Web 应用程序中动态显示大量行,同时根据需要仅加载小块。

通常,这是通过如下查询完成的:

SET @counter := 0;
INSERT INTO resultsetdata
  SELECT "12345", @counter:=@counter+1, a.ID
    FROM sometable a
    JOIN bigtable b
      WHERE (a.foo = b.bar)
    ORDER BY a.whatever DESC;

固定的 "12345" 值只是一个将“结果集”标识为一个整体并随每个查询而变化的值。第二列是递增索引计数器,旨在允许直接访问结果中的特定行,ID 列引用源数据表中的特定行。

当应用程序需要一定范围的结果时,我只需将 resultsetdata 与源表连接起来即可获取详细数据 - 这与 resultsetdata 查询相比更快以上可能需要 2-3 秒才能完成(这解释了为什么我需要这个中间表)。

SELECT 查询本身与这个问题无关。

resultsetdata 具有以下结构:

CREATE TABLE `resultsetdata` (
  `ID`      int(11) NOT NULL,
  `ContIdx` int(11) NOT NULL,
  `Value`   int(11) NOT NULL,
  PRIMARY KEY (`ID`,`ContIdx`)
) ENGINE=InnoDB;

这通常很有效,但最近我们注意到在某些 情况下结果的 ORDER 不正确。这取决于查询本身(例如,添加 DISTINCT 是一个典型原因)、服务器版本和源表中包含的数据,所以我想可以说使用此方法行顺序是不可预测的。可能这取决于内部优化。

但是,问题现在是我想不出任何替代解决方案来提供我预期的结果。

由于结果集可能有数千行,将所有数据加载到内存中然后手动INSERTing 是不可行的。

有什么建议吗?


编辑:要进一步说明,请查看这些查询:

DROP TABLE IF EXISTS test;
CREATE TABLE test (ID INT NOT NULL, PRIMARY KEY(ID)) ENGINE=InnoDB;
INSERT INTO test (ID) VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);

SET @counter:=0;
SELECT "12345", @counter:=@counter+1, ID
  FROM test
  ORDER BY ID DESC;

这会产生以下“预期”结果:

+-------+----------------------+----+
| 12345 | @counter:=@counter+1 | ID |
+-------+----------------------+----+
| 12345 |                    1 | 10 |
| 12345 |                    2 |  9 |
| 12345 |                    3 |  8 |
| 12345 |                    4 |  7 |
| 12345 |                    5 |  6 |
| 12345 |                    6 |  5 |
| 12345 |                    7 |  4 |
| 12345 |                    8 |  3 |
| 12345 |                    9 |  2 |
| 12345 |                   10 |  1 |
+-------+----------------------+----+
10 rows in set (0.00 sec)

如前所述,在某些情况下(抱歉,我无法在此处提供测试用例),这可能会导致类似于以下的结果:

+-------+----------------------+----+
| 12345 | @counter:=@counter+1 | ID |
+-------+----------------------+----+
| 12345 |                   10 | 10 |
| 12345 |                    9 |  9 |
| 12345 |                    8 |  8 |
| 12345 |                    7 |  7 |
| 12345 |                    6 |  6 |
| 12345 |                    5 |  5 |
| 12345 |                    4 |  4 |
| 12345 |                    3 |  3 |
| 12345 |                    2 |  2 |
| 12345 |                    1 |  1 |
+-------+----------------------+----+

我并不是说这是一个 MySQL 错误,我完全理解我的方法目前会提供不可预测的结果。不过,我不知道如何调整它以获得可预测的结果。

最佳答案

这是因为插入记录时的排序顺序与检索它们时的顺序无关。

当您检索它们时,将创建一个查询计划。如果您的 SELECT 语句中没有指定 ORDER BY,那么顺序将取决于生成的查询计划。这就是为什么它是不可预测的,添加 DISTINCT 可以改变顺序。

解决方案是存储足够的数据,以便您可以使用 ORDER BY 子句以正确的顺序检索它们。在您的情况下,您已经通过 a.whatever 订购了您的数据。 a.whatever 可以存储在 resultsetdata 中吗?如果是这样,那么您可以按正确的顺序读出记录。

关于mysql - 如何将 *sorted* SELECT 结果存储在另一个表中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15920829/

相关文章:

Mysql innodb 压缩没有给出正确的结果

mysql - 如何为MySQL InnoDB表正确建立索引并选择最佳主键

php - 使用 php 表单更新 WordPress 插件表行

MYSQL - 多个值插入到另一个表中不存在的表中

php - php 中的 mysql 语法错误,输入 phpmyadmin 时工作正常

sqlite select max查询和排序

mysql - 我应该牺牲 innodb_buffer_pool_size/RAM 来为 query_cache_size 腾出空间吗?

python - 在 MacOS Sierra (Python 3.6) 上安装 mysqlclient 时出错

php - 在虚拟字段中访问多个外部字段?

sql - 定义表格:允许为空或坚持“n/a”