Oracle SQL健全性检查 - 加入以获取最新创建的记录

时间:2015-12-01 19:20:41

标签: sql oracle

我有一个Facility / Node表,以及一个带有简单查询/连接的清单表,以显示当前打开的清单(具有空CLOSE_DATE字段)。

节点可能有也可能没有检查表,只有一个打开的检查表,多个已关闭的检查表。相当标准的东西。

但是现在我还需要显示最近创建但已关闭的清单,并希望在我开始将其集成到真正的代码库之前检查我的解决方案的健全性。

注意:Oracle SQL

CREATE TABLE test_user (user_id number, user_name varchar2(16));
CREATE TABLE test_node (node_id number, node_name varchar2(16));
CREATE TABLE test_list (list_id number, node_id number, user_id number, create_date date, close_date date);
INSERT INTO test_user VALUES (1, 'apple');
INSERT INTO test_user VALUES (2, 'pear');
INSERT INTO test_node VALUES (1, 'facility 1');
INSERT INTO test_node VALUES (2, 'facility 2');
INSERT INTO test_node VALUES (3, 'facility 3');
INSERT INTO test_node VALUES (4, 'facility 4');
INSERT INTO test_list VALUES (1, 1, 1,  '1-jan-2015', NULL);
INSERT INTO test_list VALUES (2, 2, 1, '1-jan-2015', '1-feb-2015');
INSERT INTO test_list VALUES (3, 2, 1, '1-apr-2015', '1-jun-2015');
INSERT INTO test_list VALUES (4, 2, 2, '1-mar-2015', '2-mar-2015');
INSERT INTO test_list VALUES (5, 2, 1, '1-nov-2015', NULL);
INSERT INTO test_list VALUES (6, 4, 2, '1-nov-2015', '15-nov-2015');

用户表主要是其中的一部分,以使左连接更复杂,以模拟实际的数据库。

我正在尝试创建一个返回每个节点/工具的单个SQL,有关其当前打开的核对表(如果有)的信息,以及有关最近关闭的核对表(如果有)的信息。

我最初尝试解决我的问题:

WITH list_breakout_open AS (
    SELECT  ll.list_id, ll.node_id, ll.create_date,
            ll.user_id, uu.user_name

      FROM  test_list ll
      JOIN  test_user uu ON ll.user_id = uu.user_id
     WHERE  ll.close_date IS NULL
), list_breakout_close AS (
    SELECT  ll.list_id, ll.node_id, ll.create_date, ll.close_date,
            ll.user_id, uu.user_name,
            RANK() OVER (PARTITION BY ll.node_id ORDER BY ll.create_date DESC) AS close_rank

      FROM  test_list ll
      JOIN  test_user uu ON ll.user_id = uu.user_id
     WHERE  ll.close_date IS NOT NULL
)
SELECT  nn.node_id, nn.node_name, 
        lbo.list_id AS open_list_id, lbo.user_name AS open_user_name, lbo.create_date AS open_create_date,
        lbc.list_id AS close_list_id, lbc.user_name AS close_user_name, lbc.create_date AS close_create_date, lbc.close_date AS close_close_date

  FROM  test_node nn
  LEFT JOIN list_breakout_open lbo ON nn.node_id = lbo.node_id
  LEFT JOIN list_breakout_close lbc ON nn.node_id = lbc.node_id
                                    AND close_rank = 1;

这些表永远不会变得庞大(数千行,而不是数百万行),因此性能不是一个大问题。我主要关注的是代码维护,并且想要一个干净的查询,当有人在5年后看到这个混乱时,它会有意义。

我的解决方案是否理智?我错过了一些可能会让我以后绊倒的明显事物吗?

编辑:添加了一条关于我的最终查询应该做什么的说明。

2 个答案:

答案 0 :(得分:1)

您可以扫描每个表一次并使用分析函数进行聚合,如下所示:

