【问题标题】:SQLcombine continuous recordsSQLcombine 连续记录
【发布时间】:2014-09-18 13:38:59
【问题描述】:

我有一个数据库表,用于根据用户“版本”对输入/输出结果进行版本控制。我需要结合源和目标匹配的位置,以及在停用记录之后直接出现激活记录的位置。

我目前拥有的:

ID     Source   Target  Activate    Deactivate
361440  1760     2569    1          78
532741  1760     2569    79         80
532742  1760     2569    81         84
574687  1760     2569    95         97
574687  1760     2569    98         NULL

我想要什么:

ID     Source   Target  Activate    Deactivate
361440  1760     2569    1          84
574687  1760     2569    95         NULL

编辑:我的示例仅包含 1 个附加记录的连续链,在某些情况下,该链存在多个记录。也有没有设置Deactivation版本的情况。我已经更新了我的示例以反映这一点。 谢谢

【问题讨论】:

  • 表格是否包含大量数据?
  • 是的,该表包含大约 125,000 条记录。但是,这个查询只会运行一次,而不是应用程序的一部分。运行所需的时间并不重要。

标签: mysql sql sql-server-2008 plsql


【解决方案1】:

这适用于您提供的数据样本:

SELECT 
  v1.ID, 
  v1.source, 
  v1.target, 
  v1.activate, 
  COALESCE(v2.Deactivate, v1.Deactivate) as Deactivate  
FROM Versions v1 LEFT JOIN 
   Versions v2 ON v1.source = v2.source
              and v1.target = v2. target 
              and v1.Deactivate + 1 = v2.Activate

但是,如果您有多个记录的连续链,您需要决定要做什么。

【讨论】:

  • 感谢您的快速回复!在我的例子中,我没有提到一些连续的记录是一个系列而不是一个链。还有一些情况是尚未设置停用版本,使其无限期激活。我已经更新了我的示例以反映这一点。
【解决方案2】:

PL/SQL 函数是否可接受?这增加了一些样板,因为我需要创建两个额外的类型,但是一旦完成,这只是一个循环 CURSOR 的问题。

CREATE OR REPLACE TYPE merge_items_rec AS OBJECT (
  ID NUMBER(6),
  SOURCE NUMBER(6),
  TARGET NUMBER(6),
  ACTIVATE NUMBER(4),
  DEACTIVATE NUMBER(4)
);

CREATE OR REPLACE TYPE merge_items_tbl AS TABLE OF  merge_items_rec;

这里我使用PIPELINED function 来正确处理非常大的数据集。我不知道在你的情况下这是否必要。但我觉得这是一种干净的做事方式:

CREATE OR REPLACE FUNCTION merge_items
RETURN merge_items_tbl PIPELINED
AS
  -- assuming your data table is called MYTABLE
  CURSOR data IS SELECT * FROM MYTABLE ORDER BY SOURCE,TARGET,ACTIVATE;
  acc  MYTABLE%ROWTYPE;
  curr MYTABLE%ROWTYPE;
BEGIN
  OPEN data;
  FETCH data INTO acc;

  IF data%FOUND THEN
  LOOP
    FETCH data INTO curr;

    -- emit a row when there is not more row to fetch,
    -- OR if the newly fetched row does not coalesce with the one stored in `acc`
    IF data%NOTFOUND OR acc.source != curr.source 
                     OR acc.target != curr.target 
                     OR acc.deactivate + 1 != curr.activate THEN
      PIPE ROW(merge_items_rec(acc.id, acc.source,acc.target,acc.activate,acc.deactivate));
      EXIT WHEN data%NOTFOUND;
      acc := curr;   
    ELSE
      acc.deactivate := curr.deactivate;
    END IF;
  END LOOP;
  END IF;

  CLOSE data;
END;

用法:

SELECT * FROM TABLE(merge_items())

制作:

ID      SOURCE  TARGET  ACTIVATE    DEACTIVATE
361440  1760    2569    1           84
574687  1760    2569    95          - 

【讨论】:

    【解决方案3】:

    完全不同的方法可能使用 Oracle CONNECT BY hierarchical queries。这里的想法是从“连续”行(根据激活/停用)构建路径:

    SELECT CONNECT_BY_ROOT A.id AS ROOTID,
           A.SOURCE,
           A.TARGET, 
           CONNECT_BY_ROOT A.activate AS ACTIVATE, 
           A.DEACTIVATE
        FROM MYTABLE A 
    
        --    Keep only path ending on a leaf node (i.e.: no following range)
        WHERE CONNECT_BY_ISLEAF <> 0
    
        --    Make a path by chaining consecutive rows
        CONNECT BY PRIOR A.deactivate+1 = A.activate
               AND PRIOR A.source = source
               AND PRIOR A.target = target
    
        --    Keep only path starting on a root node (i.e.: no previous range)
        START WITH NOT EXISTS (SELECT 1 FROM MYTABLE B WHERE B.deactivate+1 = A.activate)
    

    制作:

    ROOTID  SOURCE  TARGET  ACTIVATE    DEACTIVATE
    361440  1760    2569    1           84
    574687  1760    2569    95          -
    

    【讨论】:

      猜你喜欢
      • 2017-05-20
      • 2017-08-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多