在我的项目中,我经常需要将 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
是一个典型的原因)、服务器版本和源表中包含的数据,所以我猜可以说使用这种方法无法预测行顺序。可能它取决于内部优化。
但是,现在的问题是我想不出任何可以给我预期结果的替代解决方案。
由于结果集可以得到几千行,因此将所有数据加载到内存中然后手动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;
这会产生以下“预期”结果:
+-------+----------------------+----+
| 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 错误,我完全理解我的方法目前提供了不可预测的结果。不过,我不知道如何调整它以获得可预测的结果。