Old*_*ool 6 postgresql syntax json
我正在努力从我的 PostgreSQL 数据库中的 JSON 列中获取数据。在我们的users
表中,我们有一known_ips
列,它是一个 JSON 列,保存给定用户已知的 IP 地址的平面数组,如下所示:
# select email, known_ips from users limit 3;
email | known_ips
-------------------+-------------------------------
user1@example.com | ["192.168.1.1","192.168.1.2"]
user2@example.com | ["192.168.1.3"]
user3@example.com | ["192.168.1.2"]
(3 rows)
Run Code Online (Sandbox Code Playgroud)
我想要做的是从其列中的一组 IP 地址(在本例中为 192.168.1.1 或 192.168.1.2)中选择具有 IP 地址的所有用户known_ips
。因此,在这种情况下,user1 和 user3 的 IP 地址为 .1 或 .2,因此我希望返回它们,而不是 user2,因为我不是在寻找 .3 地址。
我尝试了以下查询,但都无济于事:
database=# select email, ip from users, json_array_elements(known_ips) as ip where ip in("192.168.1.1","192.168.1.2");
ERROR: column "192.168.1.1" does not exist
LINE 1: ... json_array_elements(known_ips) as ip where ip in("192.168.1...
^
Run Code Online (Sandbox Code Playgroud)database=# select email, ip from users, json_array_elements(known_ips) as ip where ip in('192.168.1.1','192.168.1.2') limit 5;
ERROR: invalid input syntax for type json
LINE 1: ... json_array_elements(known_ips) as ip where ip in('192.168.1...
^
DETAIL: Token "." is invalid.
CONTEXT: JSON data, line 1: 192.168....
Run Code Online (Sandbox Code Playgroud)database=# select email, ip from users, json_array_elements(known_ips) as ip where ip in(192.168.1.1,192.168.1.2) limit 5;
ERROR: syntax error at or near ".168"
LINE 1: ...array_elements(known_ips) as ip where ip in(192.168.1.1,192...
^
Run Code Online (Sandbox Code Playgroud)database=# select email, ip from users, json_array_elements(known_ips) as ip where ip in("192.168.1.1","192.168.1.2");
ERROR: column "192.168.1.1" does not exist
LINE 1: ... json_array_elements(known_ips) as ip where ip in("192.168.1...
^
Run Code Online (Sandbox Code Playgroud)database=# select email, ip from users, json_array_elements(known_ips) as ip where ip in(array["192.168.1.1","192.168.1.2"]);
ERROR: column "192.168.1.1" does not exist
LINE 1: ...array_elements(known_ips) as ip where ip in(array["192.168.1...
^
Run Code Online (Sandbox Code Playgroud)database=# select email, ip from users, json_array_elements(known_ips) as ip where ip = any(array["192.168.1.1","192.168.1.2"]);
ERROR: column "192.168.1.1" does not exist
LINE 1: ...ay_elements(known_ips) as ip where ip = any(array["192.168.1...
^
Run Code Online (Sandbox Code Playgroud)database=# select email, ip from users, json_array_elements(known_ips) as ip where ip in(unnest(array["192.168.1.1","192.168.1.2"]));
ERROR: column "192.168.1.1" does not exist
LINE 1: ...lements(known_ips) as ip where ip in(unnest(array["192.168.1...
^
Run Code Online (Sandbox Code Playgroud)database=# select email, ip from users, json_array_elements(known_ips) as ip where ip = any(array["192.168.1.1","192.168.1.2"]);
ERROR: column "192.168.1.1" does not exist
LINE 1: ...ay_elements(known_ips) as ip where ip = any(array["192.168.1...
^
Run Code Online (Sandbox Code Playgroud)我觉得我错过了一些明显的东西,但我只是没有看到。为什么 Postgres 认为我定义的是列而不是字符串?如何正确获取数据?
双引号是名称分隔符。它们被保留用于分隔包含非标准字符的名称(列名、表名等)或那些您想明确区分大小写的名称(因为这是它们在 PostgreSQL 中的效果,这也符合标准) .
所以,这就是为什么所有尝试使用 "192.168.1.1"
失败的原因:PostgreSQL 确实将它们解释为名称(特别是每个上下文中的列名称)。
没有引号的一种情况仅因为192.168.1.1
无效的标记序列而失败。数字和其他一些常量可以在 PostgreSQL 中表示而不用引号,但您在那里指定的标记不能解释为数字或其他任何东西。
最后,在 IP 周围使用单引号的方法失败了,因为 PostgreSQL 试图将它们解释为 JSON 文字。为什么?因为该ip
列属于类型json
– 即由 返回的列值的类型json_array_elements
。
因此,为了使您的第二次尝试成功,您应该首先将 IP 表示为有效的 JSON 字符串项。这意味着您需要将它们用双引号括起来,然后用单引号括起来,如下所示:
where ip in ('"192.168.1.1"','"192.168.1.2"')
Run Code Online (Sandbox Code Playgroud)
但是,这会给你这个错误:
运算符不存在:json = json
您的选择是:
转换ip
为text
:
where ip::text in ('"192.168.1.1"','"192.168.1.2"')
Run Code Online (Sandbox Code Playgroud)转换ip
为jsonb
where ip::jsonb in ('"192.168.1.1"','"192.168.1.2"')
Run Code Online (Sandbox Code Playgroud)要么应该让你去。
但请注意,像这样过滤数据可能会导致输出重复。问题是,该json_array_elements
函数将指定json
值转换为行集,为每个转置项重复源行的列。因此,对于您的示例,FROM 子句有效地生成以下行集:
email | known_ips | ip
-------------------+-------------------------------+---------------
user1@example.com | ["192.168.1.1","192.168.1.2"] | "192.168.1.1"
user1@example.com | ["192.168.1.1","192.168.1.2"] | "192.168.1.2"
user2@example.com | ["192.168.1.3"] | "192.168.1.3"
user3@example.com | ["192.168.1.2"] | "192.168.1.2"
Run Code Online (Sandbox Code Playgroud)
因为对于 user1 每个 ip
将匹配 IN 谓词,因此您将收到两次返回的相应电子邮件。
要解决这个问题,而不是这样:
...
from users, json_array_elements(known_ips) as ip
where ip::jsonb in ('"192.168.1.1"','"192.168.1.2"')
Run Code Online (Sandbox Code Playgroud)
你可以这样做:
...
from users
where exists
(
select *
from json_array_elements(known_ips) as ip
where ip::jsonb in ('"192.168.1.1"','"192.168.1.2"')
)
Run Code Online (Sandbox Code Playgroud)