根据变量的值从同一个查询中得到不同的结果

getting different results from the same query according to the value of a variable

假设我们有以下主数据:

order_type: [classic, transport]
order_status: [new, active, delivered, finished]

我们有订单 tables:

+----------+------------+--------------+
| order_id | order_type | order_status |
+==========+============+==============+
| 1        | classic    | new          |
+----------+------------+--------------+
| 2        | classic    | active       |
+----------+------------+--------------+
| 3        | transport  | active       |
+----------+------------+--------------+
| 4        | transport  | delivered    |
+----------+------------+--------------+
| 5        | classic    | finished     |
+----------+------------+--------------+
| 6        | classic    | active       |
+----------+------------+--------------+
| 7        | transport  | delivered    |
+----------+------------+--------------+
| 8        | classic    | finished     |
+----------+------------+--------------+

请求的是根据给定变量的值 v_order_status:

进行一个查询,该查询可以 return 2 组不同的数据

所需的 SQL 语句将在存储过程中使用,例如:

procedure process_orders (v_order_status in varchar2(3) default null)
as
begin
  for ord in (select order_id, order_type, order_status
                from orders
                where [here comes the tricky part])
  loop
    do_something (ord.order_id);
    ...
  end loop;
end process_orders ; 

知道如何在 SQL 语句中设置 where 子句吗?

使用布尔逻辑:

select order_id, order_type, order_status
from orders
where 
    (v_order_status = 'del' and order_type = 'transport')
    or v_order_status <> 'del' 
    or v_order_status is null
WHERE (v_order_status='del' AND order_type= 'transport') 
OR (v_order_status <> 'del' OR v_order_status is null)  

