【问题标题】:SQL - Efficient versioning of DNS recordsSQL - DNS 记录的高效版本控制
【发布时间】:2014-06-05 14:39:25
【问题描述】:

到目前为止,我已经提出了需要进一步完善的解决方案(非常感谢 freenode 上的#postgresql)。

我试图克服的问题是一种有效的方式来存储 DNS 记录,同时保持某种历史记录。我目前遇到的问题是 wCTE 插入新记录并正确删除旧记录。然而,它不是阅读记录。 wCTE 是:

 WITH deltas AS (
     SELECT o, n FROM (
         SELECT id, name, domain_id, class_id, addr FROM record WHERE tld_id = $1
     ) AS o FULL OUTER JOIN record_temp n
     ON (
         o.name = n.name AND 
         o.domain_id = n.domain_id AND 
         o.class_id = n.class_id AND 
         o.addr = n.addr
     )
     WHERE (o.name, o.domain_id, o.class_id, o.addr)
     IS DISTINCT FROM (n.name, n.domain_id, n.class_id, n.addr)
 ), mark_dead AS (
     UPDATE record SET alive = FALSE 
     WHERE id IN (
         SELECT (o).id FROM deltas WHERE (o).id IS NOT NULL
     ) RETURNING *
 )
 INSERT INTO record (name, domain_id, tld_id, class_id, addr)
 SELECT (n).name, (n).domain_id, (n).tld_id, (n).class_id, (n).addr
 FROM deltas WHERE
     (n).name IS NOT NULL AND 
     (n).domain_id IS NOT NULL AND 
     (n).tld_id IS NOT NULL AND 
     (n).class_id IS NOT NULL AND
     (n).addr IS NOT NULL
 ;

o 结果有所有在record_temp 中不存在的旧记录,n 有所有新的需要插入的记录。我希望我需要添加另一个连接来拉入(内部连接?)两个表上都存在的结果(如果标记为死,则需要标记为活动)。

其余供参考的架构是:

  CREATE TABLE record (
        id SERIAL,
        name VARCHAR(255),
        domain_id INT,
        tld_id INT,
        class_id INT,
        addr INET,
        alive BOOLEAN DEFAULT TRUE,
        PRIMARY KEY (id),
        CONSTRAINT fk1 FOREIGN KEY (domain_id) REFERENCES domain (id) MATCH SIMPLE,
        CONSTRAINT fk2 FOREIGN KEY (tld_id) REFERENCES tld (id) MATCH SIMPLE,
        UNIQUE(name, domain_id, class_id, addr)
  );

  CREATE TABLE record_history (
        id SERIAL,
        record_id INT,
        history_type record_history_type,
        stamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        CONSTRAINT fk1 FOREIGN KEY (record_id) REFERENCES record (id) MATCH SIMPLE,
        PRIMARY KEY(id)
  );

 CREATE TEMP TABLE record_temp (
     name VARCHAR(255),
     domain_id INT,
     tld_id INT,
     class_id INT,
     addr INET,
     UNIQUE(name, domain_id, class_id, addr)
 )
 ON COMMIT DROP;

record_history 使用函数和触发器填充,并按照我的预期填充,以下是这些触发器:

  RETURNS TRIGGER AS $$
  BEGIN
      INSERT INTO record_history (record_id, history_type) VALUES (NEW.id, 'added');
        RETURN NEW;
      END;
  $$ language 'plpgsql';

  RETURNS TRIGGER AS $$
     BEGIN
        IF NEW.alive = OLD.alive THEN
            RETURN NEW;
        END IF;
        IF NEW.alive THEN
            INSERT INTO record_history (record_id, history_type) VALUES (NEW.id, 'added');
        END IF;
        IF NOT NEW.alive THEN
            INSERT INTO record_history (record_id, history_type) VALUES (NEW.id, 'deleted');
        END IF;
        RETURN NEW;
    END;
    $$ language 'plpgsql';

    ON record FOR EACH ROW EXECUTE PROCEDURE
        add_insert_record_history();

    ON record FOR EACH ROW EXECUTE PROCEDURE
        add_update_record_history();

【问题讨论】:

  • 提供的更新命令语法错误。
  • 如果您希望此表也提供历史记录(或版本控制),至少其中一个时间戳字段应该是键的一部分。
  • 如果您希望能够跟踪所有版本历史记录,那么您需要禁止更新 SET alive = TRUE:这样的操作将需要额外的 INSERT。我的建议是通过存储过程调解对表的所有更新/插入访问,以便您可以强制执行所需的 API。
  • 我已根据我的进度更新了我的问题,但不确定是否值得提出新问题。

标签: sql postgresql rdbms common-table-expression database-administration


【解决方案1】:

我似乎通过以下查询让它按我想要的方式工作,我觉得它没有得到优化:

    WITH deltas AS (
        SELECT o, n FROM (
            SELECT id, name, domain_id, class_id, addr FROM record WHERE tld_id = $1
        ) AS o FULL OUTER JOIN record_temp n
        ON (
            o.name = n.name AND 
            o.domain_id = n.domain_id AND 
            o.class_id = n.class_id AND 
            o.addr = n.addr
        )
        WHERE (o.name, o.domain_id, o.class_id, o.addr)
        IS DISTINCT FROM (n.name, n.domain_id, n.class_id, n.addr)
    ), mark_dead AS (
        UPDATE record SET alive = FALSE 
        WHERE id IN (
            SELECT (o).id FROM deltas WHERE (o).id IS NOT NULL
        ) RETURNING *
    ), mark_alive AS (
        UPDATE record SET alive = TRUE
        WHERE alive = FALSE AND id IN (
            SELECT id FROM (
                SELECT id, name, domain_id, class_id, addr FROM record WHERE tld_id = $1
            ) AS o INNER JOIN record_temp n
            ON (
                o.name = n.name AND 
                o.domain_id = n.domain_id AND 
                o.class_id = n.class_id AND 
                o.addr = n.addr
            )
        ) RETURNING *
    )
    INSERT INTO record (name, domain_id, tld_id, class_id, addr)
    SELECT (n).name, (n).domain_id, (n).tld_id, (n).class_id, (n).addr
    FROM deltas WHERE
        (n).name IS NOT NULL AND 
        (n).domain_id IS NOT NULL AND 
        (n).tld_id IS NOT NULL AND 
        (n).class_id IS NOT NULL AND
        (n).addr IS NOT NULL
    ;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-11-16
    • 2017-08-12
    • 1970-01-01
    • 2011-02-18
    • 2010-10-01
    • 2011-02-16
    • 1970-01-01
    相关资源
    最近更新 更多