Postgresql

在其中一列中使用 json 字元串數組導入 CSV

  • May 17, 2022

CSV 的列具有格式text(可以包括空行)date和 JSON 字元串數組(類似於['a', 'b', 'c']。我一直在嘗試將該 CSV 複製到 PostgreSQL 表中(使用psycopg2’s copy_expert,它只是執行給定的COPY命令,如果這很重要)

表是用

CREATE TABLE posts(
   id SERIAL PRIMARY KEY,
   text TEXT NOT NULL,
   created_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
   rubrics jsonb[] NOT NULL
);

複製命令是

COPY posts(text, created_date, rubrics)
FROM STDIN
WITH CSV HEADER

STDINCSV 文件在哪裡。

我得到的錯誤是

malformed array literal: "['a', 'b', 'c']"
DETAIL:  "[" must introduce explicitly-specified array dimensions.
CONTEXT:  COPY posts, line 15, column rubrics: "['a', 'b', 'c']"

我已經嘗試了所有 4 種與 JSON 相關的數據類型(json並且jsonb有和沒有[]or [3]),包括方括號會產生上述錯誤,而省略它們(rubrics jsonb NOT NULL創建時)會產生新的錯誤:

invalid input syntax for type json
DETAIL:  Token "'" is invalid.
CONTEXT:  JSON data, line 1: ['...
COPY posts, line 15, column rubrics: "['a', 'b', 'c']"

除了手動修復要使用的 .csv{}而不是[]在複製之前,我還有什麼辦法嗎?感覺就像我一樣,但除了幾個有點但不完全相關的問題之外,我真的找不到任何東西。

關於評論的更新

我已將表創建更改為

CREATE TABLE posts(
   id SERIAL PRIMARY KEY,
   text TEXT NOT NULL,
   created_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
   rubrics jsonb NOT NULL
);

並在 CSV 中留下一個條目進行測試,所以看起來像這樣

text,created_date,rubrics
"Lorem

Ipsum

Test",2019-07-25 12:42:13,'["f", "o", "o"]'

我嘗試過的事情和我得到的錯誤

按原樣使用 CSV:

extra data after last expected column
CONTEXT:  COPY posts, line 6: ""Lorem

Ipsum

Test",2019-07-25 12:42:13,'["f", "o", "o"]'"

""數組周圍的另外一對( "'["f", "o", "o"]'")

invalid input syntax for type json
DETAIL:  Token "'" is invalid.
CONTEXT:  JSON data, line 1: '...
COPY posts, line 6, column rubrics: "'[f, o, o]'"

根本沒有引號(只有["f", "o", "o"]

extra data after last expected column
CONTEXT:  COPY posts, line 6: ""Lorem

Ipsum

Test",2019-07-25 12:42:13,["f", "o", "o"]"

雙引號代替單引號 ( "["f", "o", "o"]")

invalid input syntax for type json
DETAIL:  Token "f" is invalid.
CONTEXT:  JSON data, line 1: [f...
COPY posts, line 6, column rubrics: "[f, o, o]"

外雙引號,內單引號 ( "['f', 'o', 'o']")

invalid input syntax for type json
DETAIL:  Token "'" is invalid.
CONTEXT:  JSON data, line 1: ['...
COPY posts, line 6, column rubrics: "['f', 'o', 'o']"

畢竟我正在使用的 Python 庫有問題嗎?

JSON 是具有標準化語法的封裝層,輸入中的 JSON 欄位必須遵守該語法。

CSV 是在頂部應用的另一層封裝,其語法在 rfc 4180 中標準化程度較低。正如 RFC 中提到的,讀取或寫入 CSV 的程序並不總是嚴格遵守所有這些規則,但在引用規則方面,Postgres 是符合的。

JSON 在字元串文字周圍使用雙引號,因此問題中在字元串周圍使用單引號的所有嘗試對於 JSON 解析器都是無效的

CSV 要求雙引號加倍,請參閱 rfc 4180 的規則 #5 和 #7,因此所有嘗試在 JSON 中使用雙引號而不為 CSV 加倍或不將整個欄位括在雙引號中的所有嘗試對於 CSV 解析器都是無效的。

  1. 每個欄位可能包含也可能不包含在雙引號中(但是某些程序,例如 Microsoft Excel,根本不使用雙引號)。如果欄位沒有用雙引號括起來,則雙引號可能不會出現在欄位內
  1. 如果使用雙引號將欄位括起來,則出現在欄位內的雙引號必須通過在其前面加上另一個雙引號來進行轉義

您的範例稍作編輯以符合 CSV 和 JSON 確實有效:

CREATE TABLE posts(
   id SERIAL PRIMARY KEY,
   text TEXT NOT NULL,
   created_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
   rubrics jsonb NOT NULL
);

COPY posts(text, created_date, rubrics)
FROM STDIN WITH CSV;
foo,2022-05-17,"[""a"", ""b"", ""c""]"
\.

select * from posts;
id | text |    created_date     |     rubrics     
----+------+---------------------+-----------------
 1 | foo  | 2022-05-17 00:00:00 | ["a", "b", "c"]

如果你想要一個 JSON 對象的 Postgres 數組,那會更複雜,因為將 Postgres 數組表示為文本需要使用自己的語法規則rubrics進行另一層封裝,因此必須使用 COPY 生成要導入的有效數據

JSON 引用 -> postgres 數組引用 -> CSV 引用

但如您的範例中所見,您似乎不需要 JSON 對象的 Postgres 數組作為列,而是需要包含 JSON 數組的單個 JSON 值。

引用自:https://dba.stackexchange.com/questions/312245