Postgresql 在psql cli中使用\set变量在正常查询中工作,但在\copy中不工作/扩展

Postgresql 在psql cli中使用\set变量在正常查询中工作,但在\copy中不工作/扩展,postgresql,psql,Postgresql,Psql,如标题所述,问题在于使用\set设置的psql变量对我有效,除非在\copy功能中使用psql客户端提供的 在\copy中引用psql变量是否需要一些特殊语法?还是我运气不好?如果是这样的话,这有没有记录在案 我在StackOverflow中找不到这个问题,也没有任何文档记录。我看了大约20个帖子,但什么也没找到。我还检查了(CLI的版本)并没有发现任何关于这方面的警告-我在页面中搜索了“variable”,但没有找到任何与此相关的内容。我还搜索了“扩展”和“扩展”,但什么也没找到。所以现在我在

如标题所述,问题在于使用
\set
设置的
psql
变量对我有效,除非在
\copy
功能中使用
psql
客户端提供的

\copy
中引用
psql
变量是否需要一些特殊语法?还是我运气不好?如果是这样的话,这有没有记录在案

我在StackOverflow中找不到这个问题,也没有任何文档记录。我看了大约20个帖子,但什么也没找到。我还检查了(CLI的版本)并没有发现任何关于这方面的警告-我在页面中搜索了“variable”,但没有找到任何与此相关的内容。我还搜索了“扩展”和“扩展”,但什么也没找到。所以现在我在这里寻求帮助

PostgreSQL client的版本是11.10,Debian应用了以下任何下游补丁:

psql (PostgreSQL) 11.10 (Debian 11.10-1.pgdg100+1)
我很确定服务器版本几乎没有相关性,但为了更全面,服务器是Ubuntu提供的10.13版本:

psql (PostgreSQL) 10.13 (Ubuntu 10.13-1.pgdg16.04+1) 
复制

我知道
\copy
copy
之间的区别(一个是在
psql
客户端中实现的功能,另一个是在服务器进程上下文中执行的服务器功能),对于这个任务,我需要使用的肯定是
\copy

显示我正在正确设置和引用变量的标准查询:

[local:/tmp]:5432 dbuser@dbdev# \set var_tname ag_test
[local:/tmp]:5432 dbuser@dbdev# \set var_cname fname
[local:/tmp]:5432 dbuser@dbdev# SELECT * from :var_tname WHERE :var_cname = 'TestVal' LIMIT 1;
fname|lname|score|nonce
TestVal|C|100|b
(1 row)
Time: 88.786 ms
失败案例,因为变量在
\copy
中引用,所以似乎失败了-我看不出这与工作示例之间有任何其他区别:

