如何从 CSV 为 PostgreSQL 副本生成模式
How to generate a schema from a CSV for a PostgreSQL Copy
给定一个包含几十个或更多列的 CSV,如何创建一个可以在 PostgreSQL 中的 CREATE TABLE SQL 表达式中使用的 'schema'与 COPY 工具一起使用?
我看到了很多关于 COPY 工具和基本 CREATE TABLE 表达式的示例,但是没有详细说明当您有可能无法手动创建模式的列数时的情况。
基本上,您应该在数据库外准备数据(包括其结构),使用 ready-made 工具或使用 python、ruby 或您选择的语言。
但是,如果没有这样的机会,您可以使用 plpgsql 做很多事情。
创建带有文本列的 table
csv 格式的文件不包含有关列类型、主键或外键等的任何信息。
您可以相对轻松地创建带有文本列的 table 并将数据复制到其中。
之后您应该手动更改列的类型并添加约束。
create or replace function import_csv(csv_file text, table_name text)
returns void language plpgsql as $$
begin
create temp table import (line text) on commit drop;
execute format('copy import from %L', csv_file);
execute format('create table %I (%s);',
table_name, concat(replace(line, ',', ' text, '), ' text'))
from import limit 1;
execute format('copy %I from %L (format csv, header)', table_name, csv_file);
end $$;
文件中的示例数据 c:\data\test.csv
:
id,a_text,a_date,a_timestamp,an_array
1,str 1,2016-08-01,2016-08-01 10:10:10,"{1,2}"
2,str 2,2016-08-02,2016-08-02 10:10:10,"{1,2,3}"
3,str 3,2016-08-03,2016-08-03 10:10:10,"{1,2,3,4}"
导入:
select import_csv('c:\data\test.csv', 'new_table');
select * from new_table;
id | a_text | a_date | a_timestamp | an_array
----+--------+------------+---------------------+-----------
1 | str 1 | 2016-08-01 | 2016-08-01 10:10:10 | {1,2}
2 | str 2 | 2016-08-02 | 2016-08-02 10:10:10 | {1,2,3}
3 | str 3 | 2016-08-03 | 2016-08-03 10:10:10 | {1,2,3,4}
(3 rows)
大型 csv 文件
上述函数导入数据两次(到临时和目标 tables)。
对于大文件,这可能会严重浪费时间并给服务器带来不必要的负载。
一种解决方案是将一个 csv 文件拆分为两个文件,一个包含 header,一个包含数据。
那么函数应该是这样的:
create or replace function import_csv(header_file text, data_file text, table_name text)
returns void language plpgsql as $$
begin
create temp table import (line text) on commit drop;
execute format('copy import from %L', header_file);
execute format('create table %I (%s);',
table_name, concat(replace(line, ',', ' text, '), ' text'))
from import;
execute format('copy %I from %L (format csv)', table_name, data_file);
end $$;
改变列类型
您可以尝试根据内容自动更改列类型。
如果您处理的是简单类型并且文件中的数据始终保留特定格式,那么您就可以成功。但是,总的来说这是一项复杂的任务,下面列出的功能应仅作为示例考虑。
根据内容确定列类型(编辑函数以添加所需的转换):
create or replace function column_type(val text)
returns text language sql as $$
select
case
when val ~ '^[\+-]{0,1}\d+$' then 'integer'
when val ~ '^[\+-]{0,1}\d*\.\d+$' then 'numeric'
when val ~ '^\d\d\d\d-\d\d-\d\d$' then 'date'
when val ~ '^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$' then 'timestamp'
end
$$;
使用上述函数更改列类型:
create or replace function alter_column_types(table_name text)
returns void language plpgsql as $$
declare
rec record;
qry text;
begin
for rec in
execute format(
'select key, column_type(value) ctype
from (
select row_to_json(t) a_row
from %I t
limit 1
) s, json_each_text (a_row)',
table_name)
loop
if rec.ctype is not null then
qry:= format(
'%salter table %I alter %I type %s using %s::%s;',
qry, table_name, rec.key, rec.ctype, rec.key, rec.ctype);
end if;
end loop;
execute(qry);
end $$;
使用:
select alter_column_types('new_table');
\d new_table
Table "public.new_table"
Column | Type | Modifiers
-------------+-----------------------------+-----------
id | integer |
a_text | text |
a_date | date |
a_timestamp | timestamp without time zone |
an_array | text |
(嗯,正确识别数组类型是相当复杂的)
如果 CSV 不是太大并且在您的本地计算机上可用,那么 csvkit is the simplest solution. It also contains a number of other utilities for working with CSVs, so it is a usefull tool 了解一般情况。
最简单的输入 shell:
$ csvsql myfile.csv
将打印出所需的 CREATE TABLE
SQL 命令,可以使用输出重定向将其保存到文件中。
如果您还提供连接字符串 csvsql
将创建 table 并一次性上传文件:
$ csvsql --db "$MY_DB_URI" --insert myfile.csv
还有一些选项可以指定您正在使用的 SQL 和 CSV 的风格。它们记录在内置帮助中:
$ csvsql -h
usage: csvsql [-h] [-d DELIMITER] [-t] [-q QUOTECHAR] [-u {0,1,2,3}] [-b]
[-p ESCAPECHAR] [-z MAXFIELDSIZE] [-e ENCODING] [-S] [-H] [-v]
[--zero] [-y SNIFFLIMIT]
[-i {access,sybase,sqlite,informix,firebird,mysql,oracle,maxdb,postgresql,mssql}]
[--db CONNECTION_STRING] [--query QUERY] [--insert]
[--tables TABLE_NAMES] [--no-constraints] [--no-create]
[--blanks] [--no-inference] [--db-schema DB_SCHEMA]
[FILE [FILE ...]]
Generate SQL statements for one or more CSV files, create execute those
statements directly on a database, and execute one or more SQL queries.
positional arguments:
FILE The CSV file(s) to operate on. If omitted, will accept
input on STDIN.
optional arguments:
-h, --help show this help message and exit
-d DELIMITER, --delimiter DELIMITER
Delimiting character of the input CSV file.
-t, --tabs Specifies that the input CSV file is delimited with
tabs. Overrides "-d".
-q QUOTECHAR, --quotechar QUOTECHAR
Character used to quote strings in the input CSV file.
-u {0,1,2,3}, --quoting {0,1,2,3}
Quoting style used in the input CSV file. 0 = Quote
Minimal, 1 = Quote All, 2 = Quote Non-numeric, 3 =
Quote None.
-b, --doublequote Whether or not double quotes are doubled in the input
CSV file.
-p ESCAPECHAR, --escapechar ESCAPECHAR
Character used to escape the delimiter if --quoting 3
("Quote None") is specified and to escape the
QUOTECHAR if --doublequote is not specified.
-z MAXFIELDSIZE, --maxfieldsize MAXFIELDSIZE
Maximum length of a single field in the input CSV
file.
-e ENCODING, --encoding ENCODING
Specify the encoding the input CSV file.
-S, --skipinitialspace
Ignore whitespace immediately following the delimiter.
-H, --no-header-row Specifies that the input CSV file has no header row.
Will create default headers.
-v, --verbose Print detailed tracebacks when errors occur.
--zero When interpreting or displaying column numbers, use
zero-based numbering instead of the default 1-based
numbering.
-y SNIFFLIMIT, --snifflimit SNIFFLIMIT
Limit CSV dialect sniffing to the specified number of
bytes. Specify "0" to disable sniffing entirely.
-i {access,sybase,sqlite,informix,firebird,mysql,oracle,maxdb,postgresql,mssql}, --dialect {access,sybase,sqlite,informix,firebird,mysql,oracle,maxdb,postgresql,mssql}
Dialect of SQL to generate. Only valid when --db is
not specified.
--db CONNECTION_STRING
If present, a sqlalchemy connection string to use to
directly execute generated SQL on a database.
--query QUERY Execute one or more SQL queries delimited by ";" and
output the result of the last query as CSV.
--insert In addition to creating the table, also insert the
data into the table. Only valid when --db is
specified.
--tables TABLE_NAMES Specify one or more names for the tables to be
created. If omitted, the filename (minus extension) or
"stdin" will be used.
--no-constraints Generate a schema without length limits or null
checks. Useful when sampling big tables.
--no-create Skip creating a table. Only valid when --insert is
specified.
--blanks Do not coerce empty strings to NULL values.
--no-inference Disable type inference when parsing the input.
--db-schema DB_SCHEMA
Optional name of database schema to create table(s)
in.
其他几个工具也可以进行模式推断,包括:
- Apache Spark
- Pandas (Python)
- 火焰 (Python)
- read.csv + R 中你最喜欢的数据库包
它们中的每一个都具有将 CSV(和其他格式)读入通常称为 DataFrame 或类似数据结构的表格数据结构的功能,从而推断该过程中的列类型。然后他们有其他命令来写出等效的 SQL 模式或将 DataFrame 直接上传到指定的数据库中。工具的选择将取决于数据量、数据的存储方式、CSV 的特性、目标数据库以及您喜欢使用的语言。
给定一个包含几十个或更多列的 CSV,如何创建一个可以在 PostgreSQL 中的 CREATE TABLE SQL 表达式中使用的 'schema'与 COPY 工具一起使用?
我看到了很多关于 COPY 工具和基本 CREATE TABLE 表达式的示例,但是没有详细说明当您有可能无法手动创建模式的列数时的情况。
基本上,您应该在数据库外准备数据(包括其结构),使用 ready-made 工具或使用 python、ruby 或您选择的语言。 但是,如果没有这样的机会,您可以使用 plpgsql 做很多事情。
创建带有文本列的 table
csv 格式的文件不包含有关列类型、主键或外键等的任何信息。 您可以相对轻松地创建带有文本列的 table 并将数据复制到其中。 之后您应该手动更改列的类型并添加约束。
create or replace function import_csv(csv_file text, table_name text)
returns void language plpgsql as $$
begin
create temp table import (line text) on commit drop;
execute format('copy import from %L', csv_file);
execute format('create table %I (%s);',
table_name, concat(replace(line, ',', ' text, '), ' text'))
from import limit 1;
execute format('copy %I from %L (format csv, header)', table_name, csv_file);
end $$;
文件中的示例数据 c:\data\test.csv
:
id,a_text,a_date,a_timestamp,an_array
1,str 1,2016-08-01,2016-08-01 10:10:10,"{1,2}"
2,str 2,2016-08-02,2016-08-02 10:10:10,"{1,2,3}"
3,str 3,2016-08-03,2016-08-03 10:10:10,"{1,2,3,4}"
导入:
select import_csv('c:\data\test.csv', 'new_table');
select * from new_table;
id | a_text | a_date | a_timestamp | an_array
----+--------+------------+---------------------+-----------
1 | str 1 | 2016-08-01 | 2016-08-01 10:10:10 | {1,2}
2 | str 2 | 2016-08-02 | 2016-08-02 10:10:10 | {1,2,3}
3 | str 3 | 2016-08-03 | 2016-08-03 10:10:10 | {1,2,3,4}
(3 rows)
大型 csv 文件
上述函数导入数据两次(到临时和目标 tables)。 对于大文件,这可能会严重浪费时间并给服务器带来不必要的负载。 一种解决方案是将一个 csv 文件拆分为两个文件,一个包含 header,一个包含数据。 那么函数应该是这样的:
create or replace function import_csv(header_file text, data_file text, table_name text)
returns void language plpgsql as $$
begin
create temp table import (line text) on commit drop;
execute format('copy import from %L', header_file);
execute format('create table %I (%s);',
table_name, concat(replace(line, ',', ' text, '), ' text'))
from import;
execute format('copy %I from %L (format csv)', table_name, data_file);
end $$;
改变列类型
您可以尝试根据内容自动更改列类型。 如果您处理的是简单类型并且文件中的数据始终保留特定格式,那么您就可以成功。但是,总的来说这是一项复杂的任务,下面列出的功能应仅作为示例考虑。
根据内容确定列类型(编辑函数以添加所需的转换):
create or replace function column_type(val text)
returns text language sql as $$
select
case
when val ~ '^[\+-]{0,1}\d+$' then 'integer'
when val ~ '^[\+-]{0,1}\d*\.\d+$' then 'numeric'
when val ~ '^\d\d\d\d-\d\d-\d\d$' then 'date'
when val ~ '^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$' then 'timestamp'
end
$$;
使用上述函数更改列类型:
create or replace function alter_column_types(table_name text)
returns void language plpgsql as $$
declare
rec record;
qry text;
begin
for rec in
execute format(
'select key, column_type(value) ctype
from (
select row_to_json(t) a_row
from %I t
limit 1
) s, json_each_text (a_row)',
table_name)
loop
if rec.ctype is not null then
qry:= format(
'%salter table %I alter %I type %s using %s::%s;',
qry, table_name, rec.key, rec.ctype, rec.key, rec.ctype);
end if;
end loop;
execute(qry);
end $$;
使用:
select alter_column_types('new_table');
\d new_table
Table "public.new_table"
Column | Type | Modifiers
-------------+-----------------------------+-----------
id | integer |
a_text | text |
a_date | date |
a_timestamp | timestamp without time zone |
an_array | text |
(嗯,正确识别数组类型是相当复杂的)
如果 CSV 不是太大并且在您的本地计算机上可用,那么 csvkit is the simplest solution. It also contains a number of other utilities for working with CSVs, so it is a usefull tool 了解一般情况。
最简单的输入 shell:
$ csvsql myfile.csv
将打印出所需的 CREATE TABLE
SQL 命令,可以使用输出重定向将其保存到文件中。
如果您还提供连接字符串 csvsql
将创建 table 并一次性上传文件:
$ csvsql --db "$MY_DB_URI" --insert myfile.csv
还有一些选项可以指定您正在使用的 SQL 和 CSV 的风格。它们记录在内置帮助中:
$ csvsql -h
usage: csvsql [-h] [-d DELIMITER] [-t] [-q QUOTECHAR] [-u {0,1,2,3}] [-b]
[-p ESCAPECHAR] [-z MAXFIELDSIZE] [-e ENCODING] [-S] [-H] [-v]
[--zero] [-y SNIFFLIMIT]
[-i {access,sybase,sqlite,informix,firebird,mysql,oracle,maxdb,postgresql,mssql}]
[--db CONNECTION_STRING] [--query QUERY] [--insert]
[--tables TABLE_NAMES] [--no-constraints] [--no-create]
[--blanks] [--no-inference] [--db-schema DB_SCHEMA]
[FILE [FILE ...]]
Generate SQL statements for one or more CSV files, create execute those
statements directly on a database, and execute one or more SQL queries.
positional arguments:
FILE The CSV file(s) to operate on. If omitted, will accept
input on STDIN.
optional arguments:
-h, --help show this help message and exit
-d DELIMITER, --delimiter DELIMITER
Delimiting character of the input CSV file.
-t, --tabs Specifies that the input CSV file is delimited with
tabs. Overrides "-d".
-q QUOTECHAR, --quotechar QUOTECHAR
Character used to quote strings in the input CSV file.
-u {0,1,2,3}, --quoting {0,1,2,3}
Quoting style used in the input CSV file. 0 = Quote
Minimal, 1 = Quote All, 2 = Quote Non-numeric, 3 =
Quote None.
-b, --doublequote Whether or not double quotes are doubled in the input
CSV file.
-p ESCAPECHAR, --escapechar ESCAPECHAR
Character used to escape the delimiter if --quoting 3
("Quote None") is specified and to escape the
QUOTECHAR if --doublequote is not specified.
-z MAXFIELDSIZE, --maxfieldsize MAXFIELDSIZE
Maximum length of a single field in the input CSV
file.
-e ENCODING, --encoding ENCODING
Specify the encoding the input CSV file.
-S, --skipinitialspace
Ignore whitespace immediately following the delimiter.
-H, --no-header-row Specifies that the input CSV file has no header row.
Will create default headers.
-v, --verbose Print detailed tracebacks when errors occur.
--zero When interpreting or displaying column numbers, use
zero-based numbering instead of the default 1-based
numbering.
-y SNIFFLIMIT, --snifflimit SNIFFLIMIT
Limit CSV dialect sniffing to the specified number of
bytes. Specify "0" to disable sniffing entirely.
-i {access,sybase,sqlite,informix,firebird,mysql,oracle,maxdb,postgresql,mssql}, --dialect {access,sybase,sqlite,informix,firebird,mysql,oracle,maxdb,postgresql,mssql}
Dialect of SQL to generate. Only valid when --db is
not specified.
--db CONNECTION_STRING
If present, a sqlalchemy connection string to use to
directly execute generated SQL on a database.
--query QUERY Execute one or more SQL queries delimited by ";" and
output the result of the last query as CSV.
--insert In addition to creating the table, also insert the
data into the table. Only valid when --db is
specified.
--tables TABLE_NAMES Specify one or more names for the tables to be
created. If omitted, the filename (minus extension) or
"stdin" will be used.
--no-constraints Generate a schema without length limits or null
checks. Useful when sampling big tables.
--no-create Skip creating a table. Only valid when --insert is
specified.
--blanks Do not coerce empty strings to NULL values.
--no-inference Disable type inference when parsing the input.
--db-schema DB_SCHEMA
Optional name of database schema to create table(s)
in.
其他几个工具也可以进行模式推断,包括:
- Apache Spark
- Pandas (Python)
- 火焰 (Python)
- read.csv + R 中你最喜欢的数据库包
它们中的每一个都具有将 CSV(和其他格式)读入通常称为 DataFrame 或类似数据结构的表格数据结构的功能,从而推断该过程中的列类型。然后他们有其他命令来写出等效的 SQL 模式或将 DataFrame 直接上传到指定的数据库中。工具的选择将取决于数据量、数据的存储方式、CSV 的特性、目标数据库以及您喜欢使用的语言。