SQL> SELECT n.node_id,
  2         n.node_name,
  3         min(case when l.close_date is null then l.list_id end) keep (dense_rank first order by nvl2(l.close_date, null, l.create_date) desc nulls last) open_list_id,
  4         min(case when l.close_date is null then u.user_name end) keep (dense_rank first order by nvl2(l.close_date, null, l.create_date) desc nulls last) open_user_name,
  5         min(case when l.close_date is null then l.create_date end) keep (dense_rank first order by nvl2(l.close_date, null, l.create_date) desc nulls last) open_create_date,
  6         min(case when l.close_date is not null then l.list_id end) keep (dense_rank first order by nvl2(l.close_date, l.create_date, null) desc nulls last) close_list_id,
  7         min(case when l.close_date is not null then u.user_name end) keep (dense_rank first order by nvl2(l.close_date, l.create_date, null) desc nulls last) close_user_name,
  8         min(case when l.close_date is not null then l.create_date end) keep (dense_rank first order by nvl2(l.close_date, l.create_date, null) desc nulls last) close_create_date,
  9         min(case when l.close_date is not null then l.close_date end) keep (dense_rank first order by nvl2(l.close_date, l.create_date, null) desc nulls last) close_close_date
 10    FROM test_node n
 11    LEFT JOIN test_list l ON l.node_id = n.node_id
 12    LEFT JOIN test_user u ON u.user_id = l.user_id
 13   GROUP BY n.node_id, n.node_name
 14   ORDER BY node_id;
   NODE_ID NODE_NAME        OPEN_LIST_ID OPEN_USER_NAME   OPEN_CREATE_DATE CLOSE_LIST_ID CLOSE_USER_NAME  CLOSE_CREATE_DATE CLOSE_CLOSE_DATE
---------- ---------------- ------------ ---------------- ---------------- ------------- ---------------- ----------------- ----------------
         1 facility 1                  1 apple            01/01/2015                                                        
         2 facility 2                  5 apple            01/11/2015                   3 apple            01/04/2015        01/06/2015
         3 facility 3                                                                                                       
         4 facility 4                                                                  6 pear             01/11/2015        15/11/2015

不完全确定它是正确的输出,正如其他人提到的那样。我使用您发布的解决方案对其进行了验证。

答案 1 :(得分:1)

考虑一个派生表方法,避免使用窗口函数WITH()RANK()

SELECT openchecklist.node_id, openchecklist.node_name,
       openchecklist.list_id AS open_list_id, openchecklist.user_name AS open_user_name, 
       openchecklist.create_date AS open_create_date,
       closedchecklist.list_id AS close_list_id, closedchecklist.user_name AS close_user_name, 
       closedchecklist.create_date AS close_create_date, closedchecklist.close_date AS close_close_date

FROM
       (SELECT ll.list_id, ll.node_id, nn.node_name, ll.create_date, 
               ll.user_id, uu.user_name
          FROM (test_list ll
    INNER JOIN test_user uu ON ll.user_id = uu.user_id)
    INNER JOIN test_node nn ON ll.node_id = nn.node_id
         WHERE ll.close_date IS NULL) AS openchecklist

LEFT JOIN

      (SELECT ll.list_id, ll.node_id, nn.node_name, ll.create_date, ll.close_date,
              ll.user_id, uu.user_name        
         FROM (test_list ll
   INNER JOIN test_user uu ON ll.user_id = uu.user_id)
   INNER JOIN test_node nn ON ll.node_id = nn.node_id
        WHERE ll.close_date IS NOT NULL
          AND ll.close_date = (SELECT MAX(close_date)  FROM test_list temp 
                               WHERE temp.node_id = ll.node_id)
       ) As closedchecklist

ON openchecklist.node_id = closedchecklist.node_id

或者,甚至考虑联合查询 (堆叠数据),这样可以避免昂贵的列构建,因为数据以长格式显示。

    SELECT nn.node_id, nn.node_name, ll.list_id,
           uu.user_name, ll.create_date, ll.close_date,
           'OPEN' as status    
      FROM test_list ll
INNER JOIN test_node nn ON ll.node_id = nn.node_id
 LEFT JOIN test_user uu ON ll.user_id = uu.user_id
     WHERE ll.close_date IS NULL
  ORDER BY ll.node_id, ll.create_date DESC

     UNION

    SELECT nn.node_id, nn.node_name, ll.list_id,
           uu.user_name, ll.create_date, ll.close_date,
           'CLOSED' as status               
      FROM test_list ll
INNER JOIN test_node nn ON ll.node_id = nn.node_id
 LEFT JOIN test_user uu ON ll.user_id = uu.user_id
     WHERE ll.close_date IS NOT NULL
       AND ll.close_date = (SELECT MAX(close_date)  FROM test_list temp 
                            WHERE temp.node_id = ll.node_id)