【问题标题】:Oracle - drill down the recordsOracle - 向下钻取记录
【发布时间】:2022-06-16 17:19:00
【问题描述】:

我有一张包含服务的表格,每种服务组合都有特定的费用金额。 我想过滤一项服务并了解左侧一级的服务是什么,然后从该子部分中选择另一项服务,等等。

请看示例图片: 左边是“向下钻取”的过程,右边是所需的输出。请忽略金额的总和(它们不正确)。

example picture

CREATE TABLE test_table (
id              INTEGER,
costcenter      VARCHAR2(20),
service_level1  VARCHAR2(40),
service_level2  VARCHAR2(40),
service_level3  VARCHAR2(40),
service_level4  VARCHAR2(40),
amount          INTEGER);

INSERT INTO test_table (id,costcenter, service_level1, service_level2, service_level3, service_level4, amount)
VALUES ( 1, '10016831', 'U00 COGNOS AL', NULL, NUll, NULL, 50000); 
INSERT INTO test_table (id,costcenter, service_level1, service_level2, service_level3, service_level4, amount)
VALUES ( 2, '10016832', 'EXADATA Basis', 'U00 COGNOS AL', NUll, NULL, 20000); 
INSERT INTO test_table (id,costcenter, service_level1, service_level2, service_level3, service_level4, amount)
VALUES ( 3, '10016833', 'SPLUNK','EXADATA Basis', 'U00 COGNOS AL', NULL, 15000); 
INSERT INTO test_table (id,costcenter, service_level1, service_level2, service_level3, service_level4, amount)
VALUES ( 4, '10016833', 'Linux Basis', 'SPLUNK', 'EXADATA Basis', 'U00 COGNOS AL', 30000); 
INSERT INTO test_table (id,costcenter, service_level1, service_level2, service_level3, service_level4, amount)
VALUES ( 5, '10016833', 'Linux Basis', 'Oracle Admin', 'EXADATA Basis', 'U00 COGNOS AL', 20000); 
COMMIT;

【问题讨论】:

  • 阅读CONNECT BY

标签: sql oracle hierarchical-data


【解决方案1】:

我试图从图片中获取所需的输出。没有完全抓住它,但这是可以做的。我相信你会成功的......

WITH
    test_table AS
        (
          Select 1 "ID", '10016831' "COSTCENTER", 'U00 COGNOS AL' "SL_1", NULL "SL_2", NUll "SL_3", NULL "SL_4", 50000 "AMOUNT" From DUAL UNION ALL 
          Select 2 "ID", '10016832' "COSTCENTER", 'EXADATA Basis' "SL_1", 'U00 COGNOS AL' "SL_2", NUll "SL_3", NULL "SL_4", 20000 "AMOUNT" From DUAL UNION ALL 
          Select 3 "ID", '10016833' "COSTCENTER", 'SPLUNK' "SL_1", 'EXADATA Basis' "SL_2", 'U00 COGNOS AL' "SL_3", NULL "SL_4", 15000 "AMOUNT" From DUAL UNION ALL
          Select 4 "ID", '10016833' "COSTCENTER", 'Linux Basis' "SL_1", 'SPLUNK' "SL_2", 'EXADATA Basis' "SL_3", 'U00 COGNOS AL' "SL_4", 30000 "AMOUNT" From DUAL UNION ALL
          Select 5 "ID", '10016833' "COSTCENTER", 'Linux Basis' "SL_1", 'Oracle Admin' "SL_2", 'EXADATA Basis' "SL_3", 'U00 COGNOS AL' "SL_4", 20000 "AMOUNT" From DUAL 
        )
--
-- S E R V I C E
SELECT
    'EXADATA Basis' "SERVICE", 
    Sum(AMOUNT) "AMOUNT"
FROM
    test_table
WHERE
    InStr(SL_1 || SL_2 || SL_3 || SL_4, 'EXADATA Basis') > 0
-- 
-- Result
-- SERVICE         AMOUNT
-- EXADATA Basis    85000
--
-- L E F T   1
SELECT 
    SERVICE "SERVICE",
    LEVEL_LEFT "LEVEL_LEFT", 
    Sum(AMOUNT) "AMOUNT"
