Postgres在查询结果应为空时返回记录

huangapple go评论85阅读模式
英文:

Postgres returning records when query result should be empty

问题

  1. 假设以下内容,
  2. ```sql
  3. CREATE SCHEMA IF NOT EXISTS my_schema;
  4. CREATE TABLE IF NOT EXISTS my_schema.user (
  5. id serial PRIMARY KEY,
  6. chat_ids BIGINT[] NOT NULL
  7. );
  8. CREATE TABLE IF NOT EXISTS my_schema.chat (
  9. id serial PRIMARY KEY,
  10. chat_id_value BIGINT UNIQUE NOT NULL
  11. );
  12. INSERT INTO my_schema.chat VALUES
  13. (1, 12321);
  14. INSERT INTO my_schema.user VALUES
  15. (1, '{12321}');

当我查询一个不存在的聊天记录的用户时,我仍然收到结果:

  1. SELECT u.id,
  2. (
  3. SELECT TO_JSON(COALESCE(ARRAY_AGG(c.*) FILTER (WHERE c IS NOT NULL), '{}'))
  4. FROM my_schema.chat as c
  5. WHERE c.chat_id_value = ANY (ARRAY[ 1234 ]::int[])
  6. ) AS chat_ids
  7. FROM my_schema.user as u

显然,没有与 chat_id_value = 1234my_schema.chat 记录。

我尝试添加,

  1. . . .
  2. FROM my_schema.user as u
  3. WHERE chat_ids != '{}'

但这仍然产生相同的结果:

  1. [
  2. {
  3. "id": 1,
  4. "chat_ids": []
  5. }
  6. ]

我尝试 WHERE ARRAY_LENGTH(chat_ids, 1) != 0WHERE CARDINALITY(chat_ids) != 0,都没有返回预期的结果。

奇怪的是,WHERE ARRAY_LENGTH(chat_ids, 1) != 1 可以工作,这意味着 chat_ids 的长度为 1,而实际上它是 0?非常令人困惑。

我在这里做错了什么?预期的结果应该是 []

  1. <details>
  2. <summary>英文:</summary>
  3. Suppose the following,

CREATE SCHEMA IF NOT EXISTS my_schema;

CREATE TABLE IF NOT EXISTS my_schema.user (
id serial PRIMARY KEY,
chat_ids BIGINT[] NOT NULL
);

CREATE TABLE IF NOT EXISTS my_schema.chat (
id serial PRIMARY KEY,
chat_id_value BIGINT UNIQUE NOT NULL
);

INSERT INTO my_schema.chat VALUES
(1, 12321);

INSERT INTO my_schema.user VALUES
(1, '{12321}');

  1. When I query for a user record with a nonexisting chat, I still receive a result:

SELECT u.id,
(
SELECT TO_JSON(COALESCE(ARRAY_AGG(c.*) FILTER (WHERE c IS NOT NULL), '{}'))
FROM my_schema.chat as c
WHERE c.chat_id_value = ANY (ARRAY[ 1234 ]::int[])
) AS chat_ids
FROM my_schema.user as u

  1. Clearly, there is no `my_schema.chat` record with with `chat_id_value = 1234`.
  2. I&#39;ve tried adding,

. . .
FROM my_schema.user as u
WHERE chat_ids != '{}'

  1. But this still yields the same result:

[
{
"id": 1,
"chat_ids": []
}
]

  1. I&#39;ve tried `WHERE ARRAY_LENGTH(chat_ids, 1) != 0`, `WHERE CARDINALITY(chat_ids) != 0`, none return the expected result.
  2. Oddly enough, `WHERE ARRAY_LENGTH(chat_ids, 1) != 1` works, implying the length of `chat_ids` is `1` when it&#39;s actually `0`? Very confusing.
  3. What am I doing wrong here? The expected result should be `[]`.
  4. </details>
  5. # 答案1
  6. **得分**: 3
  7. 如果在 `my_schema.chat` 上的子查询没有返回结果,你将会得到 NULL,而 `coalesce` 会将其转换为 `{}`。此外,内部查询与外部查询没有关联,因此在 `my_schema."user"` 的每一行中都会得到相同的结果。你应该使用内连接:
  8. ```sql
  9. SELECT u.id,
  10. TO_JSON(COALESCE(ARRAY_AGG(c.*) FILTER (WHERE c IS NOT NULL), '{}'))
  11. FROM my_schema.user as u
  12. JOIN my_schema.chat as c
  13. ON c.chat_id_value = ANY (u.chat_ids);

我认为你的数据模型不太好。你应该避免使用数组,而是使用联接表。这将提供更好的性能和更简单的查询。

英文:

If the subselect on my_schema.chat returns no result, you will get NULL, which coalesce will turn into {}. Moreover, the inner query is not correlated to the outer query, so you will get the same result for each row in my_schema.&quot;user&quot;. You should use an inner join:

  1. SELECT u.id,
  2. TO_JSON(COALESCE(ARRAY_AGG(c.*) FILTER (WHERE c IS NOT NULL), &#39;{}&#39;))
  3. FROM my_schema.user as u
  4. JOIN my_schema.chat as c
  5. ON c.chat_id_value = ANY (u.chat_ids);

I don't think that your data model is good. You should avoid arrays and use a junction table instead. It will make for better performance and simpler queries.

答案2

得分: 1

你可以按以下方式操作:

  1. WITH cte as (
  2. SELECT TO_JSON(ARRAY_AGG(c.*) FILTER (WHERE c IS NOT NULL)) as to_json
  3. FROM my_schema.chat as c
  4. inner join my_schema.user u on c.chat_id_value = ANY (u.chat_ids)
  5. WHERE c.chat_id_value = ANY (ARRAY[ 12321]::int[])
  6. )
  7. select *
  8. from cte where to_json is not null;

这将在查询不匹配时强制不显示任何结果!

演示在这里

英文:

You can do it as follows :

  1. WITH cte as (
  2. SELECT TO_JSON(ARRAY_AGG(c.*) FILTER (WHERE c IS NOT NULL)) as to_json
  3. FROM my_schema.chat as c
  4. inner join my_schema.user u on c.chat_id_value = ANY (u.chat_ids)
  5. WHERE c.chat_id_value = ANY (ARRAY[ 12321]::int[])
  6. )
  7. select *
  8. from cte where to_json is not null;

This will force not to show any result if the query don't match !

Demo here

huangapple
  • 本文由 发表于 2023年2月6日 20:46:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/75361491.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定