在参数化查询中传递数据类型区间的值

Yog*_*sch 7 postgresql datatypes parameter interval postgresql-10

上下文正在从其余服务器连接到 Postgres 数据库。

考虑一个假设的代表性示例:我希望能够获取帐户创建日期比任意值早/新的名称列表。

在下面的示例查询中,表结构很简单 -name是 type text,并且creation_date是 type timestamp。所以当我做类似的事情时

server_pg_module:query("select name from new_table where 
current_timestamp - creation_date < '6 days'") 
Run Code Online (Sandbox Code Playgroud)

它工作得很好。但我真正想做的是6 days从服务器获取该值。所以我尝试像

server_pg_module:query("select name from new_table where
current_timestamp - timestamp < $1", ["6 days"]
Run Code Online (Sandbox Code Playgroud)

它抛出一个错误。我试过'6 days'"'6 days'"以及其他一些混合物,都抛出错误。所以为了检查我添加了一个新interval的类型列interval并尝试了一个查询

server_pg_module:query("insert into new_table (name, interval) values ($1, '3 day')", ["fooo"]). 
Run Code Online (Sandbox Code Playgroud)

哪个有效,但是

server_pg_module:query("insert into new_table (name, interval) values ($1, $2)", ["fooo", "3 days"]). 
Run Code Online (Sandbox Code Playgroud)

休息。为了更好的衡量,除了"'3 days'"上面提到的混合物之外,我还尝试过$2::interval(我不确定是否合法),但它不起作用。

因此,我相信它可能与在 param 查询中表达间隔有关,或者与我正在使用的模块有关。关于导致问题的原因以及如何做这类事情的任何想法都将不胜感激。或者可能可以缩小问题不在于 pg 而在于模块,然后我必须在其他地方解决它。

Postgres 版本:10.x

我使用的模块是 pgo(用于 Erlang 编程语言)https://github.com/SpaceTime-IoT/pgo。我得到的错误消息(当我传递"2 days""'2 days'"作为查询参数)看起来像:

{error,{pgsql_error,#{code => <<"08P01">>,file => <<"pqformat.c">>,
                          line => <<"575">>,
                          message => <<"insufficient data left in message">>,
                          routine => <<"pq_copymsgbytes">>,severity => <<"ERROR">>,
                          {unknown,86} => <<"ERROR">>}}}
Run Code Online (Sandbox Code Playgroud)

当我'2 days'作为参数传递时,它会引发badarg错误。

Erw*_*ter 6

TLDR:跳到下面的“高级查询”一章。

您没有透露您正在使用的模块,但问题显然是类型转换之一。看起来您的参数是作为类型值传递的,我假设textor varchartext->没有隐式类型转换interval

SELECT castsource::regtype, casttarget::regtype, castcontext
FROM   pg_cast
WHERE  casttarget = 'interval'::regtype;
Run Code Online (Sandbox Code Playgroud)
铸源| 投靶| 演员表
:--------------------- | :--------- | :----------
没有时区的时间| 间隔| 一世          
间隔| 间隔| 一世          

db<>在这里摆弄

如果我的假设是正确的,您应该会看到如下错误消息:

ERROR:  operator does not exist: interval < text
Run Code Online (Sandbox Code Playgroud)

我非常有信心您未公开的模块有方法传递不同的数据类型或无类型的字符串文字。Postgres 确实提供了这个功能。

您还声明:

我也试过 $2::interval

这很奇怪,因为显式类型转换也应该有效。

演示

-- interval typed value
SELECT current_timestamp - timestamp '2018-05-04 18:40' < interval '6 days'; -- works
Run Code Online (Sandbox Code Playgroud)
| ?柱子?|
| :------- |
| f |
--untyped string literal
SELECT current_timestamp - timestamp '2018-05-04 18:40' < '6 days'; -- works
Run Code Online (Sandbox Code Playgroud)
| ?柱子?|
| :------- |
| f |
-- text typed value
SELECT current_timestamp - timestamp '2018-05-04 18:40' < text '6 days'; -- fails!
Run Code Online (Sandbox Code Playgroud)
错误:运算符不存在:间隔 < 文本

第 2 行:... current_timestamp - timestamp '2018-05-04 18:40' < text '6 ... ^ 提示:没有运算符匹配给定的名称和参数类型。您可能需要添加显式类型转换。

-- text typed value, with explicit cast
SELECT current_timestamp - timestamp '2018-05-04 18:40' < ('6 days'::text::interval); -- works
Run Code Online (Sandbox Code Playgroud)
| ?柱子?|
| :------- |
| f |

准备好的语句也是如此:

PREPARE fooplan_typed_interval(interval) AS
SELECT current_timestamp - timestamp '2018-05-04 18:40' < $1;

EXECUTE fooplan_typed_interval('6 days');
Run Code Online (Sandbox Code Playgroud)
| ?柱子?|
| :------- |
| f |
PREPARE fooplan_untyped AS
SELECT current_timestamp - timestamp '2018-05-04 18:40' < $1;

EXECUTE fooplan_untyped('6 days');
Run Code Online (Sandbox Code Playgroud)
| ?柱子?|
| :------- |
| f |
PREPARE fooplan_typed_text(text) AS
SELECT current_timestamp - timestamp '2018-05-04 18:40' < $1;

EXECUTE fooplan_typed_text('6 days');  -- only this one fails!
Run Code Online (Sandbox Code Playgroud)
错误:运算符不存在:间隔 < 文本

第 3 行:...ELECT current_timestamp - 时间戳 '2018-05-04 18:40' < $1; ^ 提示:没有运算符匹配给定的名称和参数类型。您可能需要添加显式类型转换。

PREPARE fooplan_typed_text(text) AS
SELECT current_timestamp - timestamp '2018-05-04 18:40' < $1::interval;

EXECUTE fooplan_typed_text('6 days');
Run Code Online (Sandbox Code Playgroud)
| ?柱子?|
| :------- |
| f |

db<>在这里摆弄

高级查询

除此之外,您的任何查询都不能使用索引(不是“sargable”)。 改用这样的东西!

server_pg_module:query("select name from new_table
where creation_date > localtimestamp - interval '1 day' * $1", [6])
Run Code Online (Sandbox Code Playgroud)

您可以将interval与相乘integer
或者,如果您发现传递类型参数的问题:

server_pg_module:query("select name from new_table
where creation_date > localtimestamp - $1::interval", ['6 days'])
Run Code Online (Sandbox Code Playgroud)

采用localtimestamp指出,“当前时间”取决于与类型当前时区设置timestamp。细节:

  • 嗨欧文,你建议的“高级查询”就像一个魅力。你关于“可交易性”的观点很关键,但从来不知道这个词。非常感谢您的详细回答,特别是对于 sargable 位。我的查询可能会破坏性能。 (2认同)