协助复杂的SQL UPDATE语句

时间:2014-08-29 21:48:33

标签: sql postgresql

我有一个数据库表,我需要在其上运行UPDATE语句。该表有大约250,000条记录,因此优化的性能很重要。

以下是相关的数据库架构和一些示例数据。

audit_logs
-- id -- ticket_id -- ip_address      -- created_at                 --
-- 10 -- 100       -- 100.101.102.103 -- 2014-08-22 11:17:28.325844 --
-- 11 -- 100       -- 100.101.102.103 -- 2014-08-23 12:18:28.325844 --
-- 12 -- 101       -- 200.1.2.3       -- 2014-08-24 13:19:28.325844 --
-- 13 -- 101       -- 201.2.3.4       -- 2014-08-25 14:20:28.325844 --
-- 14  - 101       -- 202.3.4.5       -- 2014-08-26 15:21:28.325844 --
-- 15  - 102       -- 102.12.34.56    -- 2014-08-27 16:22:28.325844 --

这是我需要做的。对于具有多于1个IP地址的记录的任何ticket_id,我需要为除第一个以外的每个IP地址将ticket_id设置为NULL。以下是上述数据的示例结果。

audit_logs
-- id -- ticket_id -- ip_address      -- created_at                 --
-- 10 -- 100       -- 100.101.102.103 -- 2014-08-22 11:17:28.325844 --
-- 11 -- 100       -- 100.101.102.103 -- 2014-08-23 12:18:28.325844 --
-- 12 -- 101       -- 200.1.2.3       -- 2014-08-24 13:19:28.325844 --
-- 13 -- NULL      -- 201.2.3.4       -- 2014-08-25 14:20:28.325844 --
-- 14  - NULL      -- 202.3.4.5       -- 2014-08-26 15:21:28.325844 --
-- 15  - 102       -- 102.12.34.56    -- 2014-08-27 16:22:28.325844 --

因此,可以有多个具有相同票证的记录。但是,如果有一个以上的IP地址与一个票证相关联,那么每个记录的IP地址都不是第一个看到的IP地址。

我使用的实际RDBMS是Postgres。最简单的方法是什么?谢谢大家。

2 个答案:

答案 0 :(得分:1)

不幸的是Postgres还不支持count(distinct ..)作为窗口函数,所以它比必要的复杂一点:

update audit_logs
  set ticket_id = null
from (
  select a.id, 
         a.ticket_id,
         row_number() over (partition by a.ticket_id order by a.created_at) as rn
  from audit_logs a
  join  (
    select ticket_id
    from audit_logs
    group by ticket_id
    having count(distinct ip_address) > 1
  ) t on t.ticket_id = a.ticket_id
) x
where x.id = audit_logs.id
and x.rn > 1;

SQLFiddle:http://sqlfiddle.com/#!15/a632c/1

最里面的选择(别名t)获得具有多个IP地址的所有ticket_id。然后将其与基表连接以计算行数。然后在update中使用该结果来查找应更新的行。

不确定性能,它在很大程度上取决于最内部选择将返回的行数。

答案 1 :(得分:0)

尝试这样的事情开始,稍后进行优化

UPDATE audit_logs O
SET ticket_id = NULL
WHERE (
    SELECT count(distinct ip_address)
    FROM audit_logs I WHERE I.ticket_id = O.ticket_id
) > 1 AND id <> (
    SELECT DISTINCT ON (id) id
    FROM audit_logs I WHERE I.ticket_id = O.ticket_id
) AND ip_address <> (
    SELECT DISTINCT ON (ip_address) ip_address
    FROM audit_logs I WHERE I.ticket_id = O.ticket_id
)

P.S。我的PostgreSQL很生疏,所以语法可能有些偏差......我的家用机器上安装了PostgreSQL来检查,对不起。