【问题标题】:postgresql where condition returns at least one resultpostgresql where 条件返回至少一个结果
【发布时间】:2012-01-22 14:43:13
【问题描述】:

postgreSQL 问题...我在下面有一个更新查询,它使用子查询的结果更新列,但是在某些情况下,子查询将返回 null,这会在列上抛出“非 null”约束,我怎样才能得到如果子查询返回 null,它不会更新?

我尝试过 EXISTS,但这似乎只适用于 WHERE 子句?

UPDATE user_stats as stats
SET ave_price = (
    SELECT AVG(l.price)
    FROM lengths as l, user_sessions as us
    WHERE l.product_type = 'car'
    AND l.session_id = us.session_id
    AND stats.user_id = us.user_id
)

【问题讨论】:

  • 对这个问题很重要:PostgreSQL的版本号?
  • 请发布您的表定义(至少是相关列和 PK/FK 约束),如果可能,请提供一个小脚本来使用测试数据填充表。
  • @ErwinBrandstetter:我很想知道您正在寻找哪个功能。是 CTE,因此最低 PostgreSQL 8.4?
  • @tscho:没错。但是,仅在 PostgreSQL 9.1 中引入了用于数据修改命令的 CTE。 Allow data-modification commands (INSERT/UPDATE/DELETE) in WITH clauses
  • @ErwinBrandstetter:感谢您指出这一点!

标签: sql postgresql


【解决方案1】:

使用实际的子查询而不是子查询表达式进行选择:

UPDATE user_stats s
SET    ave_price = x.ave_price
FROM  (
    SELECT user_id
          ,avg(l.price) AS ave_price
    FROM   lengths l
    JOIN   user_sessions us ON us.session_id = l.session_id
    WHERE  l.product_type = 'car'
    GROUP  BY us.user_id
    HAVING avg(l.price) IS NOT NULL
    ) x
WHERE x.user_id = s.user_id;

这也会更快。 如果您在表user_sessions 中存在user_id 的相关比例,但在user_stats 中不存在,那么以下查询可能会更快(虽然在每种情况下都产生相同的结果):

UPDATE user_stats s
SET    ave_price = x.ave_price
FROM  (
    SELECT user_id
          ,avg(l.price) AS ave_price
    FROM   lengths l
    JOIN   user_stats usr USING (user_id)
    JOIN   user_sessions us ON us.session_id = l.session_id
    WHERE  l.product_type = 'car'
    GROUP  BY us.user_id
    HAVING avg(l.price) IS NOT NULL
    ) x
WHERE x.user_id = s.user_id;

第二个版本的重点是尽早排除不相关的行。 用 CTE 编写的相同查询(更优雅和可读):

WITH x AS (
    SELECT user_id
          ,avg(l.price) AS ave_price
    FROM   lengths l
    JOIN   user_stats usr USING (user_id)
    JOIN   user_sessions us ON us.session_id = l.session_id
    WHERE  l.product_type = 'car'
    GROUP  BY us.user_id
    HAVING avg(l.price) IS NOT NULL
    )
UPDATE user_stats s
SET    ave_price = x.ave_price
FROM   x
WHERE  x.user_id = s.user_id;

请注意,虽然 PostgreSQL 8.4 引入了用于 SELECT 查询的 CTE,但用于数据修改命令的 CTE 仅为 introduced with PostgreSQL 9.1

在 WITH 子句中允许数据修改命令(INSERT/UPDATE/DELETE)

【讨论】:

  • @tscho:谢谢!我修改了我的答案以澄清这一点。
  • 好的,谢谢!我删除了我的原始评论,建议将 8.4 作为最低版本,因为我无法将其编辑为 9.1。
【解决方案2】:

coalesce、nvl、ifnull 在大多数 db 引擎中都会执行一个条件语句,表示在这种情况下,当子选择返回 null 时,它会将 ave_price = 设置为自身。

UPDATE user_stats as stats
SET ave_price = coalesce((
    SELECT AVG(l.price)
    FROM lengths as l, user_sessions as us
    WHERE l.product_type = 'car'
    AND l.session_id = us.session_id
    AND stats.user_id = us.user_id
),ave_price)

这不会阻止请求的更新,但它对数据有类似的影响。

有关合并的更多信息,请参阅:PostgreSQL

要真正阻止更新,您需要在更新上添加 where 子句并重新执行子查询,例如:

    UPDATE user_stats as stats
    SET ave_price = (
        SELECT AVG(l.price)
        FROM lengths as l, user_sessions as us
        WHERE l.product_type = 'car'
        AND l.session_id = us.session_id
        AND stats.user_id = us.user_id)
WHERE (SELECT AVG(l.price)
        FROM lengths as l, user_sessions as us
        WHERE l.product_type = 'car'
        AND l.session_id = us.session_id
        AND stats.user_id = us.user_id) is not null

逻辑上执行子查询两次会影响性能两次;而合并只需要执行一次。做事总是有多种方法,根据要求,必须选择最适合他们的选项。

【讨论】:

  • 谢谢,coalesce 解决方案可以正常工作(不过执行需要将近 1 分钟!)
  • @DaveB:带有coalesce 的第一个版本不是一个好的解决方案。导致许多无意义的更新,花费更长的时间,在您的表中留下许多额外的死元组,并且可能会触发不应触发的 ON UPDATE 触发器。第二个版本更好(虽然不必要地慢)。有更好的方法可以做到这一点(这让我发布了另一个答案)。
  • 是的,有很多方法可以完成我指出的两个相同的任务,DaveB 提供的选项是第三个更好的解决方案
猜你喜欢
  • 2012-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-08
  • 2021-12-23
  • 2016-11-01
  • 2021-12-24
  • 1970-01-01
相关资源
最近更新 更多