如何使按查询分组更快?

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)

执行计划: 在此处输入图片说明

Ric*_*ons 5

您的索引对查询没有帮助。您按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)


Dev*_*art 4

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)

  • 这正是我想说的,但你根本没有解释它。 (10认同)
  • 虽然此代码可以回答问题,但提供有关此代码为何和/或如何回答问题的附加上下文可以提高其长期价值。 (5认同)