sqlite查询优化

时间:2009-06-08 16:11:22

标签: sql sqlite

我有一个sqlite表actions看起来像这样:

uuid varchar (36)
actiondate int
username varchar (16)
mood int
bonus int
status varchar (80)
... bunch of other similar fields (all short varchar or int fields)

对于大多数类型的查询来说,这个设计似乎足够高效,但是在特定场景中有点挣扎,我需要获取一些关于每个用户在给定日期执行的最新操作的数据。

我希望能够做到这样的事情:

SELECT status, actiondate
FROM actions WHERE actiondate < 20061231
GROUP BY username
ORDER BY actiondate DESC

但是,聚合不是针对order子句进行的,order子句只是确定返回结果的顺序,这是有道理的。

所以,我有这个:

SELECT actiondate, status FROM actions
WHERE actiondate < 20061231 and
uuid = (SELECT uuid from actions as alt
        WHERE alt.username = actions.username
        ORDER BY actiondate DESC LIMIT 1)

有没有更好的方法来进行此类查询?更好的桌面布局?目前这种查询在我的开发盒上需要大约400毫秒,如果我能在100毫秒左右剃掉它(我的目标时间实际上是100毫秒,但我对这是否可管理持怀疑态度)会很好。 / p>

我显然已经获得了用户名和日期的索引(我实际上有几个:一个似乎很适合慢查询的一个;一个用户名;一个在日期ASC;一个在日期DESC和一个在UUID)。

FWIW,action表可能有100到30,000行。

2 个答案:

答案 0 :(得分:2)

速度前的正确性 - 您的查询:

SELECT actiondate, status FROM actions
WHERE actiondate < 20061231 and
uuid = (SELECT uuid from actions as alt
        WHERE alt.username = actions.username
        ORDER BY actiondate DESC LIMIT 1)

不执行您描述的任务 - 内部选择可能会返回uuid以执行晚于2061231的操作,然后外部选择将不会为该用户名提供任何结果。我认为你可以解决这个问题,我将动作日期的WHERE检查作为嵌套选择中的AND。 (我怀疑这会加快速度,但至少它应该使行为正确 - 让我们知道它是如何影响速度的!)。

答案 1 :(得分:1)

您的索引应涵盖查询中用于获得最佳性能的所有列。

在这种情况下,我不确定嵌套查询的性能。如果执行计划没有显示它将它转换为良好的嵌套连接,我宁愿加入子查询。

对于这样的事情,我可能会尽可能避免使用UUID,如果没有,我会确保它正在增加,所以你可以写:

SELECT actiondate
    ,status
FROM actions
INNER JOIN (
    SELECT username
        ,MAX(uuid) as last_uuid from actions
    WHERE actiondate < 20061231
    GROUP BY username
) AS last_occur
    ON last_occur.username = actions.username
    AND last_occur.last_uuid = actions.uuid
WHERE actiondate < 20061231

我认为这应该可以很好地使用用户名ASC,uuid DESC,INCLUDE(actiondate)的索引以及actiondate DESC上的索引,用户名ASC,INCLUDE(状态),但显然要查看查询计划。

如果没有增加的uuids,您将需要某种规则来确保您为一个人选择最新的操作,因为除非username,actiondate是唯一的,否则您的原始ORDER BY actiondate DESC限制1中没有任何内容可确保您每次都选择正确的行。如果username,actiondate是唯一的,那么您可以使用以下内容:

SELECT actiondate
    ,status
FROM actions
INNER JOIN (
    SELECT username
        ,MAX(actiondate) as last_actiondate from actions
    WHERE actiondate < 20061231
    GROUP BY username
) AS last_occur
    ON last_occur.username = actions.username
    AND last_occur.last_actiondate = actions.actiondate
WHERE actiondate < 20061231

如果它不是唯一的,它仍然有效,但你会在最后一个动作日期为一个人获得多个动作。在这种情况下,建议的索引也会有所不同(并且更好),因为不需要大的uuid。