0

我有一个函数,它返回 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. 不管值以相同的方式附加到数组中的事实,其中一些是双引号,而另一些则不是。这在某种程度上取决于值中是否存在空格字符。例如,查看数组的第一个元素或每行中的 answer2 和“answer 3”。我想要一些统一的行为。
  2. 使用 COPY 命令导出到 CSV 后,我在开头和结尾都得到了所有这些花括号的相同行。我不希望它们在 CSV 中。

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

4

2 回答 2

1

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

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

您的函数返回text[]文字,即 PostgreSQL 数组文字。

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

不要试图仅仅{...}切断分隔符并将数组文字视为 CSV 行。

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

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

您可以重新定义您的函数以返回 asetof record并用空值填充您的记录以始终保持相同的宽度,但它会非常难看和有限,而且 CSV 将包含空值。

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

解决方案 1:导出客户端

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

例如给定虚拟函数:

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 文档必须在内存中累积,所以这不会扩展到非常大的文档。

于 2015-09-16T11:59:29.537 回答
0

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

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
于 2015-09-16T12:27:31.917 回答