【问题标题】:When Migrating from SQL Server to Snowflake how do we migrate stored procedures从 SQL Server 迁移到 Snowflake 时,我们如何迁移存储过程
【发布时间】:2022-04-25 19:28:40
【问题描述】:

我正在 Snowflake 上做 POC 并探索这个产品。

我有一个问题:在 SQL Server 中,我们有 400-500 个存储过程,这些存储过程是从 SSRS 报告中调用的。这些存储过程本质上很简单,如下所示:

CREATE PROCEDURE [dbo].[SQL_Stored_Procedure]
    (@StartDate DATETIME, 
     @EndDate   DATETIME)
AS
BEGIN
    SELECT *
    FROM MYTable
    WHERE Cloumn_Date BETWEEN @StartDate AND @EndDate;
END

EXEC [dbo].[SQL_Stored_Procedure] @StartDate = Getdate()-1, @EndDate=Getdate()

如何在 Snowflake 中实现同样的效果?似乎 Snowflake 过程就像一个 SQL Server 函数:https://docs.snowflake.net/manuals/sql-reference/stored-procedures-usage.html

想知道其他人在上述情况下的表现如何......?

【问题讨论】:

标签: sql-server stored-procedures snowflake-cloud-data-platform


【解决方案1】:

对于某些用例,UDTF 可能是更好的选择,但您可以使用 javascript 存储过程来运行 SQL 语句。它不是超级漂亮,我希望他们很快就会在存储过程中添加 SQL 作为语言选项。这是上面通用查询的示例。

CREATE OR REPLACE PROCEDURE STORED_PROCEDURE_NAME(STARTDATE VARCHAR, ENDDATE VARCHAR)
RETURNS VARIANT
LANGUAGE JAVASCRIPT
EXECUTE AS OWNER
AS 
$$
    query = `SELECT column1, column2
            FROM MYTable
            WHERE Cloumn_Date BETWEEN '` + STARTDATE + `' AND '` + ENDDATE + `';`

    resultset = snowflake.createStatement({sqlText: query}).execute() 

    //extract the results of the query into a javascript object
    output = []
    while (resultset .next())  {
        output.push(output.getColumnValue(1), output.getColumnValue(2));
    }

    //put the output of the query into a JSON object which Snowflake can translate
    // into a "variant" and can be used downstream
    json = { "result" : output };

    return json;
$$;

//I've found it easiest to work with strings because the data type conversions
//between snowflake and javascript and back again can be tricky
CALL STORED_PROCEDURE_NAME(CURRENT_DATE::STRING, DATEADD( DAY, 1, CURRENT_DATE)::STRING);
SELECT * FROM TABLE(RESULT_SCAN(LAST_QUERY_ID()));

我发现这对于执行转换和加载的过程非常有用,但到目前为止,我只手动迁移了一小部分查询,并且需要对其中一些进行一些 javascript 调整和自定义。

设置自动迁移数百个查询会有点棘手,但上面的 javascript 可以改进和更通用(我不是 javascript 专家)。例如,动态处理任何列列表,无需手动编辑:https://docs.snowflake.net/manuals/sql-reference/stored-procedures-usage.html#returning-a-result-set

使用下游结果的额外 RESULT_SCAN 步骤的文档在这里:https://docs.snowflake.net/manuals/sql-reference/stored-procedures-usage.html#using-result-scan-to-retrieve-the-result-from-a-stored-procedure

【讨论】:

  • 我真的很喜欢 Snowflake,但是这种存储过程的实现真的很糟糕——我也希望他们尽快引入纯 SQL 存储过程。 AWS Redshift 也有类似的问题,但最终在去年引入了 SQL 程序。
  • 我完全同意,他们最近宣布 SQL 程序正在开发中。
【解决方案2】:

我们目前正在将我们的数据仓库从 MS SQL 迁移到 Snowflake。 Snowflake 存储过程可以用 SQL 或 JavaScript 编写。我找不到为您迁移它们的工具,因此我们不得不重构我们的工具。我们没有你那么多。

