在 Azure Synapse Analytics 中使用 Synapse SQL 的存储过程

Synapse SQL 预配池和无服务器池使你可以将复杂的数据处理逻辑放入 SQL 存储过程。 存储过程很适合用于封装 SQL 代码并将其存储在数据仓库中的数据附近。 存储过程通过将代码封装到可管理单元中,并帮助开发人员将解决方案模块化,并促进代码的可重用性。 每个存储过程还可以接受参数,使它们更加灵活。 本文介绍在 Synapse SQL 池中实现存储过程以开发解决方案的一些提示。

可以预期什么

Synapse SQL 支持 SQL Server 中使用的许多 T-SQL 功能。 更重要的是,有一些横向扩展特定的功能可用于最大程度地提高解决方案的性能。 本文将介绍可以放入存储过程的功能。

备注

在过程主体中,只能使用 Synapse SQL 功能范围支持的功能。 查看 本文 以确定可在存储过程中使用的对象、语句。 这些文章中的示例使用了在无服务器和专用环境中均可用的通用功能。 请参阅本文末尾的 Synapse SQL 预配池和无服务器池中的其他限制

为了保持 SQL 池的规模和性能,还有一些特性和功能存在行为差异,其他特性和功能不受支持。

Synapse SQL 中的存储过程

在以下示例中,可以看到在数据库中存在外部对象时删除外部对象的过程:

CREATE PROCEDURE drop_external_table_if_exists @name SYSNAME
AS BEGIN
    IF (0 <> (SELECT COUNT(*) FROM sys.external_tables WHERE name = @name))
    BEGIN
        DECLARE @drop_stmt NVARCHAR(200) = N'DROP EXTERNAL TABLE ' + @name; 
        EXEC sp_executesql @tsql = @drop_stmt;
    END
END
GO
CREATE PROCEDURE drop_external_file_format_if_exists @name SYSNAME
AS BEGIN
    IF (0 <> (SELECT COUNT(*) FROM sys.external_file_formats WHERE name = @name))
    BEGIN
        DECLARE @drop_stmt NVARCHAR(200) = N'DROP EXTERNAL FILE FORMAT ' + @name; 
        EXEC sp_executesql @tsql = @drop_stmt;
    END
END
GO
CREATE PROCEDURE drop_external_data_source_if_exists @name SYSNAME
AS BEGIN
    IF (0 <> (SELECT COUNT(*) FROM sys.external_data_sources WHERE name = @name))
    BEGIN
        DECLARE @drop_stmt NVARCHAR(200) = N'DROP EXTERNAL DATA SOURCE ' + @name; 
        EXEC sp_executesql @tsql = @drop_stmt;
    END
END

可以使用语句执行 EXEC 这些过程,可在其中指定过程名称和参数:

EXEC drop_external_table_if_exists 'mytest';
EXEC drop_external_file_format_if_exists 'mytest';
EXEC drop_external_data_source_if_exists 'mytest';

Synapse SQL 提供了简化且精简的存储过程实现。 与 SQL Server 相比,最大的区别是存储过程不是预编译的代码。 在数据仓库中,与针对大型数据卷运行查询所需的时间相比,编译时间很小。 确保为大型查询正确优化存储过程代码更为重要。 目标是节省小时、分钟和秒,而不是毫秒。 因此,将存储过程视为 SQL 逻辑的容器更有用。

Synapse SQL 执行存储过程时,会在运行时分析、转换和优化 SQL 语句。 在此过程中,每个语句将转换为分布式查询。 针对数据执行的 SQL 代码与提交的查询不同。

封装验证规则

通过存储过程,可以在存储在 SQL 数据库中的单个模块中找到验证逻辑。 在以下示例中,可以看到如何验证参数的值并更改其默认值。

CREATE PROCEDURE count_objects_by_date_created 
                            @start_date DATETIME2,
                            @end_date DATETIME2
AS BEGIN 

    IF( @start_date >= GETUTCDATE() )
    BEGIN
        THROW 51000, 'Invalid argument @start_date. Value should be in past.', 1;  
    END

    IF( @end_date IS NULL )
    BEGIN
        SET @end_date = GETUTCDATE();
    END

    IF( @start_date >= @end_date )
    BEGIN
        THROW 51000, 'Invalid argument @end_date. Value should be greater than @start_date.', 2;  
    END

    SELECT
         year = YEAR(create_date),
         month = MONTH(create_date),
         objects_created = COUNT(*)
    FROM
        sys.objects
    WHERE
        create_date BETWEEN @start_date AND @end_date
    GROUP BY
        YEAR(create_date), MONTH(create_date);
END

调用过程时,sql 过程中的逻辑将验证输入参数。


EXEC count_objects_by_date_created '2020-08-01', '2020-09-01'

EXEC count_objects_by_date_created '2020-08-01', NULL

EXEC count_objects_by_date_created '2020-09-01', '2020-08-01'
-- Error
-- Invalid argument @end_date. Value should be greater than @start_date.

EXEC count_objects_by_date_created '2120-09-01', NULL
-- Error
-- Invalid argument @start_date. Value should be in past.

嵌套存储过程

当存储过程调用其他存储过程或执行动态 SQL 时,内部存储过程或代码调用被称为嵌套。 以下代码显示了嵌套过程的示例:

CREATE PROCEDURE clean_up @name SYSNAME
AS BEGIN
    EXEC drop_external_table_if_exists @name;
    EXEC drop_external_file_format_if_exists @name;
    EXEC drop_external_data_source_if_exists @name;
END

此过程接受表示某些名称的参数,然后调用其他过程以删除具有此名称的对象。 Synapse SQL 池最多支持 8 个嵌套级别。 此功能与 SQL Server 略有不同。 SQL Server 中的嵌套级别为 32。

最上层存储过程调用等同于嵌套级别 1。

EXEC clean_up 'mytest'

如果存储过程还调用另一个 EXEC,则嵌套级别将增加到 2。

CREATE PROCEDURE clean_up @name SYSNAME
AS
    EXEC drop_external_table_if_exists @name  -- This call is nest level 2
GO
EXEC clean_up 'mytest'  -- This call is nest level 1

如果第二个过程随后执行某种动态 SQL,则嵌套级别将增加到 3。

CREATE PROCEDURE drop_external_table_if_exists @name SYSNAME
AS BEGIN
    /* See full code in the previous example */
    EXEC sp_executesql @tsql = @drop_stmt;  -- This call is nest level 3
END
GO
CREATE PROCEDURE clean_up @name SYSNAME
AS
    EXEC drop_external_table_if_exists @name  -- This call is nest level 2
GO
EXEC clean_up 'mytest'  -- This call is nest level 1

备注

Synapse SQL 目前不支持 @@NESTLEVEL。 需要跟踪嵌套级别。 不太可能超过八个嵌套级别限制,但如果这样做,则需要重新编写代码以适应此限制内的嵌套级别。

INSERT..EXECUTE

配置的 Synapse SQL 池不允许您使用 INSERT 语句来访问存储过程的结果集。 可以使用替代方法。 有关示例,请参阅介绍 Synapse SQL 预配池的临时表的文章。

局限性

Synapse SQL 中未实现 Transact-SQL 存储过程的某些方面,比如:

功能/选项 已预配 无服务器
临时存储过程 是的
带编号的存储过程
扩展的存储过程
CLR 存储过程
加密选项 是的
复制选项
表值参数
只读参数
默认参数 是的
执行上下文
Return 语句 是的
INSERT INTO .. EXEC 是的

有关更多开发技巧,请参阅 开发概述