Bmo*_*moe 5 postgresql dbms temporary-tables csv
我想知道是否有办法将 csv 文件复制到临时表中,其中 csv 文件中的列数未知。我使用的数据库软件是 PgAdmin III。我发现如果我知道列数,那么我可以创建一个包含该列数的临时表,然后像这样复制 csv 文件:
CREATE TEMPORARY TABLE temp
(
col1 VARCHAR(80),
col2 VARCHAR(80),
....
coln VARCHAR(80)
);
COPY temp FROM 'C:/Users/postgres/Boost.txt' CSV HEADER DELIMITER E' '
Run Code Online (Sandbox Code Playgroud)
但是,如果我尝试简单地将 csv 文件复制到临时表中没有列的临时表,Postgresql(8.4 版)会抱怨我正在使用一个列数少于 csv 文件的表。我一直在研究,但似乎在 Postgresql 文档中找不到任何关于此的内容。有谁知道在 Postgresql 中是否可以将 csv 文件复制到具有在运行时决定的任意数量的列的临时表中?一旦临时表加载了 csv 文件,我计划在它被销毁之前用临时表与其他表进行一些比较。csv 文件中的第一行也包含标题。
基本:
\nPROGRAM的子句需要Postgres 9.3+。COPYGET DIAGNOSTICSCOPYformat()需要 Postgres 9.1+headshell 预期提供的命令。对于 Windows 版本,请考虑:该函数完全动态地复制任何表结构:
\nCREATE OR REPLACE FUNCTION f_dynamic_copy(_file text\n , _tbl text = \'tmp1\'\n , _delim text = E\'\\t\'\n , _nodelim text = chr(127)) -- see below!\n RETURNS text\n LANGUAGE plpgsql AS\n$func$\nDECLARE\n row_ct int;\nBEGIN\n -- create staging table for 1st row as single text column \n CREATE TEMP TABLE tmp0(cols text) ON COMMIT DROP;\n\n -- fetch 1st row\n EXECUTE format($$COPY tmp0 FROM PROGRAM \'head -n1 %I\' WITH (DELIMITER %L)$$ -- impossible delimiter\n , _file, _nodelim);\n\n -- create actual temp table with all columns text\n EXECUTE (\n SELECT format(\'CREATE TEMP TABLE %I(\', _tbl)\n || string_agg(quote_ident(col) || \' text\', \',\')\n || \')\'\n FROM (SELECT cols FROM tmp0 LIMIT 1) t\n , unnest(string_to_array(t.cols, E\'\\t\')) col\n );\n\n -- Import data\n EXECUTE format($$COPY %I FROM %L WITH (FORMAT csv, HEADER, NULL \'\\N\', DELIMITER %L)$$\n , _tbl, _file, _delim);\n\n GET DIAGNOSTICS row_ct = ROW_COUNT;\n RETURN format(\'Created table %I with %s rows.\', _tbl, row_ct);\nEND\n$func$;\nRun Code Online (Sandbox Code Playgroud)\n调用变体:
\nSELECT f_dynamic_copy(\'/path/to/file.csv\');\nSELECT f_dynamic_copy(\'/path/to/file2.csv\', \'tmp_file2\');\nSELECT f_dynamic_copy(_file => \'/path/to/file2.csv\'\n , _tbl => \'tmp_file2\');\n , _delim => E\'\\t\'); -- using assignment operator since pg 9.5\nRun Code Online (Sandbox Code Playgroud)\n回答:
\nCREATE OR REPLACE FUNCTION f_dynamic_copy(_file text\n , _tbl text = \'tmp1\'\n , _delim text = E\'\\t\'\n , _nodelim text = chr(127)) -- see below!\n RETURNS text\n LANGUAGE plpgsql AS\n$func$\nDECLARE\n row_ct int;\nBEGIN\n -- create staging table for 1st row as single text column \n CREATE TEMP TABLE tmp0(cols text) ON COMMIT DROP;\n\n -- fetch 1st row\n EXECUTE format($$COPY tmp0 FROM PROGRAM \'head -n1 %I\' WITH (DELIMITER %L)$$ -- impossible delimiter\n , _file, _nodelim);\n\n -- create actual temp table with all columns text\n EXECUTE (\n SELECT format(\'CREATE TEMP TABLE %I(\', _tbl)\n || string_agg(quote_ident(col) || \' text\', \',\')\n || \')\'\n FROM (SELECT cols FROM tmp0 LIMIT 1) t\n , unnest(string_to_array(t.cols, E\'\\t\')) col\n );\n\n -- Import data\n EXECUTE format($$COPY %I FROM %L WITH (FORMAT csv, HEADER, NULL \'\\N\', DELIMITER %L)$$\n , _tbl, _file, _delim);\n\n GET DIAGNOSTICS row_ct = ROW_COUNT;\n RETURN format(\'Created table %I with %s rows.\', _tbl, row_ct);\nEND\n$func$;\nRun Code Online (Sandbox Code Playgroud)\n在 main 之前COPY,运行初步操作COPY ... TO tmp0以获取带有列名称的第一行,这些列名称预计是不带引号、区分大小写的字符串,就像COPY ... TO ... (FORMAT csv, HEADER)导出它们一样。
实际目标表的结构源自它,所有列的数据类型text为。结果表的默认名称是tmp1- 或提供您自己的名称作为第二个函数参数。
然后就COPY被执行了。默认分隔符是制表符 - 或者提供分隔符作为第三个函数参数。
_nodelim使用未出现在 CSV 文件第一行中的任何单字节字符作为非分隔符。我任意选择控制字符“Delete”(ASCII 127)。该角色将在此处被吞噬,因此我改为生成chr(127),这也是有效的。假设字符不会弹出 - 或提供非分隔符作为第四个函数参数。
该函数返回表名称和导入的行数。
\n请记住,临时表会随着会话的结束而消失。
\n\n执行命令
\nPROGRAM可能会受到操作系统的访问控制机制(例如 SELinux)的限制。
SO的相关答案:
\n\nPostgres 8.4 太旧了,无法向后移植。一些提示:
\nGET DIAGNOSTICS是一个可选功能。您可以将其保留或替换为桌子上的完整计数
PROGRAM第 9.3 页中的子句的一个原始(昂贵)替代方案COPY是导入完整的表:
EXECUTE format($$COPY tmp0 FROM %L WITH (DELIMITER %L)$$, _file, _delim);\nRun Code Online (Sandbox Code Playgroud)\n或者您准备第二个输入文件,或者您可以通过从 shell 进行管道传输来使其工作:COPY tablename FROM STDIN在第 8.4 页中可用。
format()可以用普通字符串连接替换。但要小心 SQL 注入!| 归档时间: |
|
| 查看次数: |
19568 次 |
| 最近记录: |