我创建的存储过程比较简单,所以我创建了一个字符串并执行它们:

create or replace procedure STAGE_ABC.SP_LOAD_HISTORY(src varchar, target varchar, run_for_DT varchar, key_cols varchar)
  returns varchar not null
  language javascript
  as 
  $$
//get columns list
var col_sql = "select COLUMN_NAME from information_schema.columns where TABLE_SCHEMA = '" + src_schema + "' and TABLE_NAME = '" + src_table + "' order by ORDINAL_POSITION;"

try {
    var result_set = snowflake.execute( {sqlText: col_sql});    

    while (result_set.next())  {
       cols += result_set.getColumnValue(1) + ", ";
    }}
catch (err)  {
    result =  "Failed to get columns: Code: " + err.code + "\n  State: " + err.state;
    result += "\n  Message: " + err.message;
    result += "\nStack Trace:\n" + err.stackTraceTxt; 
    }

cols = cols.substring(0, cols.length - 2);

var result = "";
$$;

【讨论】:

  • "Snowflake 存储过程可以用 SQL 或 JavaScript 编写。" - 这是误导,雪花存储过程不能单独用 SQL 编写。您目前要求使用 JavaScript,然后可以执行 SQL 语句作为该代码的一部分,如上所示。
【解决方案3】:

我们正在进行类似的迁移。 Snowflake proc 不容易使用,原因如下:

  • 构建 SQL 很容易出错,例如立即执行
  • 将结果转换为 JSON 很难看
  • Procs 不会像其他程序一样返回结果集 数据库并需要第二次调用(也许您可以在 应用程序端,但我没有尝试过)
  • 如果您使用直接 SQL,则 proc 中的许多处理看起来与您在应用程序中执行的处理相同。将这个逻辑留在应用程序中会使应用程序更具可移植性。如果您需要从 Snowflake procs 迁移到另一个 DB,您将面临很多工作,因为很少或没有其他 DB 使用像 snowflake 这样的 JavaScript 语法。

我们正在查看上述存储过程并将 SQL 存储在 Java 应用程序的文本文件中。我将尝试使用 YAML 来分​​离 SQL 语句并回滚事务以删除临时表和变量。

【讨论】:

    【解决方案4】:

    通常这样的过程可以翻译成Tabular SQL UDF:

    CREATE OR REPLACE FUNCTION SQL_UDF(STARTDATE DATETIME, ENDDATE DATETIME)
    RETURNS TABLE(id INT, Column_Date DATETIME)
    AS
    'SELECT * FROM MyTable WHERE Column_Date BETWEEN STARTDATE AND ENDDATE';
       
    SELECT *
    FROM TABLE(SQL_UDF((CURRENT_DATE()-1)::DATETIME,
                        CURRENT_DATE()::DATETIME));
    

    为:

    CREATE OR REPLACE TABLE MyTable(id INT, Column_Date DATETIME);
    
    INSERT INTO MyTable SELECT 1, CURRENT_DATE();
    INSERT INTO MyTable SELECT 2, CURRENT_DATE()-10;
    

    输出:


    如果需要存储过程,也是可以的:

    CREATE OR REPLACE PROCEDURE SQL_Stored_Procedure(STARTDATE DATETIME,ENDDATE DATETIME)
    RETURNS TABLE(id INT, Column_Date DATETIME)
    LANGUAGE SQL
    AS
    DECLARE
      res RESULTSET;
      query VARCHAR DEFAULT 'SELECT * FROM MyTable WHERE Column_Date BETWEEN ? AND ?';
    BEGIN
      res := (EXECUTE IMMEDIATE :query USING (STARTDATE, ENDDATE));
      RETURN TABLE (res);
    END;
    

    呼叫:

    CALL SQL_Stored_Procedure(CURRENT_DATE()-1, CURRENT_DATE());
    

    输出:

    更多信息:Working with RESULTSETs

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-18
      • 2021-04-23
      • 2016-08-23
      • 2014-05-31
      • 1970-01-01
      相关资源
      最近更新 更多