【问题标题】:Stored procedure - get anticipated columns before fully executing statement?存储过程 - 在完全执行语句之前获取预期的列?
【发布时间】:2021-08-10 20:44:15
【问题描述】:

我正在处理一个存储过程,想知道是否有办法在完全执行之前从 sql 语句中检索预期的结果列列表。

场景:

  • 动态 SQL
  • 一个 UDF,它可能会改变我们无法控制的列

前:

//inbound parameter
SET QUERY_DEFINITION_ID = 12345;

//Initial statement pulls query text from bank of queries
var sqlText = getQueryFromQueryBank(QUERY_DEFINITION_ID);

//now we run our query
var cmd = {sqlText: sqlText };
stmt = snowflake.createStatement(cmd);

我想做的是说“对 - 在你运行这个之前,给我预期的列列表”,这样我就可以将它与预期的内容进行比较。

前:

  • 预期:[col1, col2, col3, col4]
  • 得到:[col1]
  • 结果:糟糕。不要跑。

这里的理由是,如果缺少某些东西,我想使执行短路 - 在它可能运行一段时间之前。我可以在事后验证所有这些,但尽早停止会很有帮助。

非常感谢任何想法!

【问题讨论】:

  • 也许我不完全理解用例,所以如果不是请澄清。如果您在 select 中指定了一个或多个列,但其中一个或多个不存在,则查询将快速返回一个错误,您可以在 TRY/CATCH 块中捕获该错误。如果您使用 SELECT * 并且需要测试您期望的列,最简单的方法是将列名显式添加到选择中并使用 TRY/CATCH。这样查询不会在不返回您期望的列的情况下运行很长时间。
  • 您是否在动态构建查询?如果你是,请在你的问题中显示这一点。如果你不是,那么 SQL 查询总是返回固定数量的列,并且你必须知道你的 select 语句中包含了多少列,所以你的问题对我来说没有多大意义。请您澄清一下您实际在做什么以及您想要实现什么?
  • 为清晰起见,根据请求进行了编辑。我们正在尝试复制类似于“SET FMTONLY ON”之类的内容,以便在允许可能昂贵的查询不符合所需的结果集结构时运行之前,我们可以检查预期结构的外观。我们可以运行它并检查结果集 - 但是我们遇到了执行时间和潜在成本的问题。
  • 仍然没有多大意义,至少对我来说。您的查询库中存储了大量查询,每个查询在其 select 语句中都有一组固定的列 - 然后您想检查该列列表是否正确?你根据什么标准检查它是否正确,如果有标准你可以检查你的查询,那么你为什么不在你写查询(或将你的查询保存到查询库)时这样做,而不是什么时候您尝试执行查询吗?
  • 好的,有一种方法可以在不运行查询的情况下获取结果的列列表,但它不会告诉您(至少在不解开快死结的情况下不会告诉您)列的来源。例如,如果该列是一个别名并且是计算出来的,那么就不容易分辨出来。如果您只想要列数及其名称的列表,我可以编写 SP 代码来做到这一点。让我知道这是否有帮助,我可以写出来。

标签: snowflake-cloud-data-platform


【解决方案1】:

此示例 SP 代码显示了如何在运行查询之前获取查询将投射到结果中的列列表。它应该只用于大型、长时间运行的查询,因为获取列列表需要几秒钟。

有几个注意事项。 1)它只会返回列的名称。它不会告诉您它们是如何构建的,即它们是否是别名、直接来自表、计算等。 2) 我使用的示例查询直接来自雪花文档https://docs.snowflake.com/en/user-guide/sample-data-tpcds.html#functional-query-definition。为方便起见,我将查询最小化为一行。除了列名之外,列的输出还包括对象限定符,例如 V1.I_CATEGORY、V1.D_YEAR、V1.D_MOY 等。如果您不希望它们更容易比较名称,您可以去掉限定符在点上使用 JavaScript 拆分函数并获取结果数组的索引 1。

create or replace procedure EXPLAIN_BEFORE_RUNNING()
returns string
language javascript
execute as caller
as
$$
    // Set the context for the session to the TPC-H sample data:
    executeNonQuery("use schema snowflake_sample_data.tpcds_sf10tcl;");
    
    // Here's a complex query from the Snowflake docs (minimized to one line for convienience):
    var sql = `with v1 as( select i_category, i_brand, cc_name, d_year, d_moy, sum(cs_sales_price) sum_sales, avg(sum(cs_sales_price)) over(partition by i_category, i_brand, cc_name, d_year) avg_monthly_sales, rank() over (partition by i_category, i_brand, cc_name order by d_year, d_moy) rn from item, catalog_sales, date_dim, call_center where cs_item_sk = i_item_sk and cs_sold_date_sk = d_date_sk and cc_call_center_sk= cs_call_center_sk and ( d_year = 1999 or ( d_year = 1999-1 and d_moy =12) or ( d_year = 1999+1 and d_moy =1)) group by i_category, i_brand, cc_name , d_year, d_moy), v2 as( select v1.i_category ,v1.d_year, v1.d_moy ,v1.avg_monthly_sales ,v1.sum_sales, v1_lag.sum_sales psum, v1_lead.sum_sales nsum from v1, v1 v1_lag, v1 v1_lead where v1.i_category = v1_lag.i_category and v1.i_category = v1_lead.i_category and v1.i_brand = v1_lag.i_brand and v1.i_brand = v1_lead.i_brand and v1.cc_name = v1_lag.cc_name and v1.cc_name = v1_lead.cc_name and v1.rn = v1_lag.rn + 1 and v1.rn = v1_lead.rn - 1) select * from v2 where d_year = 1999 and avg_monthly_sales > 0 and case when avg_monthly_sales > 0 then abs(sum_sales - avg_monthly_sales) / avg_monthly_sales else null end > 0.1 order by sum_sales - avg_monthly_sales, 3 limit 100;`;

    // Before actually running the query, generate an explain plan.
    executeNonQuery("explain " + sql);
    
    // Now read the column list from the explain plan from the result set.
    var columnList = executeSingleValueQuery("COLUMN_LIST", `select "expressions" as COLUMN_LIST from table(result_scan(last_query_id())) where "operation" = 'Result';`);
    
    // For now, just exit with the column list as the output...
    return columnList;
    
    // Your code here...


    // Helper functions:

function executeNonQuery(queryString) {
    var out = '';
    cmd = {sqlText: queryString};
    stmt = snowflake.createStatement(cmd);
    var rs;
    rs = stmt.execute();
}

function executeSingleValueQuery(columnName, queryString) {
    var out;
    cmd1 = {sqlText: queryString};
    stmt = snowflake.createStatement(cmd1);
    var rs;
    try{
        rs = stmt.execute();
        rs.next();
        return rs.getColumnValue(columnName);
    }
    catch(err) {
        if (err.message.substring(0, 18) == "ResultSet is empty"){
            throw "ERROR: No rows returned in query.";
        } else {
            throw "ERROR: " + err.message.replace(/\n/g, " ");
        } 
    }
    return out;
}

$$;

call Explain_Before_Running();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-01
    • 1970-01-01
    • 2019-06-23
    • 2020-02-28
    • 1970-01-01
    • 2011-10-10
    相关资源
    最近更新 更多