【问题标题】:How can I find out which rows have been deleted?如何找出哪些行已被删除?
【发布时间】:2011-03-30 06:09:14
【问题描述】:

带有 SE 引擎的 Informix-SQL 7.32:

我有一位客户从 SE 表中删除了几行。 (我没有使用事务日志记录或审计)。该表有一个序列列。我想创建一个 Ace 报告来打印缺少的序列列。我尝试了以下快速而肮脏的报告,但没有成功!..您能提出更好的方法吗?

define
variable next_id integer
end

  select tbl_id 
    from tbl
order by tbl_id {I'm ordering tbl_id because all the rows are periodically re-clustered}
     end        {by an fk_id in order to group all rows belonging to the same customer}

format
on every row
let next_id = tbl_id + 1  

after group of tbl_id
if tbl_id + 1 <> next_id then
print column 1, tbl_id + 1 using "######"

end

或者可能创建一个临时表,其中包含一个 INT 列,其中包含从 1 到 5000 的序号,并执行如下选择语句:

   SELECT tbl_id 
     FROM tbl
    WHERE tbl_id NOT IN
                 (SELECT tmp_int
                    FROM tmp);

或带有 HAVING、OUTER 等的 select 语句

【问题讨论】:

    标签: sql informix auto-increment


    【解决方案1】:

    由于这是 SE,我们必须使用老式表示法,而不是 SQL-92 JOIN 表示法。

    以下四个查询是两个可能答案的共同基础:

    SELECT t1.tbl_id AS tbl_id, t2.tbl_id AS ind
      FROM tbl AS t1, OUTER tbl AS t2
     WHERE t1.tbl_id + 1 = t2.tbl_id
      INTO TEMP x1;
    
    SELECT t1.tbl_id AS tbl_id, t2.tbl_id AS ind
      FROM tbl AS t1, OUTER tbl AS t2
     WHERE t1.tbl_id - 1 = t2.tbl_id
      INTO TEMP x2;
    
    SELECT tbl_id AS hi_range
      FROM x1
     WHERE ind IS NULL
      INTO TEMP x3;
    
    SELECT tbl_id AS lo_range
      FROM x2
     WHERE ind IS NULL
      INTO TEMP x4;
    

    表 x3 和 x4 现在(分别)包含 tbl_id 的值,它们没有直接后继和直接前任。每个值都是 tbl_id 值的连续范围的开始或结束。在 IDS 而不是 SE 中,您可以使用标准 SQL OUTER JOIN 表示法并在两个查询而不是四个查询中过滤连接结果;你在 SE 没有那么奢侈。

    具有二次(或更差)行为的非解

    现在你只需要弄清楚如何组合这两个表:

    SELECT t1.lo_range, t2.hi_range
      FROM x4 AS t1, x3 AS t2
     WHERE t1.lo_range <= t2.hi_range
       AND NOT EXISTS
           (SELECT t3.lo_range, t4.hi_range
              FROM x4 AS t3, x3 AS t4
             WHERE t3.lo_range <= t4.hi_range
               AND t1.lo_range =  t3.lo_range
               AND t2.hi_range >  t4.hi_range
           );
    

    此查询的主要部分出现两次,并生成范围开始小于或等于范围结束的所有行对(equal 允许 'ranges' 由一个单独的值组成,删除了两边的行)。 NOT EXISTS 子句确保没有其他对具有相同的起始值和较小的结束值。

    如果数据有很多间隙,对临时表的查询可能不会很快;如果差距很少,那么应该没问题。

    最后一个查询在范围数方面表现出二次行为。当我只有十几个范围时,这很好(亚秒​​级响应时间);当我有 1,200 个范围时,这并不好 - 没有在合理的时间内完成。

    避免二次行为

    既然二次行为不好,我们如何改写查询...

    对于范围的每个低端,找到大于或等于低端的范围的最小高端,或者在 SQL 中:

    SELECT t1.lo_range, MIN(t2.hi_range) AS hi_range
      FROM x4 AS t1, x3 AS t2
     WHERE t2.hi_range >= t1.lo_range
     GROUP BY t1.lo_range;
    

    请注意,这可以很容易地合并到 ACE 报告中。它为您提供了存在的数字范围 - 而不是那些不存在的数字。你可以弄清楚如何生成另一个。

    时间

    在包含 1200 个数据间隙的 22100 行的表上表现得非常好。在其基准模式 (-B) 中使用(我的)SQLCMD 程序,并将 SELECT 输出发送到 /dev/null,并使用 IDS 11.70.FC1 在 MacOS X 10.6.7(MacBook Pro、Intel Core 2 Duo at 3 GHz 和4 GB RAM),结果是:

    $ sqlcmd -d stores -B -f gaps.sql
    + CLOCK START;
    2011-03-31 18:44:39
    + BEGIN;
    Time: 0.000588
    2011-03-31 18:44:39
    + SELECT t1.tbl_id AS tbl_id, t2.tbl_id AS ind
      FROM tbl AS t1, OUTER tbl AS t2
     WHERE t1.tbl_id + 1 = t2.tbl_id
      INTO TEMP x1;
    Time: 0.437521
    2011-03-31 18:44:39
    + SELECT t1.tbl_id AS tbl_id, t2.tbl_id AS ind
       FROM tbl AS t1, OUTER tbl AS t2
      WHERE t1.tbl_id - 1 = t2.tbl_id
       INTO TEMP x2;
    Time: 0.315050
    2011-03-31 18:44:39
    + SELECT tbl_id AS hi_range
      FROM x1
     WHERE ind IS NULL
      INTO TEMP x3;
    Time: 0.012510
    2011-03-31 18:44:39
    + SELECT tbl_id AS lo_range
      FROM x2
     WHERE ind IS NULL
      INTO TEMP x4;
    Time: 0.008754
    + output "/dev/null";
    2011-03-31 18:44:39
    + SELECT t1.lo_range, MIN(t2.hi_range) AS hi_range
      FROM x4 AS t1, x3 AS t2
     WHERE t2.hi_range >= t1.lo_range
     GROUP BY t1.lo_range;
    Time: 0.561935
    + output "/dev/stdout";
    2011-03-31 18:44:40
    + SELECT COUNT(*) FROM x1;
    22100
    Time: 0.001171
    2011-03-31 18:44:40
    + SELECT COUNT(*) FROM x2;
    22100
    Time: 0.000685
    2011-03-31 18:44:40
    + SELECT COUNT(*) FROM x3;
    1200
    Time: 0.000590
    2011-03-31 18:44:40
    + SELECT COUNT(*) FROM x4;
    1200
    Time: 0.000768
    2011-03-31 18:44:40
    + SELECT t1.lo_range, MIN(t2.hi_range) AS hi_range
      FROM x4 AS t1, x3 AS t2
     WHERE t2.hi_range >= t1.lo_range
     GROUP BY t1.lo_range
     INTO TEMP x5;
    Time: 0.529420
    2011-03-31 18:44:40
    + SELECT COUNT(*) FROM x5;
    1200
    Time: 0.001155
    2011-03-31 18:44:40
    + ROLLBACK;
    Time: 0.329379
    + CLOCK STOP;
    Time: 2.202523
    $ 
    

    会的;处理时间不到几秒钟。

    【讨论】:

    • 哇,您的方法看起来像使用外部进行自联接。是否有更简单的方法来实现所需的结果?...有没有一种方法可以创建一个 NOT IN(子查询)为给定范围 1:5000 自动生成序列号的 select 语句函数?或'CREATE TABLE seq_tbl(num INT)',将所需的序列加载到其中,然后:SELECT seq_tbl.num, tbl.tbl_id FROM OUTER tbl, seq_tbl WHERE seq_tbl.num = tbl.tbl_id ??? (请记住,我需要将一个有效的 select 语句合并到 Ace 报告中)。
    • @Jonathan:我看到你已经添加了基准测试结果。您是否曾经需要在连续列中查找间隙?我选择创建一个单独的表,其中包含我选择的整数序列。 ace 报告创建加载文件,然后加载序列表。 select 语句简单且成本低廉,可用于查找差距。 SELECT element FROM sequence WHERE element NOT IN (SELECT pk_id FROM target_tbl);有没有办法选择或取消删除具有 \0 标志的行?我没有使用事务日志记录或审计。
    • @Frank:没有办法使用 C-ISAM 取消删除带有 '\0' 标志的行,更不用说 SE。您始终可以准手动进行;之后您需要运行bchecksecheck 来重新生成索引。不要忘记在开始操作文件之前制作文件的备份副本。
    • @Jonathan: "quasi-manually" 怎么样?.. 使用 .DAT 上的十六进制编辑器删除 \0?.. 关于另一个主题,您是否同意它更简单、更多有效地填充具有所需范围的系列整数的新表并执行 SELECT where series NOT IN target table?
    • @Frank:关于 SELECT 问题 - 如果您愿意使用诸如存储过程之类的东西来填充表,那么它可能会更容易。您可以轻松确定所需范围的最小值和最大值。
    【解决方案2】:

    请参阅:Is there an SQL function which generates a given range of sequential numbers?,了解更简单且引擎效率更高的解决方案。

    【讨论】:

      猜你喜欢
      • 2012-02-14
      • 2013-07-24
      • 2019-10-28
      • 2012-02-11
      • 1970-01-01
      • 1970-01-01
      • 2020-12-22
      • 2021-01-09
      • 2018-03-21
      相关资源
      最近更新 更多