超长 "Not In" 请求

Extremely long "Not In" request

我有两个table:

  1. "Sessions" - 它具有 int 密钥标识,"session_id" - varchar, "device_category" - varchar 和其他一些列。 共有 149239 行。

  2. Session_events" - 它有 int 键 identity,"session_id" - uniqueidentifier 和一些其他字段。 那里有 3140768 行。

此 tables 不是从关系数据库 - Cassandra 导入的,所以我没有在 MS SQL 服务器设计器中创建任何连接。但是 session_id 列上的 Session 和 Session_events 之间的真正联系是多对多

现在我想删除所有未在个人计算机上发生的网络会话 "device_category"。所以我 运行 请求 Delete * FROM sessions where device_category != "PC" 那很快。现在我想从 Session_events table 中删除所有非 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

为什么不使用左连接?另一种选择是使用 EXISTS 而不是 IN:

DELETE FROM Session_events
WHERE NOT EXISTS(
    SELECT 1
    FROM Session 
    WHERE Session.Session_Id = Session_events.Session_Id
)
  1. 查看session_event中是否有索引?有的话就关掉
  2. 使用 NOT EXISTS 而不是 NOT IN,因为 EXISTS 比其他的有更好的性能(@Zohar Peled 写了它的查询)
  3. 如果没有解决,那么 运行 你的 select 单独查询并查看执行计划,看看当你执行 Select.

有时 delete 的问题是它正在等待获取所有相关行的锁。尝试循环删除。

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)

一次删除大量数据意味着数据库引擎必须在单个事务中完成所有这些操作。这意味着当你实际上不需要它时会产生巨大的开销(例如,你不需要回滚整个操作,或者你不关心一致性 - 你只想删除所有内容,如果它在中间失败,你将再次 运行 查询以删除其余部分)。

对于你的情况,你可以尝试批量删除。例如:

delete top 1000 from session_events where session_id Not In (SELECT distinct session_id FROM sessions)

重复直到 table 为空。

另外,你的出发点错了。您最好先在两者之间创建一个外键,然后使用 "on delete cascade"。这将自动删除不再具有有效 session 的所有 session_events。如果您可以重新开始,可能会快得多。不过没有承诺:D

试试这个代码

delete e
from session_events e 
left join sessions s (nolock)
    on e.session_id = s.session_id
where s.session_id is null