【问题标题】:How to read the headers from a staged file如何从暂存文件中读取标题
【发布时间】:2021-06-20 17:10:18
【问题描述】:

我的用例如下。我需要将数据从源加载到雪花阶段。从阶段开始,我需要将数据加载到雪花表中。现在,在加载到雪花表之前,我需要读取阶段加载的文件的列(基本上是标题)。我的理解是,如果我必须在一个阶段(使用 select)读取文件中的单行,那将不会有效,因为该文件实际上是使用云提供商的底层对象存储。如果我可以使用 'COPY INTO' 将文件加载到雪花表中,然后找到一种读取列名的方法,那将更有效率。我想检查一下我的理解是否正确。

【问题讨论】:

  • 问题的标题应该是“如何从暂存文件中读取标题”吗?
  • 你说得对,Felipe,我只是改了标题。你认为我们应该从舞台上阅读它还是我们应该复制到一个表格中然后尝试阅读它。由于stage是object store,如果stage里面的文件很多,会不会效率低下?
  • 您打算单独加载舞台中的每个文件还是同时加载许多/所有文件?所有文件都有相同的标题吗?如果您可以解释为什么需要阅读标题以及您将如何处理这些信息,那么有人可能会提出更好的解决方案
  • 嗨尼克,文件中的列经常更改。所以,我试图找到一种方法来读取标题并查看是否添加了列,然后更改表以添加列然后加载它。我有两个选项使用 JSON 阶段和带有变体列的 RAW 表。将舞台复制到 RAW。如果有列添加,则将该列添加到核心表中,然后从原始加载核心。选项 2,我在阶段读取标题并在 RAW 中添加列。我觉得选项 1 是更好的选择,但想获得第二意见

标签: snowflake-cloud-data-platform snowflake-schema


【解决方案1】:

This Snowflake 的 Craig Warman 撰写的文章描述了如何从 JSON 文档/文件自动创建视图。这可以调整为允许您读取 json 一次,然后根据该文件中的内容自动构建一个表和一个视图。您将需要一个自定义 JavaScript UDF

创建 UDF 后,您可以简单地调用:

call create_view_over_json(
  '@EXT_STAGE/sample.json', 
  'DB_NAME.MY_SCHEMA.sample', 
  'DB_NAME.MY_SCHEMA.sample_vw');

这只会产生一个对外部阶段的调用,然后再产生两个调用:一个是查询包含原始 json 的表,另一个是为其创建视图:

之后你就有了包含数据的表,以及一个可以直接查询的视图:

SELECT * FROM DB_NAME.MY_SCHEMA.sample_vw;

还有你需要的 UDF:

create or replace procedure create_view_over_json (STAGE_FILE_NAME varchar, TABLE_NAME varchar, VIEW_NAME varchar)
returns varchar
language javascript
as
$$
// Attribution:
// Craig Warman for the original code who also leveraged code developed by Alan Eldridge.

// This generates paths with levels enclosed by double quotes (ex: "path"."to"."element").  It also strips any bracket-enclosed array element references (like "[0]")
var path_name = "regexp_replace(regexp_replace(f.path,'\\\\[(.+)\\\\]',''), '(\\w+)','\"\\1\"')"
var attribute_type = "DECODE (substr(typeof(f.value),1,1),'A','ARRAY','B','BOOLEAN','I','FLOAT','D','FLOAT','STRING')";    // This generates column datatypes of ARRAY, BOOLEAN, FLOAT, and STRING only
var alias_name = "REGEXP_REPLACE(REGEXP_REPLACE(f.path, '\\[(.+)\\]'),'[^a-zA-Z0-9]','_')" ;                           // This generates column aliases based on the path
var col_list = "";

var json_col_name = "json_data";
// Create or replace the table based on the file
var table_ddl = "CREATE OR REPLACE TABLE " + TABLE_NAME + " AS \n" +
                "SELECT \n" +
                "   parse_json($1) AS " + json_col_name + " \n" +
                "FROM " + STAGE_FILE_NAME + "; \n"
var table_stmt = snowflake.createStatement({sqlText:table_ddl});
var table_res = table_stmt.execute();

// Build a query that returns a list of elements which will be used to build the column list for the CREATE VIEW statement
var element_query = "SELECT DISTINCT \n" +
                    path_name + " AS path_name, \n" +
                    attribute_type + " AS attribute_type, \n" +
                    alias_name + " AS alias_name \n" +
                    "FROM \n" + 
                    TABLE_NAME + ", \n" +
                    "LATERAL FLATTEN(" + json_col_name + ", RECURSIVE=>true) f \n" +
                    "WHERE TYPEOF(f.value) != 'OBJECT' \n" +
                    "AND NOT contains(f.path,'[') ";      // This prevents traversal down into arrays;

// Run the query...
var element_stmt = snowflake.createStatement({sqlText:element_query});
var element_res = element_stmt.execute();

// ...And loop through the list that was returned
while (element_res.next()) {

// Add elements and datatypes to the column list
// They will look something like this when added: 
//    col_name:"name"."first"::STRING as name_first, 
//    col_name:"name"."last"::STRING as name_last   

   if (col_list != "") {
      col_list += ", \n";}
   col_list += "\t" + json_col_name + ":" + element_res.getColumnValue(1);   // Start with the element path name
   col_list += "::" + element_res.getColumnValue(2);             // Add the datatype
   col_list += " as " + element_res.getColumnValue(3);           // And finally the element alias 
}

// Now build the CREATE VIEW statement
var view_ddl = "CREATE OR REPLACE VIEW " + VIEW_NAME + " AS \n" +
               "SELECT \n" + col_list + "\n" +
               "FROM " + TABLE_NAME;

var view_stmt = snowflake.createStatement({sqlText:view_ddl});
var view_res = view_stmt.execute();
view_res.next();

return ((view_ddl), (element_query));
$$;

几点:

  • 如果 json 很复杂,您需要调整存储过程来处理它。再次,克雷格在part 2 中进行救援
  • 如果 json 不时发生显着变化,请考虑确保您的架构不会中断。 Schema Evolution here 的一些简单阅读。
  • 这个例子是一个json文件,AVROParquetCSV,甚至XML都可以采用同样的解决方案,通过改变CREATE TABLE语句的生成方式和查询的构造方式后面部分查询各自的格式

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-14
    • 1970-01-01
    • 1970-01-01
    • 2012-06-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多