极长的“不在”请求
Posted
技术标签:
【中文标题】极长的“不在”请求【英文标题】:Extremely long "Not In" request 【发布时间】:2015-04-14 07:40:46 【问题描述】:我有两张桌子:
“Sessions” - 它具有 int 密钥标识,“session_id” - varchar, "device_category" - varchar 和其他一些列。 共有 149239 行。
Session_events" - 它有 int 键 身份,“session_id” - 唯一标识符和其他一些字段。 那里有 3140768 行。
这个表是从非关系型数据库 Cassandra 导入的,所以我没有在 MS SQL Server 设计器中创建任何连接。但是 session_id 列上的 Sessions 和 Session_events 之间的真正连接是多对多
现在我想删除所有未在个人计算机“device_category”上进行的网络会话。所以我运行请求Delete * FROM sessions where device_category != "PC"
那很快。现在我想从 Session_events 表中删除所有非 PC 会话。所以我运行请求
Delete FROM session_events where session_id Not In (SELECT distinct session_id FROM sessions)
该请求当前运行超过 24 小时,我不知道需要多长时间...
(我有 16 GB 内存和 Intel Xenon)。
我知道 Left Join 可以更快,但 20% 并不有趣。你知道如何更快地完成我的任务吗?
----
CREATE TABLE [dbo].[session_events](
[key] [bigint] IDENTITY(1,1) NOT NULL,
[session_id] [uniqueidentifier](max) NULL,
[visitor_id] [uniqueidentifier] NULL,
[shipping_method] [varchar](max) NULL,
[shipping_price] [varchar](max) NULL,
[site_id] [int] NULL,
[stream_data_chunk] [varbinary](max) NULL,
[total] [varchar](max) NULL,
[total_inc_tax] [varchar](max) NULL,
[tracker_ver] [varchar](max) NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
CREATE TABLE [dbo].[visitor_sessions](
[key] [int] IDENTITY(1,1) NOT NULL,
[visitor_id] [varchar](max) NULL,
[created] [varchar](max) NULL,
[session_id] [varchar](max) NULL
)
CONSTRAINT [PK_visitor_sessions4] PRIMARY KEY CLUSTERED
(
[key] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
【问题讨论】:
表中有索引吗?向我们展示CREATE TABLE
脚本和两个表上的索引。
我没有创建表脚本。我在每个表中创建了两个 bigint autoincrimented Identity 列,它们是可索引的
你可以用SSMS找到CREATE TABLE(我想你可以用SSMS连接数据库?)
@ypercube 我已经添加了
我在表 session_events
中没有看到 session_id
。您确定这是您运行的实际查询吗?
【参考方案1】:
一次删除大量数据意味着数据库引擎必须在单个事务中完成所有这些操作。当您实际上不需要它时,这意味着巨大的开销(例如,您不需要回滚整个操作,或者您不关心一致性 - 您只想删除所有内容,如果它在中间失败,您'将再次运行查询以删除其余部分)。
对于您的情况,您可以尝试批量删除。例如:
delete top 1000 from session_events where session_id Not In (SELECT distinct session_id FROM sessions)
重复直到表格为空。
另外,你是从错误的角度开始的。您最好先在两者之间创建一个外键,然后使用“on delete cascade”。这将自动删除所有不再具有有效session
的session_events
。如果您可以重新开始,它可能会明显更快。不过没有承诺:D
【讨论】:
【参考方案2】:为什么不使用左连接?另一种选择是使用 EXISTS 而不是 IN:
DELETE FROM Session_events
WHERE NOT EXISTS(
SELECT 1
FROM Session
WHERE Session.Session_Id = Session_events.Session_Id
)
【讨论】:
据我了解,这可能会给我带来高达 30% 的性能提升,这不是必需的,同样的任务将持续几天.. 或几周.. 不是是吗? @BransDs 我明白了。无论如何,Luaan 的答案比我好(并且得到了我的投票)。读完之后,我记得几年前也遇到过同样的问题。删除 10,000 条记录的块非常快。【参考方案3】:有时删除的问题是它正在等待获取所有相关行的锁。尝试循环删除。
DECLARE @MyCursor CURSOR;
DECLARE @MyField YourFieldDataType;//replace with the data type of session_id
BEGIN
SET @MyCursor = CURSOR FOR
select session_id from session_events minus select session_id from sessions
OPEN @MyCursor
FETCH NEXT FROM @MyCursor
INTO @MyField
WHILE @@FETCH_STATUS = 0
BEGIN
delete session_events where session_id = @MyField
FETCH NEXT FROM @MyCursor
INTO @MyField
END;
CLOSE @MyCursor ;
DEALLOCATE @MyCursor;
END;
您也可以尝试将not in
重写为in
:
delete from session_events where session_id in (select session_id from session_events minus select session_id from sessions)
【讨论】:
OP 的引擎是 MS SQL。这是哪一个?语法很漂亮:)) @Luaan 重写为 T-SQL(与 PL/SQL 相比相当复杂 :-) 是的,游标在 T-SQL 中是非常口齿不清的。当然,你不应该这么多地使用它们,但是...... :D PL/SQL 确实泄露了这些人是如何不知道如何在这么多地方对关系查询进行建模的;在某些情况下它很方便,而在其他情况下则完全令人难以置信:D @downvoter thanx 表示反对,但逐步删除的想法有什么问题???【参考方案4】:-
检查session_event中是否有索引?如果有,则禁用它
使用 NOT EXISTS 而不是 NOT IN,因为 EXISTS 的性能比其他更好(@Zohar Peled 写它的查询)
如果未解决,则单独运行您的 select 查询并查看执行计划以了解您在执行 Select 时将要执行的操作。
【讨论】:
【参考方案5】:试试这个代码
delete e
from session_events e
left join sessions s (nolock)
on e.session_id = s.session_id
where s.session_id is null
【讨论】:
投反对票的原因:您真的应该在自己写之前阅读帖子和现有答案。在这种情况下,OP 专门写了他知道左连接选项但对它不感兴趣。 好的,谢谢@ZoharPeled。作者的查询“Delete FROM session_events where session_id Not In (SELECT distinct session_id FROM sessions)”看起来不利于数据库引擎优化 你是对的,正如你所看到的,我的建议和你的没有太大不同。但是,Luaan 的答案比我们的两个答案都好 作者,session_id 字段上是否存在索引? @Kutsoff 不,OP 已经编辑了这个问题。session_id
列根本没有索引。以上是关于极长的“不在”请求的主要内容,如果未能解决你的问题,请参考以下文章