如标题中所述,问题是使用\set
设置的psql
变量对我有效,除非在psql
客户端提供的\copy
函数中使用
引用\copy
中的psql
变量是否需要一些特殊的语法?还是我运气不好如果是的话,有任何记录吗?
我在StackOverflow或任何地方都找不到这个问题。我看了20篇文章,但什么也没找到。我还检查了the documentation on \copy for PostgreSQL 11(CLI的版本),没有看到关于这一点的警告-我在页面上搜索了“变量”,没有发现任何与此相关的内容。我还搜索了“扩展”和“扩展”,什么也没找到。所以现在我在这里寻求帮助...
PostgreSQL客户端的版本是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_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> ...
型\copy
内部不支持变量扩展吗?如果是这样的话,这似乎是一个非常糟糕的限制,而且似乎没有记录在案
最后一点需要补充的是,我希望有人会问--我没有将这些实现为函数或存储过程的原因。首先,我的PostgreSQL版本根本不支持存储过程。它也不支持函数中的事务。即使这样,我希望这些查询在应用程序存储库中的psql文件中的真实的原因是,它们非常易于阅读以进行代码审查,易于维护以进行开发,并作为文档使用
- 除非您也有此问题并希望找到解决方法,否则没有必要阅读此内容
除此之外,我还记录了一堆我能很快想到的解决方法--这个问题可以用1001种不同的方法来解决。但是,如果有一个解决方案可以让我坚持下去,我宁愿知道它,而不是应用任何变通方法。我还在下面添加了用例信息,因为响应沿着于“您为什么要这样做?只是不要使用xyz功能,问题解决了!“。我希望不要收到这些回复:>
感谢任何愿意帮助的人!
解决方案选项
我有很多变通方法,但我真的很想了解为什么这不起作用,如果它在某个地方被记录下来,或者如果可能有一些特殊的方法导致在\copy
中使用时发生扩展,以避免需要更改这一点-原因我将在下面的 * 用例**部分解释
- 这是我想出的解决办法 *
- 使用变量SELECT到临时表中,\复制固定名称表 *
SELECT * INTO tmp_table FROM :var_tname WHERE :var_cname = 'TestVal' LIMIT 1;
\copy (SELECT * FROM tmp_table) TO 'testvar.csv'
型
这是可行的,但它有点笨重,似乎不应该是不必要的
- 使用\pset fieldsetp生成TSV并将stdout重定向到文件(笨重,可能有转义问题)*
另一个选项是不使用\copy
,并在将分隔符设置为tab后将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应用程序,它们收集、解析和处理数据,然后使用psycopg 2将它们加载到数据库中。一旦原始数据进入数据库,我就将一堆较重的逻辑委托给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])
型
我想对表名(和一些列名)使用变量的原因之一是因为数据库当前正在重构/重建,所以表名和一些列名将随着时间的推移而重命名。因为一些.psql
脚本非常广泛,表名在其中被引用了很多次-所以使用\set
在顶部设置它们一次更有意义,这样当数据库中的每个表被更改时,每个psql文件中只需要一次更改。将来可能会有一些小的变化,使这种方法比需要搜索和替换10-15个不同列名或表名的示例的方法更好
- 最后一个我不想使用的解决方法:从Python模板化psql文件 *
我意识到我也可以使用一些自己开发的模板或Jinja 2直接从Python代码中动态地从模板生成PSQL文件。但我更喜欢在文件中使用纯psql,因为它使项目更具可读性和可编辑性,对于那些可能需要执行代码审查或在未来接管项目维护的人来说。对我来说也更容易工作。显然,一旦我们开始讨论在Python中通过psycopg 2使用查询来实现这一点,就有大量的变通方法可供选择-但是将.psql
文件放在每个项目存储库的同一相对目录中会起到非常有用的作用
2条答案
按热度按时间bt1cpqcv1#
这似乎是
\copy
的解析问题。更新:实际上是一种记录在案的行为:https://www.postgresql.org/docs/current/app-psql.html\copy...与大多数其他元命令不同的是,该行的其余部分始终被视为\copy的参数,并且变量插值和反引号扩展都不会在参数中执行。
尖端
另一种获得与“复制”相同结果的方法。。使用SQL > COPY... TO STDOUT命令并以\g filename或\g >终止它|与\copy不同,此方法允许命令跨越多个>行;还可以使用变量插值和反引号扩展。
字符串
a0zr77ik2#
我的解决方案是使用shell脚本重命名目标文件为预定名称,然后启动引用该名称的psql脚本。
字符串