如何应对大数据文件格式和外部表中区分大小写的列名?
How to cope with case-sensitive column names in big data file formats and external tables?
背景
我正在使用 Azure 数据工厂 v2 将数据从本地数据库(例如 SQL 服务器)加载到 Azure 数据湖 gen2。由于我要加载数千个 table,因此我创建了一个动态 ADF 管道,它根据架构参数、table 名称、修改日期 (用于识别增量)等。这显然意味着我无法在 ADF 中手动指定任何类型的架构或映射。这很好,因为我希望数据湖以相同的结构保存源数据的持久副本。数据加载到 ORC 文件中。
基于这些 ORC 文件,我想在带有虚拟列的 Snowflake 中创建外部 table。我已经在 Snowflake 中创建了正常的 tables,其列名和数据类型与源 tables 中的相同,我将在稍后阶段使用它们。我想使用这些 tables 的信息架构来为外部 tables 动态创建 DDL 语句。
问题
由于列名在 Snowflake 中始终为大写,并且在许多方面区分大小写,Snowflake 无法使用动态生成的 DDL 语句解析 ORC 文件,因为虚拟列的定义不再对应于源列名称大小写。例如,它将生成一个虚拟列作为 -> ID NUMBER AS(value:ID::NUMBER)
这将 return NULL,因为该列在源数据库中以小写字母 D 命名 "Id",因此在数据湖的 ORC 文件中也是如此。
这感觉像是 Snowflake 的一个主要缺点。有什么合理的方法可以解决这个问题吗?我能想到的唯一选择是:
1. 将信息模式从源数据库单独加载到 Snowflake,并使用该数据构建具有正确大小写列名称的正确虚拟列定义。
2. 将记录全部加载到 Snowflake 中的某个变体列中,转换为 UPPER 或 LOWER。
这两个选项都会增加很多复杂性,甚至会弄乱数据。有什么直接的方法可以只 return 来自 ORC 文件的列名吗?最终,我需要能够在数据湖中的文件上使用 Snowflake 的 DESCRIBE TABLE 之类的东西。
除非您设置参数 QUOTED_IDENTIFIERS_IGNORE_CASE = TRUE
您可以在您想要的大小写中声明您的列:
CREATE TABLE "MyTable" ("Id" NUMBER);
如果你的动态 SQL 小心使用 "Id"
而不仅仅是 Id
你会没事的。
找到了实现此目标的更好方法,所以我正在回答我自己的问题。
通过以下查询,我们可以直接从阶段中的 ORC 文件中获取 path/column 名称,并提示来自源的数据类型。这会过滤掉仅包含 NULL 值的列。很可能会创建某种类型的数据类型排名 table,用于确定我们旨在为外部 table 动态定义的虚拟列的最终数据类型。
SELECT f.path as "ColumnName"
, TYPEOF(f.value) as "DataType"
, COUNT(1) as NbrOfRecords
FROM (
SELECT as "value" FROM @<db>.<schema>.<stg>/<directory>/ (FILE_FORMAT => '<fileformat>')
),
lateral flatten(value, recursive=>true) f
WHERE TYPEOF(f.value) != 'NULL_VALUE'
GROUP BY f.path, TYPEOF(f.value)
ORDER BY 1
背景
我正在使用 Azure 数据工厂 v2 将数据从本地数据库(例如 SQL 服务器)加载到 Azure 数据湖 gen2。由于我要加载数千个 table,因此我创建了一个动态 ADF 管道,它根据架构参数、table 名称、修改日期 (用于识别增量)等。这显然意味着我无法在 ADF 中手动指定任何类型的架构或映射。这很好,因为我希望数据湖以相同的结构保存源数据的持久副本。数据加载到 ORC 文件中。
基于这些 ORC 文件,我想在带有虚拟列的 Snowflake 中创建外部 table。我已经在 Snowflake 中创建了正常的 tables,其列名和数据类型与源 tables 中的相同,我将在稍后阶段使用它们。我想使用这些 tables 的信息架构来为外部 tables 动态创建 DDL 语句。
问题
由于列名在 Snowflake 中始终为大写,并且在许多方面区分大小写,Snowflake 无法使用动态生成的 DDL 语句解析 ORC 文件,因为虚拟列的定义不再对应于源列名称大小写。例如,它将生成一个虚拟列作为 -> ID NUMBER AS(value:ID::NUMBER)
这将 return NULL,因为该列在源数据库中以小写字母 D 命名 "Id",因此在数据湖的 ORC 文件中也是如此。
这感觉像是 Snowflake 的一个主要缺点。有什么合理的方法可以解决这个问题吗?我能想到的唯一选择是: 1. 将信息模式从源数据库单独加载到 Snowflake,并使用该数据构建具有正确大小写列名称的正确虚拟列定义。 2. 将记录全部加载到 Snowflake 中的某个变体列中,转换为 UPPER 或 LOWER。
这两个选项都会增加很多复杂性,甚至会弄乱数据。有什么直接的方法可以只 return 来自 ORC 文件的列名吗?最终,我需要能够在数据湖中的文件上使用 Snowflake 的 DESCRIBE TABLE 之类的东西。
除非您设置参数 QUOTED_IDENTIFIERS_IGNORE_CASE = TRUE
您可以在您想要的大小写中声明您的列:
CREATE TABLE "MyTable" ("Id" NUMBER);
如果你的动态 SQL 小心使用 "Id"
而不仅仅是 Id
你会没事的。
找到了实现此目标的更好方法,所以我正在回答我自己的问题。
通过以下查询,我们可以直接从阶段中的 ORC 文件中获取 path/column 名称,并提示来自源的数据类型。这会过滤掉仅包含 NULL 值的列。很可能会创建某种类型的数据类型排名 table,用于确定我们旨在为外部 table 动态定义的虚拟列的最终数据类型。
SELECT f.path as "ColumnName"
, TYPEOF(f.value) as "DataType"
, COUNT(1) as NbrOfRecords
FROM (
SELECT as "value" FROM @<db>.<schema>.<stg>/<directory>/ (FILE_FORMAT => '<fileformat>')
),
lateral flatten(value, recursive=>true) f
WHERE TYPEOF(f.value) != 'NULL_VALUE'
GROUP BY f.path, TYPEOF(f.value)
ORDER BY 1