【问题标题】:Query table A or table B based on some condition根据某些条件查询表A或表B
【发布时间】:2020-10-13 13:52:44
【问题描述】:

在 Oracle DB (12) 中,我有 2 个表:

表格:STEP_DETAILS

+-----------+---------+---------------+
| record_id | step_id | material_type |
+===========+=========+===============+
| 1         | 1       | in            |
+-----------+---------+---------------+
| 2         | 1       | in            |
+-----------+---------+---------------+
| 3         | 1       | out           |
+-----------+---------+---------------+
| 4         | 2       | in            |
+-----------+---------+---------------+
| 5         | 2       | out           |
+-----------+---------+---------------+
| 6         | 2       | out           |
+-----------+---------+---------------+

表:ACTIONS_DETAILS

+-----------+-----------+---------------+
| record_id | action_id | material_type |
+===========+===========+===============+
| 1         | 11        | in            |
+-----------+-----------+---------------+
| 2         | 11        | out           |
+-----------+-----------+---------------+
| 3         | 12        | in            |
+-----------+-----------+---------------+
| 4         | 12        | out           |
+-----------+-----------+---------------+

所有 id 列都是 INTEGER 类型。

我需要计算两个表的输入材料。 在 PL/SQL 块中,我有以下函数,每个函数都有“几乎”相同的查询:

--count from step_details:
FUNCTION get_step_input_count(p_step_id  step_details.step_id%TYPE)
  RETURN INTEGER
  IS
    l_count INTEGER := 0;
  BEGIN
    SELECT COUNT(1)
      INTO l_count
      FROM step_details
     WHERE step_id = p_step_id
       AND material_type = 'in';

    RETURN l_count;
  END get_step_input_count;

--count from action_details:
  FUNCTION get_action_input_count(p_action_id  action_details.action_id%TYPE)
  RETURN INTEGER
  IS
    l_count INTEGER := 0;
  BEGIN
    SELECT COUNT(1)
      INTO l_count
      FROM action_details
     WHERE action_id = p_action_id
       AND material_type = 'in';

    RETURN l_count;
  END get_action_input_count;

是否可以编写一个单独的 SELECT 语句,每次可以根据某些条件查询 2 个表之一,所以我最终将编写一个使用一个查询而不是 2 个函数的函数,例如:

  FUNCTION get_input_count(p_parent_id  integer,
                           p_from       varchar2)
  RETURN INTEGER
  IS
    l_count INTEGER := 0;
  BEGIN
    SELECT COUNT(1)
      INTO l_count
      FROM (when p_from = 'S' then 'step_details'
            when p_from = 'A' then 'action_details')
     WHERE (when p_from = 'S' then 'step_id   = p_parent_id'
            when p_from = 'A' then 'action_id = p_parent_id')
       AND material_type = 'in';

    RETURN l_count;
  END get_input_count;

【问题讨论】:

    标签: sql oracle plsql stored-functions


    【解决方案1】:

    您可以尝试以下方法:

    select sum(num_rows)
    from
    (
        select count(*) as num_rows
        from tab1                        /* first table */
        where :param = 1
        union all
        select count(*) as num_rows
        from tab2                        /* second table */
        where :param = 2 
    )
    

    在这里,您使用单个 select 包装来自所有可能表的查询的 UNION ALL;每个表都会根据某个参数的值给出其贡献与否,因此您只能根据参数值从表中获取所需的行。

    【讨论】:

    • 如果联合查询之一抛出 NO_DATA_FOUND 异常会发生什么?假设我们要从第一个表中计数,所以第一个查询运行良好,但由于某种原因,第二个查询由于 no_data_found 而失败。这将如何影响整个功能?
    • 无论是哪张表,如果两个查询之一失败,您将得到零计数结果,而不是找不到数据;如果一张表没有给出结果,您将获得另一张表的记录数。如果两个查询都为零,则结果为零
    【解决方案2】:

    一种选择是使用由表名和列名的连接组成的动态 SQL,以及用于从每个表中查询的通用值的绑定变量 (p_parent_id)。

    SQL> CREATE OR REPLACE FUNCTION get_input_count(p_parent_id INT, p_from VARCHAR2) RETURN INT IS
        l_count INT;
        crs     SYS_REFCURSOR;
        v_sql   VARCHAR2(32767);
        v_from  VARCHAR2(32);
        v_col   VARCHAR2(99);
    BEGIN
        SELECT DECODE(p_from,'A','actions_details','S','step_details'),
               DECODE(p_from,'A','action_id','S','step_id')
          INTO v_from, v_col
          FROM dual;
    
         v_sql := 'SELECT COUNT(*) 
                     FROM '||v_from||' 
                    WHERE material_type = ''in'' AND '||v_col||' = :prt_id';
    
         OPEN crs FOR v_sql USING p_parent_id; 
         LOOP       
            FETCH crs INTO l_count;
           EXIT WHEN crs%NOTFOUND;             
         END LOOP;
    
         CLOSE crs;
    
        RETURN l_count;
    END;
    /
    

    Demo

    在哪里

    • 用零初始化l_count变量的值是多余的, 因为查询将无一例外地返回零,只要没有 找到匹配记录
    • 关键字FUNCTION 应该在前面加上CREATE [OR REPLACE]
    • 以名称结束存储的函数是可选的(可能是 忽略)

    【讨论】:

      【解决方案3】:

      您还可以使用如下所示的动态 SQL。 在这种情况下应该注意 SQL 注入。

      sql_statement := 'select count(*)';
      IF table = 'xxx'
      THEN 
        sql_statement := sql_statement || ' from xxx where material_type = ''in''';
      ELSIF table = 'yyy'
        sql_statement := sql_statement || ' from yyy where yyyy_type = ''in''';
      END IF;
      sql_statement := sql_statement blur blur;
      EXECUTE IMMEDIATE sql_statement INTO l_count USING p_1;
      

      【讨论】:

      • 表名不能用作动态 SQL 中的绑定变量。
      猜你喜欢
      • 1970-01-01
      • 2020-09-28
      • 2019-02-14
      • 1970-01-01
      • 2018-09-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多