识别新层次结构的密钥

时间:2016-04-29 10:15:27

标签: sql hierarchical-query

假设我的来源看起来像

X1  H_ID    l1_no   l2_no   l3_no
20  1       2       12      42
20  1       6       16      42
20  1       1       19      41
20  1       5       15      41

我的查找表看起来像

X1  H_ID    l1_no   l1_key  l2_no   l2_key  l3_no   l3_key
20  1       4       104         14      114         44      144
20  1       3       103         13      113         43      143
20  1       1       101         11      111         41      141
20  1       2       102         12      112         42      142

在这种情况下,只有在我完全匹配并且将保留完整的密钥时,我的记录才会被视为更新。 如果它与任何列都不匹配,则将其视为插入。 棘手的部分甚至是插入,必须检查每个层次结构级别,并且必须保留密钥以匹配位。

例如 假设我已经有1-> 11-> 14 我验证1-> 11-> 15然后这必须是插入记录,但必须保留1和11的键,并且必须生成15

此外,如果我得到1&gt; 12-> 14,那么在这种情况下,记录将被视为插入,并且密钥必须仅保留为1,并且必须为12和14生成新密钥< / p>

我能想到的一个可能的解决方案是通过将数据集视为2D矩阵来为每个级别执行左连接。但这不是一个好方法。

WITH lookup ( x1, h_id,  l1_no, l1_key, l2_no, l2_key,l3_no,l3_key ) AS
         (SELECT 20, 1, 1, 101, 11, 111, 41, 141 FROM sys.dual UNION ALL
          SELECT 20, 1, 2, 102, 12, 112, 42, 142 FROM sys.dual UNION ALL
          SELECT 20, 1, 3, 103, 13, 113, 43, 143 FROM sys.dual UNION ALL
          SELECT 20, 1, 4, 104, 14, 114, 44, 144 FROM sys.dual),
               dwh_keys AS 
         (SELECT x1, h_id, MAX(l1_key) mk1,  MAX(l2_key) mk2,  MAX(l3_key) mk3 
          FROM  lookup GROUP BY 1,2) ,
     src ( x1, h_id, l1_no,  l2_no, l3_no ) AS
         (SELECT 20, 1, 1,  19, 41  FROM sys.dual UNION ALL
          SELECT 20, 1, 2,  12, 42  FROM sys.dual UNION ALL
          SELECT 20, 1, 5,  15, 41  FROM sys.dual UNION ALL
          SELECT 20, 1, 6,  16, 42  FROM sys.dual),
     stg1 AS
         (SELECT      b.*, 
                      CASE WHEN a.x1 IS NOT NULL THEN 'U' ELSE 'I' END AS delta_type
          FROM        src b LEFT OUTER JOIN lookup a ON a.x1 = b.x1 AND a.h_id = b.h_id 
          AND a.l1_no = b.l1_no AND a.l2_no = b.l2_no AND a.l3_no = b.l3_no)
SELECT      a.*, 
         CASE WHEN delta_type = 'I' AND b.l1_no IS NULL  THEN mk1 + ROW_NUMBER ( ) OVER (ORDER BY delta_type) ELSE b.l1_key END AS new1_key,
         CASE WHEN delta_type = 'I' AND c.l2_no IS NULL  THEN mk2 + ROW_NUMBER ( ) OVER (ORDER BY delta_type) ELSE c.l2_key END AS new1_key,
         CASE WHEN delta_type = 'I' AND d.l3_no IS NULL  THEN mk3 + ROW_NUMBER ( ) OVER (ORDER BY delta_type) ELSE d.l3_key END AS new1_key,
         delta_type
FROM            stg1 a 
LEFT OUTER JOIN lookup b ON a.x1 = b.x1 AND a.h_id = b.h_id AND a.l1_no = b.l1_no 
LEFT OUTER JOIN lookup c ON a.x1 = c.x1 AND a.h_id = c.h_id AND a.l1_no = c.l1_no AND a.l2_no = c.l2_no         
LEFT OUTER JOIN lookup d ON a.x1 = d.x1 AND a.h_id = d.h_id AND a.l1_no = d.l1_no AND a.l2_no = d.l2_no AND a.l3_no = d.l3_no 
LEFT OUTER JOIN dwh_keys xx ON a.x1 = xx.x1 AND a.h_id = xx.h_id;

我得到的输出(也是我想要的)

X1  H_ID    L1_NO   L2_NO   L3_NO   DELTA_TYPE  NEW1_KEY    NEW1_KEY    NEW1_KEY    DELTA_TYPE
20  1   6   16  42  I   105 115 145 I
20  1   5   15  41  I   106 116 146 I
20  1   1   19  41  I   101 117 147 I
20  1   2   12  42  U   102 112 142 U

