我有一个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行。
答案 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。