[local:/tmp]:5432 dbuser@dbdev# \set var_tname ag_test
[local:/tmp]:5432 dbuser@dbdev# \set var_cname fname
[local:/tmp]:5432 dbuser@dbdev# \copy (SELECT * from :var_tname WHERE :var_cname = 'TestVal' LIMIT 1) TO 'testvar.csv';
ERROR:  syntax error at or near ":"
LINE 1: COPY  ( SELECT * from :var_tname WHERE :var_cname = 'TestVal...
                              ^
Time: 193.322 ms
显然,基于错误,扩展没有发生,查询试图引用一个文本名为
:var\u tname

我没想到引用这句话会有帮助,但只是以防万一——谁知道,这可能是一个奇怪的例外,对吧?毫不奇怪,这也无济于事:

[local:/tmp]:5432 dbuser@dbdev# \copy (SELECT * from :'var_tname' WHERE :var_cname = 'TestVal' LIMIT 1) TO 'testvar.csv';
ERROR:  syntax error at or near ":"
LINE 1: COPY  ( SELECT * from : 'var_tname' WHERE :var_cname = 'Test...
                              ^
Time: 152.407 ms
[local:/tmp]:5432 dbuser@dbdev# \set var_tname 'ag_test'
[local:/tmp]:5432 dbuser@dbdev# \copy (SELECT * from :var_tname WHERE :var_cname = 'TestVal' LIMIT 1) TO 'testvar.csv';
ERROR:  syntax error at or near ":"
LINE 1: COPY  ( SELECT * from :var_tname WHERE :var_cname = 'TestVal...
                              ^
Time: 153.001 ms
[local:/tmp]:5432 dbuser@dbdev# \copy (SELECT * from :'var_tname' WHERE :var_cname = 'TestVal' LIMIT 1) TO 'testvar.csv';
ERROR:  syntax error at or near ":"
LINE 1: COPY  ( SELECT * from : 'var_tname' WHERE :var_cname = 'Test...
                              ^
Time: 153.459 ms
我还尝试使用单引号设置变量(这可能是最好的做法),但这没有什么区别:

[local:/tmp]:5432 dbuser@dbdev# \set var_tname 'ag_test'
[local:/tmp]:5432 dbuser@dbdev# \set var_cname 'fname'
... <same behavior as above> ...
这是可行的,但它有点笨重,似乎不应该是不必要的

使用\pset fieldsetp生成TSV并将stdout重定向到文件(笨重,可能有转义问题)

另一个选项是在将分隔符设置为tab后,不使用
\copy
并将stdout管道化到文件:

[local:/tmp]:5432 dbuser@dbdev# \set var_tname ag_test
[local:/tmp]:5432 dbuser@dbdev# \pset format unaligned
Output format is unaligned.
[local:/tmp]:5432 dbuser@dbdev# \pset fieldsep '\t'
Field separator is "    ".
[local:/tmp]:5432 dbuser@dbdev# SELECT * from :var_tname LIMIT 1;
fname   lname   score   nonce
TestVal G   500 a
(1 row)
Time: 91.596 ms
[local:/tmp]:5432 dbuser@dbdev# 
这可以通过
psql-f query.psql>/the/output/path.tsv
调用。我还没有检查,但我认为应该生成一个有效的TSV文件。我不确定的一点是,它是否会正确地转义或引用包含制表符的列值,例如
\copy
copy

在shell脚本中进行扩展并写入临时psql文件,使用psql-f tmp.psql

最后的解决方法是在shell脚本中设置变量,并使用
psql-c“$shellvar”
调用,或者将shell扩展查询写入一个临时的
.psql
文件,然后使用
psql-f
调用,并删除临时文件

用例(以及为什么我不特别喜欢某些解决方法)

我可能应该提到用例。。。我有几个单独的(但相关的)Python应用程序,它们收集、解析和处理数据,然后使用psycopg2将数据加载到数据库中。一旦原始数据进入数据库,为了可读性和减少需要维护的代码量,我将大量较重的逻辑委托给psql文件

psql文件在应用程序完成时使用如下方式调用:

for psql_file in glob.glob(os.path.expandvars("$VIRTUAL_ENV/etc/<appname>/psql-post.d/*.psql:
    subprocess.call([which('psql'), '-f', psql_file])
glob.glob(os.path.expandvars(“$VIRTUAL_ENV/etc//psql post.d/*.psql: 调用([which('psql'),'-f',psql_文件]) 我希望使用变量作为表名(和一些列名)的原因之一是因为数据库当前正在重构/重建,因此表名和一些列名将随着时间的推移而重命名。因为一些
.psql
脚本非常广泛,表名在其中被引用了很多次-因此使用
\set
在顶部设置一次更为合理,因此,当数据库中的每个表都发生更改时,每个psql文件只需要进行一次更改。将来可能会有一些小的更改,使这种方法比需要搜索和替换10-15个不同列或表名实例的方法更好

我不想使用的最后一种解决方法:从Python模板化psql文件


我意识到我可以直接从Python代码中使用一些自制的模板或Jinja2来动态地从模板中生成PSQL文件。但是我更喜欢在文件中使用纯PSQL,因为对于那些可能需要执行代码检查或接管项目维护的人来说,它使项目更具可读性和可编辑性在未来。这对我来说也更容易处理。显然,一旦我们开始讨论从Python内部通过psycopg2使用查询来实现这一点,就有大量的解决方案可供选择,但是将
.psql
文件放在每个项目存储库的同一相对目录中是非常有用的

这似乎是一种解析发布
\copy
更新:实际上是记录在案的行为:

\抄袭 ... 与大多数其他元命令不同,该行的整个剩余部分始终被视为\copy的参数,并且在参数中既不执行变量插值,也不执行反引号扩展

小费

获得与\copy…to相同结果的另一种方法是使用SQL>copy…to STDOUT命令并用\g filename或\g>| program终止它。与\copy不同,此方法允许命令跨越多个>
for psql_file in glob.glob(os.path.expandvars("$VIRTUAL_ENV/etc/<appname>/psql-post.d/*.psql:
    subprocess.call([which('psql'), '-f', psql_file])
\set var_tname 'cell_per'

\copy (select * from :var_tname) to stdout WITH (FORMAT CSV, HEADER);
ERROR:  syntax error at or near ":"
LINE 1: COPY  ( select * from :var_tname ) TO STDOUT WITH (FORMAT CS...

 \copy (select * from :"var_tname") to stdout WITH (FORMAT CSV, HEADER);
ERROR:  syntax error at or near ":"
LINE 1: COPY  ( select * from : "var_tname" ) TO STDOUT WITH (FORMAT...

--Note the added space when using the suggested method of including a variable as 
--table name. 

copy (select * from :var_tname) to stdout WITH (FORMAT CSV, HEADER);
copy (select * from :"var_tname") to stdout WITH (FORMAT CSV, HEADER);

--Using COPY directly works.

--So:

\o cp.csv
copy (select * from :var_tname) to stdout WITH (FORMAT CSV, HEADER);
\o

--This opens file cp.csv  COPYs  to it and then closes file. 

-- Or per docs example and UPDATE:

copy (select * from :var_tname) to stdout WITH (FORMAT CSV, HEADER) \g cp.csv


cat cp.csv  

line_id,category,cell_per,ts_insert,ts_update,user_insert,user_update,plant_type,season,short_category
5,H PREM 3.5,18,,06/02/2004 15:11:26,,postgres,herb,none,HP3
7,HERB G,1,,06/02/2004 15:11:26,,postgres,herb,none,HG
9,HERB TOP,1,,06/02/2004 15:11:26,,postgres,herb,none,HT
10,VEGGIES,1,,06/02/2004 15:11:26,,postgres,herb,annual,VG