如何以最快的方式获取给定ID的最新记录? (MS SQL)

时间:2019-11-15 08:15:21

标签: sql sql-server tsql query-performance

我想寻求您的帮助。

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

id | sequenceId
---------------
1 | 1
1 | 2
1 | 3
2 | 1
2 | 2
2 | 3
2 | 4
...

也有其他专栏,但现在并不重要。另一件事,键是键对(id,sequenceId),它们在表中建立索引。我想要的是获取给定ID的最后一行。 例如,如果myId = 1->给我(1,3),myId = 2->给我(2,4)记录,依此类推。 在我的表格中,有500个ID,每个ID具有50000个序列ID,因此记录的大小为500 * 50000

我的查询:

SELECT
     myId AS 'MyId',
     MAX(sequenceId) AS 'SequenceId'
FROM myTable
WHERE myId in (SELECT myId from @MyIds)
GROUP BY(myId)
OPTION (RECOMPILE);

不幸的是,这并没有我想要的那么快。在我的尝试中,@ MyIds包含所有ID(1-500),在这种情况下,执行时间约为1秒。但我想使其更快。

您知道我如何使其更快吗?也许另一个查询比我使用的更好?

谢谢您的回答。

Br。

6 个答案:

答案 0 :(得分:2)

您的查询正确且相对最佳;除了用索引临时表替换表变量之外,以其他方式重写它可能不会带来任何改善。

性能优化通常与索引有关。根据是否为id列建立索引,以下选项之一应该会有所帮助:

create index [IX_mytable_myid_sequenceid] on dbo.mytable (myid, sequenceid desc);

如果表的聚集索引是在myId列上创建的,则可以节省一些空间:

create index [IX_mytable_sequenceid] on dbo.mytable (sequenceid desc);

排序顺序很重要,因为不幸的是,SQL Server在必须执行向后扫描/查找时无法使用并行计划。但是,您可以尝试对asc列进行descsequenceId排序;可能,这对您的具体情况没有影响。

无论索引如何,您可能都需要用临时表替换表变量。根据所使用的SQL Server版本,基数估计器假定表变量具有1或100行。如果您的数据量未达到预期,执行计划将是次优的。因此,代码应如下所示:

create table #list (Id int primary key);

insert into #list (Id)
-- Assuming there are no duplicates, otherwise add DISTINCT
select MyId from @MyIds;

SELECT
     t.myId AS 'MyId',
     MAX(t.sequenceId) AS 'SequenceId'
FROM myTable t
  inner join #list l on l.Id = t.myId
GROUP BY t.myId
-- OPTION (RECOMPILE);

是否应离开option子句取决于性能。

答案 1 :(得分:0)

您可以按以下方式尝试INNER JOIN-

SELECT
A.myId AS 'MyId',
MAX(A.sequenceId) AS 'SequenceId'
FROM myTable A
INNER JOIN @MyIds B
ON A.myId = B.myId
GROUP BY(A.myId)

以下脚本将为您返回每个myID的最大序列值-

SELECT * FROM 
(
    SELECT myId,sequenceId, 
    ROW_NUMBER() OVER(PARTITION BY myId ORDER BY sequenceId DESC) RN
    FROM  myTable
)A
WHERE RN = 1

答案 2 :(得分:0)

首先,@ MyIds是一个表变量,不是吗?您如何声明呢?它被索引了吗?在上面添加主键:

DECLARE @MyIds TABLE (ID INT PRIMARY KEY)

第二,请确保您的密钥位于myId+sequenceId而不是sequenceId+myId

第三,避免使用IN子句包含很多项,这是一个瓶颈

这应该是您最好的选择:

SELECT myId MyId, MAX(sequenceId) SequenceId
FROM myTable t
WHERE EXISTS (SELECT TOP 1 'X' X from @MyIds m WHERE m.myId = t.myId)
GROUP BY myId

您也可以尝试在分组后强制过滤器,试试看:

SELECT * 
FROM (
    SELECT TOP (9223372036854775807) myId MyId, MAX(sequenceId) SequenceId
    FROM myTable t
    GROUP BY myId
    ORDER BY myId
) T
WHERE EXISTS (SELECT TOP 1 'X' X from @MyIds m WHERE m.myId = t.myId)