我的数据库WX2不支持递归CTE,除了递归CTE之外,我所拥有的只是普通的ANSI SQL语法。

1 个答案:

答案 0 :(得分:0)

最后找到了一个解决方案,虽然是我最初建议的修改版本

WITH lookup ( x1, h_id, l1_no, l1_key, l2_no, l2_key, l3_no, l3_key ) AS
         (SELECT 20, 1, 1, 101, 11, 111, 41, 141 FROM sys.DUAL UNION ALL
          SELECT 20, 1, 1, 101, 12, 112, 42, 142 FROM sys.DUAL UNION ALL
          SELECT 20, 1, 3, 102, 13, 113, 43, 143 FROM sys.DUAL UNION ALL
          SELECT 20, 1, 4, 103, 14, 114, 44, 144 FROM sys.DUAL),
     dwh_keys AS
         (SELECT      x1, h_id, MAX ( l1_key ) mk1, MAX ( l2_key ) mk2, MAX ( l3_key ) mk3
          FROM        lookup
          GROUP BY    1, 2),
     src ( x1, h_id, l1_no, l2_no, l3_no ) AS
         (SELECT 20, 1, 1, 19, 41 FROM sys.DUAL UNION ALL
          SELECT 20, 1, 1, 12, 42 FROM sys.DUAL UNION ALL
          SELECT 20, 1, 5, 15, 41 FROM sys.DUAL UNION ALL
          SELECT 20, 1, 6, 16, 42 FROM sys.DUAL),
     stg1 AS
         (SELECT    DISTINCT b.*, CASE WHEN a.x1 IS NOT NULL THEN 'U' ELSE 'I' END AS delta_type
          FROM      src b
                    LEFT OUTER JOIN
                    lookup a
                        ON     a.x1 = b.x1
                           AND a.h_id = b.h_id
                           AND a.l1_no = b.l1_no
                           AND a.l2_no = b.l2_no
                           AND a.l3_no = b.l3_no),
     stg2 AS
         (SELECT    a.*, CASE WHEN delta_type = 'I' AND b.l1_no IS NULL THEN 0 ELSE 1 END AS flag1, b.l1_key AS k1,
                    CASE WHEN delta_type = 'I' AND c.l2_no IS NULL THEN 0 ELSE 1 END AS flag2, c.l2_key AS k2,
                    CASE WHEN delta_type = 'I' AND d.l3_no IS NULL THEN 0 ELSE 1 END AS flag3, d.l3_key AS k3
          FROM      stg1 a
                    LEFT OUTER JOIN (SELECT DISTINCT x1, h_id, l1_no, l1_key FROM lookup) b
                        ON a.x1 = b.x1 AND a.h_id = b.h_id AND a.l1_no = b.l1_no
                    LEFT OUTER JOIN (SELECT DISTINCT x1, h_id, l1_no, l1_key, l2_no, l2_key FROM lookup) c
                        ON a.x1 = c.x1 AND a.h_id = c.h_id AND a.l1_no = c.l1_no AND a.l2_no = c.l2_no
                    LEFT OUTER JOIN
                    (SELECT DISTINCT x1, h_id, l1_no, l1_key, l2_no, l2_key, l3_no, l3_key FROM lookup) d
                        ON     a.x1 = d.x1
                           AND a.h_id = d.h_id
                           AND a.l1_no = d.l1_no
                           AND a.l2_no = d.l2_no
                           AND a.l3_no = d.l3_no),
     keys AS
         (SELECT    a.*,
                    CASE WHEN flag1 = 0 THEN mk1 + DENSE_RANK ( ) OVER (ORDER BY a.delta_type, l1_no) ELSE a.k1 END AS new_key1,
                    CASE WHEN flag2 = 0 THEN mk2 + DENSE_RANK ( ) OVER (ORDER BY a.delta_type, l1_no, l2_no) ELSE a.k2 END AS new_key2,
                    CASE WHEN flag3 = 0 THEN mk3 + DENSE_RANK ( ) OVER (ORDER BY a.delta_type, l1_no, l2_no, l3_no) ELSE a.k3 END AS new_key3
          FROM      stg2 a LEFT OUTER JOIN dwh_keys b ON a.x1 = b.x1 AND a.h_id = b.h_id)
SELECT      x1, h_id, l1_no, l2_no, l3_no, delta_type, new_key1, new_key2, new_key3
FROM        keys
ORDER BY    1, 2, 3, 4, 5;