动态查询返回0行时如何向Stage复制带表头空Parquet文件
问题背景
当前使用COPY INTO命令将查询返回的结果集导出为Parquet文件,并按指定列做分区存储,需要满足以下要求:
- 当执行的查询返回0行数据时,仍需向目标Stage路径写入仅包含表头、无实际数据行的空Parquet文件
- 逻辑封装在存储过程内部,查询语句为动态传入参数、结构不固定,无法使用固定UNION拼接空行的方案
原有参考代码如下:
copy into @<stageName>/baseFolder/ from ( <a query that returns 0 rows>) partition by (col1 || '/' || CAST(col2 as INT)) FILE_FORMAT = <fileformat> HEADER=true DETAILED_OUTPUT=true
原生COPY INTO逻辑在查询返回0行时不会生成任何文件,无法满足空文件写入要求。
可行解决方案
采用存储过程内分支判断+临时表承接结果的方案实现,全程不需要感知动态查询的列结构,适配任意传入的查询语句,具体逻辑如下:
- 存储过程接收到动态传入的查询语句后,先创建会话级临时表承接查询的全量结果,临时表会自动适配查询返回的列名、列类型,无论查询返回多少行,表结构都和查询结果完全一致。
- 统计临时表内的数据行数,根据行数走不同的导出分支:
- 行数大于0时,走原有分区导出逻辑,对临时表的数据执行带分区规则的
COPY INTO,和原有业务逻辑完全一致 - 行数等于0时,去掉分区规则直接导出空临时表,此时会自动生成仅包含表结构(即原查询表头)的空Parquet文件,写入目标Stage的baseFolder路径下
- 行数大于0时,走原有分区导出逻辑,对临时表的数据执行带分区规则的
参考实现代码:
-- 1. 用临时表承接动态查询结果,自动适配列结构 CREATE OR REPLACE TEMP TABLE tmp_query_result AS <动态传入的查询语句>; -- 2. 统计结果行数 SET row_count = (SELECT COUNT(*) FROM tmp_query_result); -- 3. 分支执行导出 IF ($row_count > 0) THEN -- 非空场景走原有分区导出逻辑 COPY INTO @<stageName>/baseFolder/ FROM tmp_query_result PARTITION BY (col1 || '/' || CAST(col2 as INT)) FILE_FORMAT = <fileformat> HEADER = true DETAILED_OUTPUT = true; ELSE -- 空结果场景直接导出空表,生成带表头的空Parquet COPY INTO @<stageName>/baseFolder/ FROM tmp_query_result FILE_FORMAT = <fileformat> HEADER = true DETAILED_OUTPUT = true; END IF;
方案说明
- 全程不需要硬编码查询返回的列信息,临时表会自动继承动态查询的schema,完全适配任意结构的传入查询,不存在UNION方案需要提前对齐列结构的问题
- 仅执行一次传入的查询,不会出现重复执行查询带来的性能损耗、结果不一致问题
- 会话级临时表会在会话结束后自动清理,不会产生冗余存储
如果不想使用临时表,也可以通过LAST_QUERY_ID()+RESULT_SCAN的方式承接第一次查询的结果,逻辑和上述方案一致,仅需要注意RESULT_SCAN有结果有效期限制,长耗时存储过程场景优先用临时表方案稳定性更高。
内容的提问来源于stack exchange,提问作者Rakhs
相关产品推荐
相关产品推荐