FROM 
    (
        SELECT  'EXADATA Basis' "SERVICE", CASE WHEN SL_1 = 'EXADATA Basis' THEN COSTCENTER END "LEVEL_LEFT", AMOUNT
        FROM    test_table
        WHERE   InStr(SL_1 || SL_2 || SL_3 || SL_4, 'EXADATA Basis') > 0
    UNION ALL
    SELECT  'EXADATA Basis' "SERVICE", CASE WHEN SL_2 = 'EXADATA Basis' THEN SL_1 END "LEVEL_LEFT", AMOUNT
        FROM    test_table
        WHERE   InStr(SL_1 || SL_2 || SL_3 || SL_4, 'EXADATA Basis') > 0 
      UNION ALL
    SELECT  'EXADATA Basis' "SERVICE", CASE WHEN SL_3 = 'EXADATA Basis' THEN SL_2 END "LEVEL_LEFT", AMOUNT
        FROM    test_table
        WHERE   InStr(SL_1 || SL_2 || SL_3 || SL_4, 'EXADATA Basis') > 0 
      UNION ALL
        SELECT  'EXADATA Basis' "SERVICE", CASE WHEN SL_4 = 'EXADATA Basis' THEN SL_3 END "LEVEL_LEFT", AMOUNT
        FROM    test_table
        WHERE   InStr(SL_1 || SL_2 || SL_3 || SL_4, 'EXADATA Basis') > 0 
    )
WHERE
    LEVEL_LEFT Is Not Null
GROUP BY
    SERVICE,
    LEVEL_LEFT
--
-- Result
-- 
-- SERVICE          LEVEL_LEFT      AMOUNT
-- EXADATA Basis    10016832        20000
-- EXADATA Basis    SPLUNK          45000
-- EXADATA Basis    Oracle Admin    20000
--
-- L E F T   2
SELECT 
    SERVICE "SERVICE",
    LEVEL_LEFT "LEVEL_LEFT", 
    LEVEL_LEFT_2 "LEVEL_LEFT_2",
    Sum(AMOUNT) "AMOUNT"
FROM 
    (
        SELECT  'EXADATA Basis' "SERVICE", 
                CASE WHEN SL_1 = 'EXADATA Basis' THEN COSTCENTER END "LEVEL_LEFT", 
                CASE WHEN SL_1 = 'EXADATA Basis' THEN Null END "LEVEL_LEFT_2",
                AMOUNT
        FROM    test_table
        WHERE   InStr(SL_1 || SL_2 || SL_3 || SL_4, 'EXADATA Basis') > 0
      UNION ALL
        SELECT  'EXADATA Basis' "SERVICE", 
            CASE WHEN SL_2 = 'EXADATA Basis' THEN SL_1 END "LEVEL_LEFT", 
            CASE WHEN SL_2 = 'EXADATA Basis' And 
                 CASE WHEN SL_2 = 'EXADATA Basis' THEN SL_1 END  = SL_1 THEN COSTCENTER 
            END "LEVEL_LEFT_2",
            AMOUNT
        FROM    test_table
        WHERE   InStr(SL_1 || SL_2 || SL_3 || SL_4, 'EXADATA Basis') > 0 
      UNION ALL
        SELECT  'EXADATA Basis' "SERVICE", 
            CASE WHEN SL_3 = 'EXADATA Basis' THEN SL_2 END "LEVEL_LEFT", 
            CASE WHEN SL_3 = 'EXADATA Basis' And 
                 CASE WHEN SL_3 = 'EXADATA Basis' THEN SL_2 END  = SL_2 THEN SL_1 
            END "LEVEL_LEFT_2",
            AMOUNT
        FROM    test_table
        WHERE   InStr(SL_1 || SL_2 || SL_3 || SL_4, 'EXADATA Basis') > 0 
      UNION ALL
        SELECT  'EXADATA Basis' "SERVICE", 
                CASE WHEN SL_4 = 'EXADATA Basis' THEN SL_3 END "LEVEL_LEFT", 
                CASE WHEN SL_4 = 'EXADATA Basis' And 
                 CASE WHEN SL_4 = 'EXADATA Basis' THEN SL_3 END  = SL_3 THEN SL_2
            END "LEVEL_LEFT_2",
                AMOUNT
        FROM    test_table
        WHERE   InStr(SL_1 || SL_2 || SL_3 || SL_4, 'EXADATA Basis') > 0 
    )
WHERE
    LEVEL_LEFT Is Not Null
GROUP BY
    SERVICE,
    LEVEL_LEFT,
    LEVEL_LEFT_2        
--
-- Result
-- SERVICE          LEVEL_LEFT      LEVEL_LEFT_2  AMOUNT
-- EXADATA Basis    SPLUNK          Linux Basis    30000
-- EXADATA Basis    Oracle Admin    Linux Basis    20000
-- EXADATA Basis    SPLUNK          10016833       15000
-- EXADATA Basis    10016832        Null           20000

