bro*_*and 5 sql-server group-by
我的下一张表有近 200 万条记录,当然每天都在增加。一些表记录(columnId 下方是父表的外键,例如 DirectionId --> 方向表,...):
Id TypeId DirectionId UserId IndicatorId Date Size ExternalId
2003 100 1 1 1 2015-06-01 00:02:23.0000000 11931 28657340
2004 2 1 2 1 2015-06-01 00:03:21.0000000 10358 28657341
2005 2 2 2 1 2015-06-01 00:03:31.0000000 10848 28657342
2006 100 1 2 1 2015-06-01 00:03:52.0000000 7860 28657343
2007 100 1 3 1 2015-06-01 00:03:59.0000000 13353 28657344
Run Code Online (Sandbox Code Playgroud)
我需要获取最后一条消息 TypeId 和 DirectionId 的日期时间。下面的查询返回我需要的
select TypeId, DirectionID, max(date) as Date
from message
group by TypeId, DirectionID;
DirectionId TypeId Date
2 1 2015-06-05 15:12:37.0000000
1 1 2015-06-05 15:12:39.0000000
Run Code Online (Sandbox Code Playgroud)
问题是这个查询需要 2500 毫秒到 3000 毫秒来执行。我添加了索引:
CREATE NONCLUSTERED INDEX [date_index] ON [mqview].[Message] ([Date] ASC)
INCLUDE ([Id], [TypeId],[DirectionId], [UserId], [Size], [ExternalId]) WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)
怎样做才能更快地获得结果?
更新
使用建议的添加索引,我可以更快地获得结果,但现在我想通过如上所述的两个内部连接获得更快的结果。或者最终我可以从表 MessageDirection 和 MessageType 中执行额外的 2 个查询,如果无法提高以下查询的性能。
SET STATISTICS TIME ON
select mt.Code, md.Code, max(m.date) as Date
from
mqview.Message m
inner join mqview.MessageDirection md on (md.Id = m.DirectionId)
inner join mqview.MessageType mt on (mt.Id = m.TypeId)
group by mt.Code, md.Code
SET STATISTICS TIME OFF
Run Code Online (Sandbox Code Playgroud)
信息:
SQL Server Execution Times:
CPU time = 3343 ms, elapsed time = 2817 ms.
Run Code Online (Sandbox Code Playgroud)
您的索引对查询没有帮助。您按TypeId第一个分组,而索引的行按Id第一个排序。因此,要 group byTypeId和 then DirectionId,查询仍然必须扫描表中的每一行。然后,一旦它按这些值进行分组,它就必须查看每个组中的每一行以找到最大日期。
如果您先按 索引行TypeId,然后再按 索引行DirectionId,则分组会更快,因为行自然会按照索引中的分组顺序排列。如果您然后添加Date到索引,那么查询将知道每个组中的最后一行将是最高日期,这会稍微加快速度,但是如果您Date在索引中进行降序排序,则第一行每个组都会有最高的日期。这意味着只需查看每组中的第一行。这将极大地提高速度 - 您可能会发现使用此索引您的查询几乎是即时的。
由于索引现在包含查询中的所有值,因此甚至不需要访问表的实际行。数据库引擎可以直接从索引返回值。这从查询处理中删除了另一个步骤并使其再次更快。
您的CREATE INDEX声明将如下所示:
CREATE INDEX ix_myNewIndex ON [mqview].[Message] (TypeId, DirectionId, [Date] DESC)
Run Code Online (Sandbox Code Playgroud)
IF OBJECT_ID('tempdb.dbo.#temp') IS NOT NULL
DROP TABLE #temp
GO
CREATE TABLE #temp
(
Id INT PRIMARY KEY,
TypeId TINYINT,
DirectionId TINYINT,
UserId TINYINT,
IndicatorId TINYINT,
[Date] DATETIME2
)
CREATE /*UNIQUE*/ NONCLUSTERED INDEX ix ON #temp (TypeId, DirectionId, [Date] DESC) -- DESC
GO
INSERT INTO #temp (Id, TypeId, DirectionId, UserId, IndicatorId, [Date])
VALUES
(2003, 100, 1, 1, 1, '20150601 00:02:23.0000000'),
(2004, 2 , 1, 2, 1, '20150601 00:03:21.0000000'),
(2005, 2 , 2, 2, 1, '20150601 00:03:31.0000000'),
(2006, 100, 1, 2, 1, '20150601 00:03:52.0000000'),
(2007, 100, 1, 3, 1, '20150601 00:03:59.0000000')
SELECT TypeId, DirectionID, MAX([Date])
FROM #temp
GROUP BY TypeId, DirectionId
Run Code Online (Sandbox Code Playgroud)
更新:
SELECT mt.Code, md.Code, t.[Date]
FROM (
SELECT TypeId, DirectionID, [Date] = MAX([Date])
FROM mqview.[Message]
GROUP BY TypeId, DirectionId
) t
JOIN mqview.MessageDirection md on md.Id = t.DirectionId
JOIN mqview.MessageType mt on mt.Id = t.TypeId
Run Code Online (Sandbox Code Playgroud)