【问题标题】:STRING_SPLIT skyrockets execution time of sql querySTRING_SPLIT 使 sql 查询的执行时间猛增
【发布时间】:2020-10-07 08:56:10
【问题描述】:

对于 SQL 查询,我需要将输入字符串拆分为其整数部分,并根据提供的整数从表中选择值。

我的问题:如果有很多整数 (>300),查询会越来越慢,即对于大约 600 个整数,它使用超过一分钟!

这里是一个执行查询的小例子:

    DECLARE @inputStr VARCHAR(MAX) = '234,2344,12,523,5667,9825,345'

    SELECT
        surname,
        firstname
    FROM Addresses
    WHERE id IN (SELECT CAST(value AS INTEGER) FROM STRING_SPLIT(@inputStr, ',')))

这是否存在已知问题或我可以做的任何改进?

很高兴得到任何帮助!

【问题讨论】:

  • 我能给你的最好建议是不要乱用分隔字符串(如果可能的话),而是将应用程序 + sql 代码更改为使用表值参数。
  • 能否贴出Addresses的实际执行计划和表大小/索引?

标签: sql sql-server performance split


【解决方案1】:

问题在于,在优化查询之前,“IN”运算符中的任何显式值都由代数器在 WHERE 子句中通过多重 OR 进行转换... IN 运算符中的大量值总是会导致性能不足,无论以何种方式执行...!

通过创建临时表,您将拥有另一个可以提高性能的查询执行计划。

你试试这个方法:

SELECT DISTINCT CAST(value AS INTEGER) 
INTO   #T
FROM   STRING_SPLIT(@inputStr, ',');

SELECT surname,
       firstname
FROM   Addresses
WHERE  id IN (SELECT value 
              FROM   #T);

最终您可以在临时表中添加一个 UNIQUE 索引来提高性能:

SELECT DISTINCT CAST("value" AS INTEGER) 
INTO   #T
FROM   STRING_SPLIT(@inputStr, ',');

CREATE UNIQUE INDEX X123456789 ON #T ("value");

SELECT surname,
       firstname
FROM   Addresses
WHERE  id IN (SELECT value 
              FROM   #T);

【讨论】:

    【解决方案2】:

    就个人而言,我会将STRING_SPLIT 移动到JOIN,因为SQL Server 很可能每行运行一次STRING_SPLIT

    SELECT surname,
           firstname
    FROM dbo.Addresses A
         JOIN STRING_SPLIT(@InputStr,',') SS ON A.id = SS.[value];
    

    如果这仍然很慢,我建议您缺少id 上的索引。考虑到它是 id,那么您可能希望它是集群的,并且可能是主键:

    ALTER TABLE dbo.Addresses ADD CONSTRAINT PK_Addresses PRIMARY KEY (id) CLUSTERED;
    

    【讨论】:

    • 我相信优化器太聪明了,如果 where 子句中的每一行都运行 string_split 的话。我之前所做的测试显示了在多个场景中 ininner join 查询的相同执行计划,我认为这不会有所不同。
    • 我同意,它应该足够聪明,@ZoharPeled,我的经验也同意这一点;这让我觉得更有可能是缺乏索引。
    【解决方案3】:

    你能试试这个吗?

    DECLARE @inputStr VARCHAR(MAX) = '234,2344,12,523,5667,9825,345'
    
    DROP TABLE IF EXISTS #TEST;
    
    CREATE TABLE #TEST
    (
        [value] INT
    );
    
    INSERT INTO #TEST ([value])
    SELECT value
    FROM STRING_SPLIT(@inputStr, ',')
    
    SELECT
        surname,
        firstname
    FROM Addresses A
    INNER JOIN #TEST B
        ON A.id  = b.value
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-16
      • 1970-01-01
      • 1970-01-01
      • 2013-01-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多