如您所见,图片中多出一行 - 可以排除,但我不确定什么是正确的条件。你会知道的。问候.. 如果您正在寻找 LEVEL_LEFT_3,这里是处理 SL_3 列中的 EXADATA 的 UNION SQL 之一(SL_4 中没有 EXADATA)。那里缺少关键字。我的错,对不起。

    SELECT  'EXADATA Basis' "SERVICE", 
        CASE WHEN SL_3 = 'EXADATA Basis' THEN SL_2 END "LEVEL_LEFT", 
        CASE WHEN SL_3 = 'EXADATA Basis' And 
             CASE WHEN SL_3 = 'EXADATA Basis' THEN SL_2 END  = SL_2 THEN SL_1 
        END "LEVEL_LEFT_2", 
  --  ----------------------------------------------------------------------------
  --      L E V E L   3
  --
        CASE
            WHEN SL_3 = 'EXADATA Basis' And         -- IF driver in SL_3  
    --
                CASE 
                    WHEN SL_3 = 'EXADATA Basis' 
                    THEN SL_2 
                END  = SL_2  And -- And driver's 1st left  is equal to value of column SL_2     
  --
                CASE 
                    WHEN SL_3 = 'EXADATA Basis' And     
                        CASE 
                            WHEN SL_3 = 'EXADATA Basis' 
                            THEN SL_2 
                        END   = SL_2 
                    THEN SL_1 
                  END  = SL_1        -- And driver's 2nd left  is equal to value of column SL_1   
            THEN COSTCENTER       -- THEN take COSTCENTER    
        END "LEVEL_LEFT_3",
  --
  --  E N D   L E V E L   3
  --  ----------------------------------------------------------------------------
        AMOUNT
    FROM    test_table
    WHERE   InStr(SL_1 || SL_2 || SL_3 || SL_4, 'EXADATA Basis') > 0
-- 
--  R e s u l t
--
--  SERVICE       LEVEL_LEFT    LEVEL_LEFT_2  LEVEL_LEFT_3     AMOUNT
--  ------------- ------------- ------------- ------------ ----------
--  EXADATA Basis                                               20000 
--  EXADATA Basis                                               15000 
--  EXADATA Basis SPLUNK        Linux Basis   10016833          30000 
--  EXADATA Basis Oracle Admin  Linux Basis   10016833          20000

【讨论】:

  • 非常感谢您的回答,除了这个组合案例CASE WHEN SL_2 = 'EXADATA Basis' And CASE WHEN SL_2 = 'EXADATA Basis' THEN SL_1 END = SL_1 THEN COSTCENTER END "LEVEL_LEFT_2" 之外,我几乎完成了它这是否意味着当 SL_2 等于“EXADATA 基础”并且 SL_1 = SL1 THEN COSTCENTER 时?
  • 它是这样的: CASE WHEN SL_2 = 'EXADATA Basis' THEN SL_1 END 是 "LEVEL_LEFT" 并且您要求的情况是 IF SL_2 = 'EXADATA Basis' And LEVEL_LEFT = SL_1 THEN LEVEL_LEFT_2 是 COSTCENTER .所以,这个嵌套案例实际上是指 LEVEL_LEFT
  • 是一种由EXADATA Basis驱动的动态相对定位。因此,如果驾驶员在 SL_4 中,左一步是 SL_3,左两步是 SL_2,如果它在 SL_3 中,那么左一步是 SL_2,左两步是 SL_1。如果在 SL_2 中,则左一步是 SL_1,左两步是 COSTCENTER。如果在 SL_1 中,则左一步为 COSTCENTER,左两步为 NULL。
  • 这里是内壳,因为有两行(可能更多)EXADATA 在同一列(SL_3)中,这种内壳解决了我们当前正在查看的行的混淆(带有 SPLUNK 的行)或 SL_2 中的 Oracle 管理员 - 位于 SL_3 的左侧或“LEVEL_LEFT”)问候...
  • 是的,我知道了,对于剩下的其他关卡,我可以这样继续吗? LEVEL_LEFT_3 CASE WHEN SL_4 = 'EXADATA Basis' and CASE WHEN SL_4 = 'EXADATA Basis' And CASE WHEN SL_4 = 'EXADATA Basis' THEN SL_3 END = SL_3 THEN SL_2 END = SL_2 then SL_1 END level_left_3
【解决方案2】:

非常感谢您的解决方案,它正在运行(我已经测试了大约 100 000 条记录),但是如果数据更多(大约 5000 万行),性能就不那么快了。

我想把它简化成这样:

  • 有一个包含服务及其子服务和 sum(amount) 的表
  • 层次结构在表格中的方向是从左到右

See the example

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多