INSERT INTO减慢表值FUNCTION

时间:2016-01-01 12:54:24

标签: sql-server performance

在SQL Server 2008端,我有表值函数,它接收合并到单VARBINARY(MAX)的45k整数id,将它们拆分并作为表返回。 SplitIds最多需要5秒。正如我在估计执行计划中看到的那样 - 100%是'表插入'。有可能以某种方式加速这个功能吗?

ALTER FUNCTION [dbo].[SplitIds](@data VARBINARY(MAX))
RETURNS @result TABLE(Id INT NOT NULL)
AS
BEGIN
    IF @data IS NULL
        RETURN
    DECLARE @ptr INT = 0, @size INT = 4
    WHILE @ptr * @size < LEN(@data)
    BEGIN
        INSERT INTO @result(Id)
        VALUES(SUBSTRING(@data, @ptr * @size + 1, @size))
        SET @ptr += 1
    END
    RETURN
END

目前在C#端,它以下一种方式用于Linq-to-SQL查询:

XDbOrder[] orders =
    database.SplitIds(ConvertToVarbinary(orderIds))
    Join(
        database.Get<XDbOrder>,
        r = r.Id,
        o => o.Id,
        (r, o) => o).
    ToArray();

更一般的问题:在Linq-to-SQL中,以某种方式实现下一个没有SplitIds的东西是否可能? .Contains不起作用 - 它创建的查询包含超过2100个SQL参数和崩溃。

int[] orderIds = { ... 45k random entries .....};

XDbOrder[] orders =
    database.Get<XDbOrder>().
    Where(o => orderIds.Contains(o.Id)).
    ToArray();

2 个答案:

答案 0 :(得分:4)

您可以尝试更基于集合的方法。

(我保留了多语句TVF方法,因为生成数字表的内联方法很好地隔离,但是当合并到更大的查询中时执行计划可能非常糟糕 - 这确保了分裂发生一次,只发生过一次)

我还在返回表中添加了一个主键,因此它包含一个有用的索引。

CREATE FUNCTION [dbo].[SplitIds](@data VARBINARY(MAX))
RETURNS @result TABLE(Id INT NOT NULL PRIMARY KEY WITH (IGNORE_DUP_KEY=ON))
AS
  BEGIN
      IF @data IS NULL
        RETURN

      DECLARE @size INT = 4;

      WITH E1(N)
           AS (SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
               SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
               SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1), -- 1*10^1 or 10 rows
           E2(N)
           AS (SELECT 1 FROM   E1 a, E1 b), -- 1*10^2 or 100 rows
           E4(N)
           AS (SELECT 1 FROM   E2 a, E2 b), -- 1*10^4 or 10,000 rows
           E8(N)
           AS (SELECT 1 FROM   E4 a, E4 b), -- 1*10^8 or 100,000,000 rows
           Nums(N)
           AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1
               FROM   E8)
      INSERT INTO @result
                  (Id)
      SELECT TOP (DATALENGTH(@data)/@size) SUBSTRING(@data, N * @size + 1, @size)
      FROM   Nums

      RETURN
  END 

以下为我完成约160ms

DECLARE @data VARBINARY(MAX) = 0x

WHILE DATALENGTH(@data) < 184000
  SET @data = @data + CRYPT_GEN_RANDOM(8000)

SELECT COUNT(*)
FROM   [dbo].[SplitIds](@data) 

答案 1 :(得分:0)

这是我基于集合的方法的版本

load

关于我的方法的几点说明

  • 在函数中添加load:function(me, node) { if(node && node.attributes.checked) node.cascade( ... [function to check all children] ) } 将避免create FUNCTION [dbo].[SplitIds1](@data VARBINARY(MAX)) returns table with SCHEMABINDING as return WITH e1(n) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 ), -- 10 e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b), -- 10*10 e3(n) AS (SELECT 1 FROM e1 CROSS JOIN e2), -- 10*100 e4(n) AS (SELECT 1 FROM e3 A CROSS JOIN e3 B), -- 1000*1000 Numbers(ptr,Size) AS (SELECT ROW_NUMBER() OVER (ORDER BY n)-1,4 FROM e4) SELECT SUBSTRING(@data, ptr * Size + 1, Size) as Id FROM Numbers WHERE ptr * Size < LEN(@data)
  • 中不必要的SCHEMABINDING运算符
  • 还删除了Table spool变量,因为它在函数内部是硬编码的
  • execution plan表值函数更改为@size表值函数,它允许您在函数内部查看Multi-Statement select语句,就像任何Inline或{{1}一样查询