将数组导出到 PL/pgSQL 中的 CSV 文件

Export an array into a CSV-file in PL/pgSQL

我有一个函数,RETURNS SETOF text[]。此函数的示例结果:

{080213806381,"personal data1","question 1",answer1,"question 2",answer2,"question 3","answer 3"}
{080213806382,"personal data1","question 1",answer1,"question 2",answer2,"question 3","answer 3"}

我用如下语句组成每一行:

resultRow := array_append(resultRow, fetchedRow.data::text);

然后:

RETURN NEXT resultRow;

这是我的 COPY 命令:

COPY( 
SELECT myFunction()
) TO 'D:\test_output.csv' WITH (FORMAT 'csv', DELIMITER E',', HEADER false)

我有几个问题:

  1. 尽管以相同的方式将值附加到数组,但其中一些值是双引号的,而另一些则不是。这在某种程度上取决于值中是否存在 space 字符。例如,查看数组的第一个元素或每行中的 answer2 和 "answer 3"。我想要一些统一的行为。
  2. 在使用 COPY 命令导出到 CSV 后,我得到了相同的行,这些行的开头和结尾都带有所有这些大括号。我不想要 CSV 格式的文件。

我能做些什么来解决这些问题?

您希望导出列数不同的行。您正在生成一组数组,但想从那里生成一个 CSV 文件。

眼前的问题 - 数组文字不是 CSV

你的函数 returns text[] 文字,即 PostgreSQL 数组文字。

这些文件不是公认的 CSV。是的,它们以逗号分隔,但它们遵循不同的语法规则。您不能可靠地将数组文字视为 CSV 行,反之亦然。

不要试图只是砍掉定界符 {...} 并将数组文字视为 CSV 行。

COPY 将无法正常工作或根本无法工作

COPY 不适合你。它旨在处理关系,即统一的结构化行集,其中每一列都是定义明确的类型,每一行都有相同数量的列。

您可以将您的函数重新定义为 return a setof record 并用空值填充您的记录以始终保持相同的宽度,但它会非常丑陋且受限,而且 CSV 将随后合并空值。

COPY 将做的是在单个 CSV 字段中导出包含数组文字的单列 CSV。这肯定不是你想要的。

解决方案 1:导出客户端

您最好在客户端执行此操作,通过脚本或程序生成 CSV。让程序接收数组集,然后通过合适的库将其写入 CSV,例如 Python 的 csv 模块。选择一种客户端脚本语言,其中 PostgreSQL 驱动程序可以理解数组并可以将它们转换为该语言格式的数组 - 同样,例如 psycopg2 for Python.

例如给定虚拟函数:

CREATE OR REPLACE FUNCTION get_rows() RETURNS setof text[] AS $$
VALUES
('{080213806381,"personal data1","question 1",answer1,"question 2",answer2,"question 3","answer 3"}'::text[]),
('{080213806382,"personal data1","question 1",answer1,"question 2",answer2,"question 3","answer 3","q4","a4"}'::text[])
$$ LANGUAGE SQL;

客户端脚本可以像这样简单:

#!/usr/bin/env python
import psycopg2
import csv

with psycopg2.connect('dbname=craig') as conn:
    curs = conn.cursor()

    with open("test.csv","w") as csvfile:
        f = csv.writer(csvfile)

        curs.execute("SELECT * FROM get_rows()")

        for row in curs:
            f.writerow(row[0])

解决方案 2:直接从过程中导出 CSV

或者,如果 CSV 文档不是太大,您可以在一个过程中生成整个 CSV,也许使用 plpythonu 和 csv 模块,或类似的 CSV 库用于您的首选程序语言.因为整个 CSV 文档必须累积在内存中,所以这不会扩展到非常非常大的文档。

使用文本数组作为结果格式是错误的想法 - 文本数组格式不能简单地转换为 CSV 格式。 Return table 改为

CREATE OR REPLACE FUNCTION foo()
RETURNS TABLE(c1 text, c2 text, c3 text, c4 text, c5 text, c6 text, c7 text, c8 text)
AS $$
  VALUES('080213806381','personal data1','question 1','answer1','question 2','answer2','question 3','answer 3'),
        ('080213806382','personal data1','question 1','answer1','question 2','answer2','question 3','answer 3');
$$ LANGUAGE sql;

postgres=# COPY (SELECT * FROM foo()) TO stdout CSV;
080213806381,personal data1,question 1,answer1,question 2,answer2,question 3,answer 3
080213806382,personal data1,question 1,answer1,question 2,answer2,question 3,answer 3
Time: 1.228 ms