infer_schema缺少列标题的文件的功能

infer_schema function on files with missing column header

提问人:naomitrina 提问时间:8/8/2023 更新时间:8/9/2023 访问量:169

问:

目前,我有 5 个暂存的文件,它们都缺少一个列标题:

2021 2022 2023
A组 54.9 55.3 55.7
B组 91.3 82.7 89.6

我无法更改这些文件的格式。我在 Snowflake 中使用了 infer_schema 函数,因为随着更多年份列的添加,文件将是动态的。

这是我想尝试的:

Create or replace table staging_table
     using template (
         select array_agg(object_construct(*))
           from table(
              infer_schema(
                 location => 'stage'
                   file_format => 'file_format'
                     )
                      )
                           );

copy into staging_table
from @stage
file_format = 'file_format'
match_by_column_name = case_insensitive;

我正在使用临时表,因为我还想取消透视数据。我最初在file_format中使用,但由于缺少标题而不起作用。我试过了,但这使得跟踪列和重命名它们变得更加困难(日期列比示例中的日期列多)。有没有人对如何更好地处理这个案子有任何想法?parse_header = trueparse_header = false

另外,我知道这可能是一个很长的延伸,但我也想在表中获取文件名。我知道我可以像这样获取文件名:

SELECT *
FROM TABLE (
    INFER_SCHEMA(
      LOCATION => MY_STAGE,
        FILE_FORMAT => 'MY_FILE_FORMAT'
        )
     );

但是,如何实际将数据与数据一起插入/复制到临时表中呢?

换句话说,我想要 Snowflake 中的数据,如下所示:

得分 File_Name
A组 2021 54.9 罚款
A组 2022 55.7 罚款
A组 2023 55.7 罚款
A组 2021 33.1 文件2
A组 2022 33.1 文件2
A组 2023 34.2 文件2

如何在 Snowflake 中从第一种文件格式转换为第二种表格格式?

任何帮助将不胜感激!!!我是否必须将其分解为 5 个不同的临时表,然后对实际表进行一个大插入?

sql snowflake-cloud-data-platform etl 雪花阶段

评论

0赞 Greg Pavlik 8/9/2023
这是一个有趣的问题。模式推理可能无法解决它。有了这个问题的答案,我(或打败我的人)可以给你一种方法来做到这一点。架构是否规则,因为它总是在最左边的列(顶行除外)中有组(或一些字符串),在顶行中具有年份编号(除了此行中最左边的列是空白的),并且年份和组的交集始终是一个数字?另外,我假设年数可能是可变的?
0赞 naomitrina 8/9/2023
@GregPavlik 是的,架构是常规的。最左边的列始终是一个字符串列,其中包含不同的组,但缺少标题。最上面的一行总是有年份,随着年度数据的增加,这个数字会随着时间的推移而增加。年份和组的交集始终是一个数字。该文件是自动生成的,因此我无法更改架构。

答:

0赞 Greg Pavlik 8/9/2023 #1

要使其正常工作,方法是:

  1. 逐行读取阶段中的每个文件(使用未指定列分隔符的文件格式)。
  2. 使用 UDTF(用户定义表函数),该函数知道每个文件的第一行是年份列表。我提供的 UDTF 将去除此行上空列的前导逗号(如果存在)。
  3. 通过 UDTF 运行每个文件的行。UDTF 将读取每个文件第一行的年份,并将分数转换为这些年份。

首先,创建一个全读取的文件格式,而不是单个字段:

create or replace file format READ_LINE type = csv 
field_delimiter = 'NONE' compression = 'AUTO';

然后,您可以通过执行 select 来确保这可以读取整行:

select metadata$file_row_number, metadata$filename, $1 as LINE
from @MY_FILES (file_format => READ_LINES);

接下来,创建 UDTF 来处理数据到达文件的方式:

create or replace function PIVOT_SCORES(LINE_NUMBER float, FILENAME string, LINE string)
returns table (GROUP_NAME string, YEAR string, SCORE string)
language javascript strict immutable
as 
$$
{
    initialize: function (argumentInfo, context) {
        this.years = [];
    },
    processRow: function (row, rowWriter, context) {
        var lineYear;
        var lineItems;
        if(row.LINE_NUMBER == 1) {
            if(row.LINE.trim().substr(0,1) == ",") {
                this.years = row.LINE.trim().substr(1).split(",");
            } else {
                this.years = row.LINE.trim().split(",");
            }
        } else {
            lineItems = row.LINE.trim().split(",");
            for (let i=1; i < lineItems.length; i++) {
                rowWriter.writeRow({GROUP_NAME:lineItems[0],YEAR:this.years[i-1],SCORE:lineItems[i]});
            }
        }
    },
    finalize: function (rowWriter, context) { /* */ },
}
$$
;

最后,选择文件中的行并通过 UDTF 运行它们,如下所示:

with FILE_LINES as
(
select   metadata$file_row_number   LINE_NUMBER
        ,metadata$filename          FILE_NAME
        ,$1                         LINE
from    @MY_FILES (file_format => READ_LINES) F
)
select   GROUP_NAME
        ,YEAR
        ,SCORE
        ,FILE_NAME
from     FILE_LINES F
        ,table(pivot_scores(LINE_NUMBER::float, FILE_NAME, F.LINE) over (partition by FILE_NAME order by LINE_NUMBER)) TF
;

我创建了两个文件来测试这一点。File1 是基于示例表的文件,File2 是新文件,多了一年(2020 年)用于测试。结果如下:

GROUP_NAME 得分 FILE_NAME
A组 2020 54.9 文件2.txt.gz
A组 2021 55.3 文件2.txt.gz
A组 2022 55.7 文件2.txt.gz
A组 2023 11.1 文件2.txt.gz
B组 2020 91.3 文件2.txt.gz
B组 2021 82.7 文件2.txt.gz
B组 2022 89.6 文件2.txt.gz
B组 2023 11.1 文件2.txt.gz
C组 2020 12.3 文件2.txt.gz
C组 2021 12.3 文件2.txt.gz
C组 2022 12.4 文件2.txt.gz
C组 2023 11.1 文件2.txt.gz
A组 2021 54.9 文件1.txt.gz
A组 2022 55.3 文件1.txt.gz
A组 2023 55.7 文件1.txt.gz
B组 2021 91.3 文件1.txt.gz
B组 2022 82.7 文件1.txt.gz
B组 2023 89.6 文件1.txt.gz