在我的项目中,我经常需要将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错误,我完全理解我的方法目前提供了不可预测的结果。不过,我不知道如何调整它以获得可预测的结果。
答案 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/