【问题标题】:Wrapper TSQL Table-valued Function is slowWrapper SQL 表值函数很慢
【发布时间】:2021-05-02 07:46:23
【问题描述】:

我构建了一个包装函数,该函数目前只调用另一个表值函数,但它只是增加了执行时间作为客户端处理时间的大量时间。有没有更快的方法来做到这一点?

没有包装:

使用包装器:

包装函数:

CREATE FUNCTION [console].[getCalculosRequisita]
(   

    @Disponivel BIGINT,
    @mediaDiaria float,
    @DiasStockArtigo INT, 
    @DiasAntes INT, 
    @SaidasPorMes float, 
    @QtdEncomendada2Meses BIGINT,
    @StockAtual BIGINT,
    @QtdRequisitada BIGINT,
    @caixaMinima INT

)
RETURNS @tbl TABLE 
(
    DiasAteRotura INT,
    AcaoRequisita varchar(10),
    Aconselhada BIGINT
)
AS
BEGIN

--future configuration check
--future log input

INSERT INTO @tbl SELECT DiasAteRotura, AcaoRequisita,Aconselhada
FROM [cartridge].[getCalculosRequisitaTSQL]
(
    @Disponivel ,
    @mediaDiaria ,
    @DiasStockArtigo , 
    @DiasAntes , 
    @SaidasPorMes , 
    @QtdEncomendada2Meses ,
    @StockAtual ,
    @QtdRequisitada ,
    @caixaMinima
)


--future log output

RETURN
END

GO

【问题讨论】:

    标签: sql-server tsql sql-execution-plan table-valued-parameters cross-apply


    【解决方案1】:

    我非常感谢您的回复。他们很有见地,我会赞成他们所有人。我只是在这里发布一些代码以供共享。如果您有建议,很高兴听到您的想法。

    我真正想要的是从视图中运行 Python,我必须完全转向另一个方向,而我认为性能只是白费了。

    似乎 Python 只能从存储过程运行...所以必须使用 OPENROWSET (!?) 才能将其作为 VIEW。不漂亮,下面的例子:

    DROP PROC IF EXISTS PythonExample;
    GO
    CREATE PROC PythonExample
    AS
    BEGIN
    
    set nocount on
    SET FMTONLY OFF
    
        DROP TABLE [dbo].[MyRows]
        CREATE TABLE [dbo].[MyRows](
            [RowNum] [int] NULL
        ) ON [PRIMARY]
        
    
        INSERT INTO [MyRows] 
            ([RowNum]) 
        VALUES 
            (1),
            (2)
    
        DECLARE @tbl1 TABLE 
        (
           COL1 INT,
           COL2 INT
        )
    
        INSERT INTO @tbl1
    
    ---ATTENTION Identing is important for Python code...
        EXEC sp_execute_external_script  @language =N'Python',
        @script=N'
    import pandas as pd
    df= MyInput
    df["newCol"]= df["RowNum"]*2
    MyOutput = df;
        ',
        @input_data_1_name = N'MyInput',
        @input_data_1 =N'SELECT [RowNum] FROM [MyRows]', --- it seems it cannot handle a temp table
        @output_data_1_name =N'MyOutput'
        --WITH RESULT SETS ((MyColName int, MyColName2 int));
    
        SELECT * FROM @tbl1
    END;
    GO
    
    DROP VIEW IF EXISTS ViewExample;
    GO
    CREATE VIEW ViewExample
    AS
        SELECT * FROM OPENROWSET('SQLNCLI', 'Server=localhost;Trusted_Connection=yes;', 'exec [dbo].[PythonExample]')
    GO
    
    SELECT * FROM ViewExample
    

    【讨论】:

      【解决方案2】:

      将其作为内联 TVF 执行,这要快得多:

      CREATE FUNCTION [console].[getCalculosRequisita]
      (   
      
          @Disponivel BIGINT,
          @mediaDiaria float,
          @DiasStockArtigo INT, 
          @DiasAntes INT, 
          @SaidasPorMes float, 
          @QtdEncomendada2Meses BIGINT,
          @StockAtual BIGINT,
          @QtdRequisitada BIGINT,
          @caixaMinima INT
      )
      RETURNS TABLE -- WITH SCHEMABINDING  -- preferable, but then you can't change the underlying function
      (
          DiasAteRotura INT,
          AcaoRequisita varchar(10),
          Aconselhada BIGINT
      )
      AS RETURN
      (SELECT DiasAteRotura, AcaoRequisita, Aconselhada
      FROM [cartridge].[getCalculosRequisitaTSQL]
      (
          @Disponivel ,
          @mediaDiaria ,
          @DiasStockArtigo , 
          @DiasAntes , 
          @SaidasPorMes , 
          @QtdEncomendada2Meses ,
          @StockAtual ,
          @QtdRequisitada ,
          @caixaMinima
      ) AS t
      );
      GO
      

      显然,如果您这样做,那么您将无法进行任何其他插入。无论如何记录都是不可能的,所以我不确定你打算做什么。

      您还没有给出底层函数的代码。也许这也可以作为 iTVF 来完成。

      【讨论】:

      • 您理解并且 100% 正确,但是包装器的目标正是为了降低未来的代码,例如日志记录或配置检查...
      • 无法进行记录,因为唯一允许的 DML 语句是“修改本地表变量的 INSERT、UPDATE 和 DELETE 语句”。配置检查可以通过救助内联进行,如果你举一个你想要的例子,我可以展示如何做到这一点。 @Larnu 我认为我是对的?
      • 函数可以对其中的表变量执行 DML 语句,是的,如果它们是多内联表值函数,则必须这样做。正如 Charlieface 所说,@ user6788933 众所周知,mlTVF 很慢。 iTVF 的速度明显更快,使用其中的 iTVF 对性能有很大好处。函数无法执行日志记录,因为它们无法针对表对象执行 DML 语句。如果您想要记录应该在被调用的存储过程中,或者可能在(写得很好)TRIGGER
      • 我必须承认,该功能似乎是多余的,@user6788933。为什么要有一个函数,调用一个函数,而在SELECT 中没有进一步的转换,甚至没有进一步的JOINWHERE。只需在您的外部查询中调用 [cartridge].[getCalculosRequisitaTSQL] 并使用您需要的列。 [console].[getCalculosRequisita] 函数实际上没有任何意义。这就像有一个VIEWSELECT,除了表中的几列之外,没有WHERE 或表达式。没有任何好处,而且很容易导致性能问题。
      • 我打赌[cartridge].[getCalculosRequisitaTSQL] 也是一个mlTVF,@user6788933。如果是这样,调用 mlTVF 的 mlTVF 是慢速函数的必经之路。
      猜你喜欢
      • 2015-11-09
      • 2018-01-23
      • 1970-01-01
      • 2012-09-10
      • 2022-06-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-17
      相关资源
      最近更新 更多