答案 3 :(得分:0)

Console.Write

答案 4 :(得分:0)

我建议以下内容:

select i.myId,
       (select max(t.sequenceId)
        from myTable t
        where t.myId = i.myId
       )
from @MyIds i;

然后,为了提高性能,您希望在myTable(myId, sequenceId desc)上建立索引。

答案 5 :(得分:0)

如前所述-如果您在myId上有一个索引,则sequenceId您的查询应该运行。列存储索引和/或batch mode processing可以大大加快处理速度。如果可以在索引中添加过滤器,那就更好了。内存优化表和/或其他对象也可以加快速度。所有这些,让我介绍一种新的索引-Virtual Index。您可以利用RangeAB或Jeff Moden的FnTally

使用dbo.rangeAB虚拟索引

首先快速热身。让我们创建一个查询,以升序和降序返回数字1到10。让我们在没有索引且没有并行执行计划的情况下进行操作。

SELECT   r.RN, r.Op
FROM     dbo.rangeAB(1,10,1,1) AS r
ORDER BY r.RN
OPTION (QUERYTRACEON 8649)

返回:

RN                   Op
-------------------- --------------------
1                    10
2                    9
3                    8
4                    7
5                    6
6                    5
7                    4
8                    3
9                    2
10                   1

执行计划:

enter image description here

看^^^不排序!!!因此,对于降序ORDER BY,您的查询如下所示:

-- Last 3 Numbers - no index, no sort + Descending Order + Parallelism (if you want it)
SELECT TOP (3) r.Op
FROM     dbo.rangeAB(1,10,1,1) AS r
ORDER BY r.RN ASC
--OPTION (QUERYTRACEON 8649);

在这里,我们有一个虚拟的FORWARD-ORDER扫描,该扫描以降序返回行。 无需索引,无需排序运算符!这不是什么把戏,让我们两次调用该函数,并做一些需要排序的事情(按来自独立函数调用的两列分组,联接,传统聚合以及我们会以演示文稿ORDER BY(而不是按窗口排名功能排序)...

DECLARE @rows INT = 10;

SELECT
  RN1      = r.RN,
  RN1_DESC = @rows+1-r.RN,
  RN2      = r2.RN,
  RN1_Low  = MIN(r.RN),
  RN1_High = MAX(r.RN),
  RN1_Avg  = AVG(r.RN)
FROM      dbo.rangeAB(1,@rows,1,1) AS r
LEFT JOIN dbo.rangeAB(1,3,1,1)  AS r2 ON r.RN = r2.RN
GROUP BY  r.RN, r2.RN 
ORDER BY  DENSE_RANK() OVER (ORDER BY r.RN);

返回:

RN1   RN1_DESC    RN2      RN1_Low    RN1_High   RN1_Avg
----- ----------- -------- ---------- ---------- --------------------
1     10          1        1          1          1
2     9           2        2          2          2
3     8           3        3          3          3
4     7           NULL     4          4          4
5     6           NULL     5          5          5
6     5           NULL     6          6          6
7     4           NULL     7          7          7
8     3           NULL     8          8          8
9     2           NULL     9          9          9
10    1           NULL     10         10         10

结果集本来就没有意义,这是我感兴趣的执行计划;我们来看一下。

enter image description here

返回您的查询

-- Sample data
DECLARE @table TABLE (id INT NOT NULL, sequenceId INT NOT NULL)--, INDEX xxx(id,sequenceId))
INSERT @table VALUES(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(2,4)

SELECT r.RN, sequenceId = MAX(t.sequenceId)
FROM        
(
  SELECT MIN(t.id), MAX(t.id), MIN(t.sequenceId), MAX(t.sequenceId) 
  FROM   @table AS t
) AS mm(Mn,Mx,Mns,Mxs)
CROSS APPLY dbo.rangeAB(mm.Mn,mm.Mx,1,1)   AS r
CROSS APPLY dbo.rangeAB(mm.Mns,mm.Mxs,1,1) AS r2
JOIN        @table                         AS t 
  ON        r.RN = t.id AND r2.RN = Mxs
GROUP BY    r.RN
OPTION (QUERYTRACEON 8649);

没有索引,没有排序,没有I / O,没有并行性丢失(无论方向如何)和RBAR!

enter image description here