优化SELECT-query - 索引使用等

时间:2012-06-20 07:23:29

标签: sql sqlite

有一个非常大的表,包含8个字段(我知道,不准确)。

在我的应用程序中,我重复运行这个SELECT:

SELECT d1, time, s1, s2 from Collection WHERE (d1 = 1) and (s1 = 1) and (time BETWEEN 5666300000 AND 566630700);

我使用不同的时间范围运行此SELECT。 d1是具有200-300个不同值的字段,与s1相同。主键是d1,time,s1。

我正在寻找有关优化表结构和查询的提示。问题是时间字段不是按升序/降序排列。所以这可能是一个需要一些时间的查询。我想知道索引我的时间字段。那么我是否必须更改我的查询?我那样的,怎么样?

我的查询是否有任何问题我没看到?这在我的应用程序中运行缓慢。 谢谢!

3 个答案:

答案 0 :(得分:2)

我不同意其他答案中提出的主键顺序。

您理想的情景(对于您的确切示例查询)是将所有相关记录彼此相邻。这样可以对您的数据进行单一搜索。例如,使用(d1, s1, time)作为集群主键,您可以将数据存储如下...

 d1 | s1 | time 
----+----+------
  1 |  1 | 1234
  1 |  1 | 1235    \
  1 |  1 | 1236     SELECT * FROM table WHERE d1 = 1 AND s1 = 1 AND time BETWEEN 1235 AND 1237
  1 |  1 | 1237    /
  1 |  1 | 1238
  1 |  2 | 1234
  1 |  2 | 1235
  1 |  2 | 1236
  1 |  2 | 1237
  1 |  2 | 1238

如果按照其他人的建议,您将time作为聚集索引中的第一个字段,那么会在一个连续的块中获取所有数据。相反,你可以获得每个时间值的单一搜索......

 time | d1 | s1
------+----+----
 1234 |  1 |  1     *Desired Row 1
 1234 |  1 |  2
 1235 |  1 |  1     *Desired Row 2
 1235 |  1 |  2
 1236 |  1 |  1     *Desired Row 3
 1236 |  1 |  2
 1237 |  1 |  1     *Desired Row 4
 1237 |  1 |  2
 1238 |  1 |  1     *Desired Row 5
 1238 |  1 |  2

这个结构实际上非常适合不同的查询......

SELECT * FROM yourTable WHERE time = 1234 AND d1 = 1 AND s2 BETWEEN 2 AND 3

这表明theree不是一个普遍完美的聚簇索引。那么,你如何选择聚集的内容,因为你只能有一个聚簇索引?

这取决于您的数据和查询。对于每个查询,您需要查看将要提取的多少个不同的连续数据块。尝试最小化这些块的数量是一个非常好的主意。但维护数据的顺序也是如此,以便它适合GROUP BY或ORDER by子句。 JOIN进一步加强了这一点。

对于您的示例查询,我建议的第一个索引确实是最好的。但不是所有的疑问。

此外,您需要考虑碎片。数据存储在页面中,您需要考虑数据的插入方式(在考虑这一点时将更新视为删除和插入)。因为可能任何插入通常都是比现有数据更新的时间值,所以在聚集索引中首先使用time将减少碎片。

例如,假装每个页面只能容纳三行数据。上面提到的两个索引看起来像这样......

 d1 | s1 | time            time | d1 | s1 
----+----+------          ------+----+----
  1 |  1 | 1234 \          1234 |  1 |  1   \
  1 |  1 | 1235  Page 1    1234 |  1 |  2    Page 1
  1 |  1 | 1236 /          1235 |  1 |  1   /
----+----+------          ------+----+----
  1 |  1 | 1237 \          1235 |  1 |  2   \
  1 |  1 | 1238  Page 2    1236 |  1 |  1    Page 2
  1 |  2 | 1234 /          1236 |  1 |  2   /
----+----+------          ------+----+----
  1 |  2 | 1235 \          1237 |  1 |  1   \
  1 |  2 | 1236  Page 3    1237 |  1 |  2    Page 3
  1 |  2 | 1237 /          1238 |  1 |  1   /
----+----+------          ------+----+----
  1 |  2 | 1238 -Page 4    1238 |  1 |  2   -Page 4

现在,尝试插入d1 = 1, s1 = 1, time = 1239

 d1 | s1 | time            time | d1 | s1 
----+----+------          ------+----+----
  1 |  1 | 1234 \          1234 |  1 |  1   \
  1 |  1 | 1235  Page 1    1234 |  1 |  2    Page 1
  1 |  1 | 1236 /          1235 |  1 |  1   /
----+----+------          ------+----+----
  1 |  1 | 1237 \          1235 |  1 |  2   \
  1 |  1 | 1238  Page 2    1236 |  1 |  1    Page 2
 *1 |  1 | 1239*/          1236 |  1 |  2   /
----+----+------          ------+----+----
  1 |  2 | 1234 -Page 3    1237 |  1 |  1   \
----+----+------           1237 |  1 |  2    Page 3
  1 |  2 | 1235 \          1238 |  1 |  1   /
  1 |  2 | 1236  Page 4   ------+----+---- 
  1 |  2 | 1237 /          1238 |  1 |  2   -Page 4
----+----+------           1239 |  1 |  1   /
  1 |  2 | 1238 -Page 5

左边的版本必须创建一个新页面。右边的版本继续填充现有页面。

当出现碎片时,通常有维护计划可以弥补碎片。这通常是一个过夜过程。

这有点复杂不是吗?好吧,关于这个主题,有完整的书籍。

在成为问题之前,我通常不会过多担心碎片。但这确实是值得记住的事情。

答案 1 :(得分:1)

我建议按时+ d1 + s1(按此顺序)构建聚簇索引(主键)。这将确保数据按时间顺序物理存储,然后是d1和s1

答案 2 :(得分:1)

首先,正如npe所说,你不应该把时间用作主要的。我认为在time - d1 - s1上添加主要索引。通过这种方式,您将有时间作为主要主要索引,因此所有之间的关系都非常快。只有这样才会出现d1和s1。此外,将d1和s1放在尽可能小的数据类型中。如果它只有1和0,则将其放入bool中,依此类推。这样可以加快检查速度。