2014-08-29 21 views
0

我有一个数据库表,我需要运行UPDATE语句。这个表格有大约250,000条记录,所以优化的性能很重要。帮助处理复杂的SQL UPDATE语句

这里是相关的数据库模式和一些示例数据。

 
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地址以外的每个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。最简单的方法是什么?谢谢大家。

+0

如何确定哪一行是'第一';创建日期? – sirlark 2014-08-29 21:52:23

+0

它可能是两件事之一。给定ticket_id的最小'created_at'时间或最小的'id'值。两者订购应产生相同的结果。 – WhiteWulfTech 2014-08-29 22:02:43

+0

不,记录11的ticket_id不应该为空,因为记录10和11的IP地址值是相同的。我只想在IP地址不同时清空票据。 – WhiteWulfTech 2014-08-29 22:03:26

回答

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)获得的所有ticket_id那有多个IP地址。然后再与基表连接来计算行数。然后在update中使用该结果查找应更新的行。

不确定性能,它很大程度上取决于最内层选择将返回多少行。

+0

这不仅工作,它运行了大约6秒!在250,000个中有约140,000个记录被击中。我运行了验证查询,一切进展顺利。谢谢! – WhiteWulfTech 2014-08-29 22:18:25

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 
) 

附:我的PostgreSQL是生锈的,所以语法可能有点偏离......没有在我的家用机器上安装PostgreSQL来检查,对不起。