如何提高 SQL Server 查询的性能以选择具有值的行不在子查询中的一次计数
Posted
技术标签:
【中文标题】如何提高 SQL Server 查询的性能以选择具有值的行不在子查询中的一次计数【英文标题】:How to improve performance of SQL Server query to select rows with value not in subquery with one count 【发布时间】:2017-05-10 19:28:10 【问题描述】:我正在运行 SQL Server 2016。
对于只有一行具有特定 ConvID (big int) 值的情况,我正在尝试消除所有行。我的最终目标是在特定 ConvID 值的行数为奇数时消除最后一行对话。如果我可以直接解决为每个具有奇数行(即具有该特定 convID 值的奇数行)的 convID 消除最后一行(按 chat_id 排序)的解决方案,那将是理想的。
源数据的一个示例在我的另一个问题中,它被标记为“所需的输出”:Create Group-able ID perhaps with RANK or ROW_NUMBER to concat row values with elusive sequential alternations in SQL Server
这是我的查询:
INSERT INTO dbo.RestoredConversationLinesConcatenated_WithChatIDWithoutSingleChats
(chat_id,
SpeakerName,
RelativeSpeakerID,
ConvID,
customer_id,
student_id,
teacher_id,
district_id,
school_id,
clas-s-room_id,
item_id,
math_lesson_id,
Label)
SELECT *
FROM dbo.RestoredConversationLinesConcatenated_WithChatID AS B
WHERE B.ConvID NOT IN (SELECT A.ConvID--, COUNT(*) AS Instances
FROM dbo.RestoredConversationLinesConcatenated_WithChatID AS A
GROUP BY A.ConvID
HAVING COUNT(*) = 1)
ORDER BY B.chat_id
这是估计的查询执行计划(您可能需要在新窗口中打开并调整大小才能完整查看):
我的源表大小约为 1700 万行,因此查询性能需要更好。我在上面的查询运行了一个多小时后停止了它,但只在目标表中插入了 40 行,并且在实时查询统计信息面板中完成率为 0%。
我使用 INSERT INTO 而不是 SELECT INTO 的原因是该表有一个 IDENTITY 列可以自动递增,因为删除行会使 ID 全部乱序。 (否则,我不反对删除表并改用 SELECT INTO。)
编辑: 这是我消除只有一个 ConvID 值的行的最终解决方案:
SELECT *
INTO dbo.RestoredConversationLinesConcatenated_WithChatIDWithoutSingleChats
FROM dbo.RestoredConversationLinesConcatenated_WithChatID c
EXCEPT
SELECT *
FROM dbo.RestoredConversationLinesConcatenated_WithChatID b
WHERE NOT EXISTS (SELECT 1
FROM dbo.RestoredConversationLinesConcatenated_WithChatID a
WHERE a.ConvId = b.ConvId
AND
a.chat_id <> b.chat_id -- or something that uniquely identifies each row
)
我还根据 SQL Server 数据库引擎优化顾问的建议创建了很多分区和统计信息以及两个索引。最终查询在 42 秒内完成。
【问题讨论】:
如果您的问题还没有解决,那么您可以尝试使用 Row_number ,partition 已经解决了,不过还是谢谢。 【参考方案1】:使用not exists
:
SELECT *
FROM dbo.RestoredConversationLinesConcatenated_WithChatID b
WHERE NOT EXISTS (SELECT 1
FROM dbo.RestoredConversationLinesConcatenated_WithChatID a
WHERE a.ConvId = b.ConvId AND
a.ChatId <> b.ChatId -- or something that uniquely identifies each row
);
我不确定哪个 id
唯一标识每一行。但这就是 ChatId
的目的——如果这不是正确的 id,请使用正确的。
为了使其发挥最佳效果,您需要在(ConvId, ChatId)
上建立一个索引——一个复合索引,其中的列按该顺序排列。
【讨论】:
此查询非常快,它为我提供了给定 ConvID 值出现一次的行,但我试图返回不包括这些值的行。换句话说,我想要该结果集中不存在的值。我想要案例的行,不包括给定 ConvId 只有一行的案例。 @devinbost 。 . .您可以使用EXISTS
代替NOT EXISTS
。
当我这样做时,结果集比我预期的要小得多。
一个疑问,由来已久。当我真的需要所有列名时,是否可以使用“*”,或者为了性能,我应该提及所有列名。
@KumarHarsh 。 . .不要在存储过程或视图或其他已保存的查询中使用*
。如果每次都重新编译查询是安全的,所以随便使用*
就可以了。以上是关于如何提高 SQL Server 查询的性能以选择具有值的行不在子查询中的一次计数的主要内容,如果未能解决你的问题,请参考以下文章