我假设存储过程语言是PL/SQL。

  • 不要将v_order定义为默认null,如果需要则默认'active'

  • 只需在where条件中使用变量

      procedure process_orders(v_order_status in varchar2(3) )
      as
      begin
        for ord in (select order_id, order_type, order_status
                      from orders
                     where order_status = v_order_status
        loop
          do_something (ord.order_id);
          ...
        end loop;
      end process_orders ;
    

因为你在一个过程中,你应该利用它来使 select 语句尽可能简单。不要使用复杂的 where 子句(这可能会导致性能问题)创建查询,您应该在 PL/SQL 代码中执行您需要的任何逻辑,然后执行一个简单的查询。

在下面的示例中,我创建了一个局部变量,它根据您描述的条件设置了值,然后将该变量用作 where 子句中的唯一条件。我添加了 NVL (order_type, 'no_order_type') 逻辑,以防万一有没有订单类型的订单,但如果那不可能,那么您可以从 where 子句中删除 NVL

例子

DECLARE
    PROCEDURE process_orders (p_order_status IN VARCHAR2 DEFAULT NULL)
    AS
        l_order_type   VARCHAR2 (20)
                           := CASE p_order_status WHEN 'del' THEN 'transport' ELSE NULL END;
    BEGIN
        FOR ord
            IN (SELECT order_id, order_type, order_status
                  FROM (SELECT 1 AS order_id, 'classic' AS order_type, 'new' AS order_status
                          FROM DUAL
                        UNION ALL
                        SELECT 2, 'classic', 'active' FROM DUAL
                        UNION ALL
                        SELECT 3, 'transport', 'active' FROM DUAL
                        UNION ALL
                        SELECT 4, 'transport', 'delivered' FROM DUAL
                        UNION ALL
                        SELECT 5, 'classic', 'finished' FROM DUAL
                        UNION ALL
                        SELECT 6, 'classic', 'active' FROM DUAL
                        UNION ALL
                        SELECT 7, 'transport', 'delivered' FROM DUAL
                        UNION ALL
                        SELECT 8, 'classic', 'finished' FROM DUAL) orders
                 WHERE NVL (order_type, 'no_order_type') =
                       NVL (l_order_type, NVL (order_type, 'no_order_type')))
        LOOP
            --do_something (ord.order_id);
            DBMS_OUTPUT.put_line ('Order ID: ' || ord.order_id);
        END LOOP;
    END process_orders;
BEGIN
    DBMS_OUTPUT.put_line ('Status: something');
    process_orders ('something');
    DBMS_OUTPUT.put_line ('Status: del');
    process_orders ('del');
    DBMS_OUTPUT.put_line ('Status: null');
    process_orders (NULL);
END;

输出

Status: something
Order ID: 1
Order ID: 2
Order ID: 3
Order ID: 4
Order ID: 5
Order ID: 6
Order ID: 7
Order ID: 8
Status: del
Order ID: 3
Order ID: 4
Order ID: 7
Status: null
Order ID: 1
Order ID: 2
Order ID: 3
Order ID: 4
Order ID: 5
Order ID: 6
Order ID: 7
Order ID: 8

作为提供的所有解决方案的替代方案,另一种选择是使用动态 sql 并将 for loop 结构更改为 bulk collect 。这就是多行查询的动态 SQL。

类似的东西应该适用于你的情况。

测试用例元素

SQL> create table orders ( order_id number, order_type varchar2(20) , order_status varchar2(20) ) ;

Table created.

SQL> insert into orders
  2  with t ( order_id , order_type, order_status )
  3  as
  4  (
  5  select 1, 'classic'   , 'del' from dual union all
  6  select 2, 'classic'   , 'del' from dual union all
  7  select 3, 'transport' , 'act' from dual union all
  8  select 4, 'transport' , 'act' from dual union all
  9  select 5, 'transport' , 'fin' from dual union all
 10  select 6, 'classic'   , 'fin' from dual union all
 11  select 7, 'classic'   , 'fin' from dual union all
 12  select 8, 'transport' , 'act' from dual union all
 13  select 9, 'transport' , 'act' from dual
 14  )
 15  select * from t ;

9 rows created.

SQL>
SQL> commit;

Commit complete.

SQL>
SQL> create or replace procedure process_orders ( v_order_status in varchar2 default null )
  2  is
  3     TYPE order_rt IS RECORD (
  4     order_id     number,
  5     order_type   varchar2(20),
  6     order_status varchar2(20)
  7     );
  8
  9     TYPE order_rt_aat IS TABLE OF order_rt INDEX BY PLS_INTEGER;
 10
 11     l_orders   order_rt_aat;
 12     v_sql      clob;
 13  BEGIN
 14
 15     v_sql := q'[ select order_id, order_type, order_status from orders ]' ;
 16     if v_order_status is not null
 17     then
 18                  v_sql := v_sql || ' where order_status = '''||v_order_status||''' ';
 19     end if;
 20
 21     execute immediate v_sql bulk collect into l_orders;
 22
 23     FOR indx IN 1 .. l_orders.COUNT
 24     LOOP
 25        DBMS_OUTPUT.put_line (l_orders (indx).order_id);
 26     END LOOP;
 27  END;
 28  /

Procedure created.

SQL>

验证

运行 无入参

SQL> set serveroutput on
SQL> exec process_orders ;
1
2
3
4
5
6
7
8
9

PL/SQL procedure successfully completed.

运行 带有特定的输入参数

SQL> exec process_orders ( 'del' ) ;
1
2

PL/SQL procedure successfully completed.

同意使用 where 子句的其他答案完全没问题,

正如这个特殊要求所说 v_order_status = 'del' 然后只检索运输订单 v_order_status =any other value or null 然后我们检索整个 table,为此我们也可以使用decode 作为一行语句,

The Oracle docs note the following about the decode SQL function: "DECODE compares a column or expression to search values, returning a result when there is a match. DECODE is similar to IF_THEN-ELSE logic."

select *
  from
(
    select 1 order_id,'classic' order_type,'new' order_status from dual
    union all
    select 2,'classic','active' from dual
    union all
    select 3,'transport','active' from dual
    union all
    select 4,'transport','delivered' from dual  
)
where order_type = decode(&v_order_status,'del','transport',order_type);