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

时间:2013-04-10 08:35:20

标签: 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是典型的原因),服务器版本和源表中包含的数据,所以我想可以说<使用此方法,strong>行顺序无法预测。可能它取决于内部优化。

然而,问题现在我无法想到任何能给我预期结果的替代解决方案。

由于结果集可以获得数千行,因此将所有数据加载到内存中然后手动INSERT是不可行的。

有什么建议吗?


编辑:有关进一步说明,请查看以下查询:

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;

这会产生以下结果为“expected”:

+-------+----------------------+----+
| 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错误,我完全理解我的方法目前提供了不可预测的结果。不过,我不知道如何调整它以获得可预测的结果。

2 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:1)

也许你可以将select包装成另一个选择:

SET @counter := 0;

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

...但你仍然受到MySQL优化器愚蠢的支配。

这就是我发现的关于这个主题的全部内容,但我找不到一个难以保证:

Pure-SQL Technique for Auto-Numbering Rows in Result Set

http://www.xaprb.com/blog/2006/12/02/how-to-number-rows-in-mysql/

http://www.xaprb.com/blog/2005/09/27/simulating-the-sql-row_number-function/