带有“not in”的 SQL 查询非常慢

Posted

技术标签:

【中文标题】带有“not in”的 SQL 查询非常慢【英文标题】:SQL query with "not in" is very slow 【发布时间】:2019-09-09 05:25:00 【问题描述】:

我有以下非常慢的 SQL 查询。如何以不同的方式编写脚本?

select 
    pws_name 
from 
    pws_asset ass
join 
    Account acc on acc.AccountId = ass.pws_AccountId
where 
    acc.AccountNumber  in ('188012', '172146', '214727', '13636', '201194', '280294', '34328')
    and ass.pws_name not in ('1018684', '1018784', '1019584', '1019784', '1019884', '1070838', '1277139', '1277339'.........)

【问题讨论】:

尝试提高查询性能时要做的第一件事是查看执行计划。请edit您的问题与执行计划的链接(您可以使用 Brent Ozar 的paste the plan。 添加EXPLAIN输出 NOT 子句中实际有多少个值? 有一些不同的问题。首先你应该把索引放到AccountNumberpws_name。其次,您应该查看 AccountNumberpws_name 列类型。如果列类型与in 语句中的给定值类型不同,则应使用相同类型 "超过 5000 条记录" ...您实际上是在执行 5000 条 OR 语句.. 尝试将数据放入临时表中(并尝试对其进行索引)并加入它。跨度> 【参考方案1】:

请按照以下步骤操作,这将有助于提高查询性能。

第 1 步:声明两个变量

DECLARE @AccNumList VARCHAR(4000)
DECLARE @PwsNameList VARCHAR(4000)

SET @AccNumList = '188012,172146,214727,13636,201194,280294,34328'
SET @PwsNameList = '1018684,1018784,1019584,1019784,1019884,1070838,1277139,1277339'

第 2 步:创建两个不同的临时表。 1 代表帐号

Create table #tblAcNum(AccountNumber VARCHAR(50))

pws_name 不需要 2

Create table #tblPwsNameNotNeeded(pws_name VARCHAR(50))

第三步:在上面的两个表中添加记录,分别用于IN和NOT IN。 请查看此Split csv string using XML in SQL Server 以供参考。

INSERT INTO #tblAcNum(AccountNumber)
SELECT
l.value('.','VARCHAR(50)') AcNum
FROM
(
SELECT CAST('<a>' + REPLACE(@AccNumList,',','</a><a>') + '</a>' AS XML) AcNumXML
) x
CROSS APPLY x.AcNumXML.nodes('a') Split(l)

INSERT INTO #tblPwsNameNotNeeded(pws_name)
SELECT
l.value('.','VARCHAR(50)') pws_name
FROM
(
SELECT CAST('<a>' + REPLACE(@PwsNameList,',','</a><a>') + '</a>' AS XML) PwsNameXML
) x
CROSS APPLY x.PwsNameXML.nodes('a') Split(l)

第 3 步:INNER JOIN #tblAcNum table with account table with accountnumber column

第 4 步:对不需要的 pws_name 使用 NOT EXISTS() 函数,如下所示

WHERE NOT EXISTS
(
SELECT 1
FROM #tblPwsNameNotNeeded pn
Where pn.pws_name = ass.pws_name
)

第 5 步:在选择查询后删除临时表。

DROP TABLE tblAcNum;
DROP TABLE #tblPwsNameNotNeeded;

请检查以下查询。

DECLARE @AccNumList VARCHAR(4000)
DECLARE @PwsNameList VARCHAR(4000)

SET @AccNumList = '188012,172146,214727,13636,201194,280294,34328'
SET @PwsNameList = '1018684,1018784,1019584,1019784,1019884,1070838,1277139,1277339'

Create table #tblAcNum(AccountNumber VARCHAR(50))

Create table #tblPwsNameNotNeeded(pws_name VARCHAR(50))

INSERT INTO #tblAcNum(AccountNumber)
SELECT
l.value('.','VARCHAR(50)') AcNum
FROM
(
SELECT CAST('<a>' + REPLACE(@AccNumList,',','</a><a>') + '</a>' AS XML) AcNumXML
) x
CROSS APPLY x.AcNumXML.nodes('a') Split(l)

INSERT INTO #tblPwsNameNotNeeded(pws_name)
SELECT
l.value('.','VARCHAR(50)') pws_name
FROM
(
SELECT CAST('<a>' + REPLACE(@PwsNameList,',','</a><a>') + '</a>' AS XML) PwsNameXML
) x
CROSS APPLY x.PwsNameXML.nodes('a') Split(l)

select 
ass.pws_name 
from pws_asset ass 
join Account acc on acc.AccountId = ass.pws_AccountId 
INNER JOIN #tblAcNum an ON an.AccountNumber = acc.AccountNumber
WHERE NOT EXISTS
(
SELECT 1
FROM #tblPwsNameNotNeeded pn
Where pn.pws_name = ass.pws_name
)

DROP TABLE tblAcNum;
DROP TABLE #tblPwsNameNotNeeded;

【讨论】:

【参考方案2】:

试试这个:

; with cte_excludepws as
(select AccountId from pws_asset where pws_name not in ('1018684', '1018784', '1019584', '1019784', '1019884', '1070838', '1277139', '1277339'.........))
select 
    pws_name 
from 
    pws_asset ass
join 
    Account acc on acc.AccountId = ass.pws_AccountId
where ass.AccountId not in (select AccountId from cte_excludepws)
and acc.AccountNumber  in ('188012', '172146', '214727', '13636', '201194', '280294', '34328')

如果可以的话,也可以选择将 AccountID 放入临时表而不是 cte 并在其上创建索引。

【讨论】:

【参考方案3】:

首先,请确保帐户数字 是真正的字符串。如果它们是数字,请去掉单引号!

那么,对于这个查询

select a.pws_name 
from pws_asset a join 
     Account ac 
     on ac.AccountId = a.pws_AccountId
where ac.AccountNumber  in ('188012', '172146', '214727', '13636', '201194', '280294', '34328') and
      a.pws_name not in ('1018684', '1018784', '1019584', '1019784', '1019884', '1070838', '1277139', '1277339'.........);

我会推荐以下索引:account(accountNumber, AccountId)pws_asset(pws_AccountId, pws_name)

【讨论】:

以上是关于带有“not in”的 SQL 查询非常慢的主要内容,如果未能解决你的问题,请参考以下文章

MySQL:在 WHERE 子句中带有 NOT IN 的从属子查询非常慢

为啥在 SQL 查询中 NOT IN 比 IN 慢得多

如何在 SQL 子查询中使用 NOT IN? [复制]

NOT IN 子查询速度慢并且内存不足(Clickhouse)

选择带有 Not IN 关键字的查询

带有 NOT IN 子查询的 SELECT INTO 查询需要很长时间/挂起