我想寻求您的帮助。
我有一个看起来像这样的表:
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。
答案 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
列进行desc
和sequenceId
排序;可能,这对您的具体情况没有影响。
无论索引如何,您可能都需要用临时表替换表变量。根据所使用的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
执行计划:
看^^^不排序!!!因此,对于降序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
结果集本来就没有意义,这是我感兴趣的执行计划;我们来看一下。
返回您的查询
-- 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!