如何使用 case 语句来确定存储过程中的 from 和 where 子句?

Posted

技术标签:

【中文标题】如何使用 case 语句来确定存储过程中的 from 和 where 子句?【英文标题】:How do I use a case statement to determine the from and where clauses in a stored procedure? 【发布时间】:2012-02-09 16:57:54 【问题描述】:

我有一个长存储过程,它根据所选项目返回有关项目的数据。

我能够在很长一段时间内使用案例构建存储过程,但我必须在每个案例中包含大量重复信息。有 11 种不同的项目可供选择,我想让代码尽可能简洁。

FROM 和 WHERE 子句只有细微的差别,但我无法找出在常见的 INSERT 和 SELECT 语句之后使用 Cases 的正确方法。

我一直在四处寻找,我知道 oracle 在 SELECT 之后要求我提供适当的 THEN 语句。

这只是一种语法情况还是存在根本缺陷?

我的两种情况的工作代码如下。我已经在我想放一个箱子的地方加入了 cmets。

感谢您的阅读,并提前感谢您的任何建议。

PROCEDURE Fetch_Data_By_Item     
(
inItem STRING,
RC1 IN OUT Sys_Refcursor
)AS

tRc1 SYS_REFCURSOR;

 BEGIN

       CASE
       WHEN inItem = 'ItemOne'
       THEN

       DELETE FROM TEMP_DATA;           ----Common to all procedures
       INSERT   INTO    TEMP_DATA
       (
          Item_Name,
          Item_OTher,
          Item_id,
          _code,
          _date
        )


     --Get all the ILists 

      WITH _LIST AS                 ----Common to all procedures
    ( 
           SELECT DISTINCT 
                           A.List_ as "_IList"
           FROM            Schema.VIEW_One             A
           INNER JOIN      SChema2.VIEW_Two    
           WHERE           A.SI_                = 'Y'   

    )

       --ItemOne


    (   SELECT           
              'Item_One' as "Item_Name",
              'ITem_One' as "Item_Other" ,
              S.Item_ID,
              T._CODE,
              S._DATE,                      ----Common to all procedures  

                        ----Case should start here    
       FROM   Schema.VIEW_ONE T, 
              SChema.VIEW_Two S
       WHERE  T.Item_id           =           S.Item_id
       AND    T.SI_               =           'Y' 
       AND    S.SI_                =          'Y'
       AND    T.List_             NOT IN     (SELECT _IList FROM  _LIST)
    )   

       );

       --Item 2

    WHEN inItem = 'ItemTwo'
        THEN

       DELETE FROM TEMP_DATA;
       INSERT   INTO    TEMP_DATA
       (
          Item_Name,
          Item_OTher,
          Item_id,
          _code,
          _date
       )


     --Get all the ILists

      WITH _LIST AS
    ( 

           SELECT DISTINCT 
                           A.List_ as "_IList"
           FROM            Schema.VIEW_One             A
           INNER JOIN      SChema2.VIEW_Two    
            WHERE           A.SI_               = 'Y'                    
       --Real Estate
    )

    (   SELECT           
              'Item_Two' as "Item_Name",
              'ITem_Two' as "Item_Other" ,
              S.Item_ID,
              T._CODE,
              S._DATE,                        

       FROM   Schema.VIEW_ONE   T, 
              SChema.VIEW_Two   S,
          Schema.VIEW_Three U
       WHERE  T.Item_id           =           S.Item_id
       AND    T.SI_               =           'Z' 
       AND    S.SI_               =           'Z'
       And    U.Id        =       '123'
       AND    T.List_             NOT IN     (SELECT _IList FROM  _LIST)
    )  ;

---9 other cases********

End Case;

OPEn RC1 for

SELECT

Item_Name,
Item_Other,
Item_Id,
code,
date

FROM TEMP_DATA;
END FETCH_DATA_BY_ITEM

修订 如前所述,一直在尝试多种方法来实现这一结果,到目前为止,这是我能得到的最接近的方法。我似乎无法弄清楚这里缺少什么。这就是我目前所拥有的,也许可以看到我缺少的东西?

    PROCEDURE Fetch_Data_By_Item
    (
    inItem STRING,
    RC1 IN OUT Sys_Refcursor
    )AS

   tRc1 SYS_REFCURSOR;

   BEGIN

   DELETE FROM TEMP_DATA;              ----Common to all procedures
   INSERT   INTO    TEMP_DATA
   (
      ....
   )
 --Get all the ILists 

  WITH _LIST AS                          ----Common to all procedures
( 
      ....
)

(   SELECT           
          S.Item_Name,
          S.Item_Other ,
          S.Item_ID,
          T._CODE,
          S._DATE,                         ----Common to all procedures  


   FROM   Schema.VIEW_ONE T, 
          Schema.VIEW_Two S,
          Schema.VIEW_Three U

   WHERE

   CASE inItem

   --ItemOne

   WHEN 'ItemOne'
   THEN

          T.Item_id     =           S.Item_id
   AND    T.SI_         =           'Y' 
   AND    S.SI_         =           'Y'
   AND    T.List_             NOT IN     (SELECT _IList FROM  _LIST)

   --Item 2

   WHEN 'ItemTwo'
   THEN         

          T.Item_id     =           S.Item_id
   AND    T.SI_         =           'Z' 
   AND    S.SI_         =           'Z'
   And    U.Id          =          '123'
   AND    T.List_             NOT IN     (SELECT _IList FROM  _LIST)
   END
);

    ---9 other cases********

    OPEN RC1 for

    SELECT

    ....

    FROM TEMP_DATA;
    END FETCH_DATA_BY_ITEM

【问题讨论】:

非常感谢您的编辑。 【参考方案1】:

您的 WHERE 子句现在不需要 CASE:

WHERE
(
   inItem = 'ItemOne'
   AND T.Item_id     =           S.Item_id
   AND    T.SI_         =           'Y' 
   AND    S.SI_         =           'Y'
   AND    T.List_             NOT IN     (SELECT _IList FROM  _LIST)
)
OR (
   inItem = 'ItemTwo'
   AND T.Item_id     =           S.Item_id
   AND    T.SI_         =           'Z' 
   AND    S.SI_         =           'Z'
   And    U.Id          =          '123'
   AND    T.List_             NOT IN     (SELECT _IList FROM  _LIST)
)

根据您的其他选择,这可以简化:

WHERE
   T.Item_id     =           S.Item_id
   AND T.List_             NOT IN     (SELECT _IList FROM  _LIST)
   AND (
      (
         inItem = 'ItemOne'
         AND    T.SI_         =           'Y' 
         AND    S.SI_         =           'Y'
      )
      OR (
         inItem = 'ItemTwo'
         AND    T.SI_         =           'Z' 
         AND    S.SI_         =           'Z'
         And    U.Id          =          '123'
      )
   )

如果你有表格的“条件”表

inItem, T_SI_, S_SI_, U_Id
ItemOne, Y, Y, NULL
ItemTwo, Z, Z, 123

然后这会变得更简单一些(我知道你使用了隐式连接,我建议放弃它):

INNER JOIN conditions
    ON conditions.inItem = inItem
    AND (conditions.T_SI_ IS NULL OR conditions.T_SI_ = T.SI_)
    AND (conditions.S_SI_ IS NULL OR conditions.S_SI = S.SI_)
    AND (conditions.U_Id IS NULL OR conditions.U_Id = U.Id)
WHERE
   T.Item_id = S.Item_id -- This actually moves up into an INNER JOIN
   AND T.List_ NOT IN (SELECT _IList FROM  _LIST)

逻辑表适用于可能偶尔更改的内容,并且通常可以使代码更具可读性。您必须小心不要过度使用元数据,因为这会使系统更难维护,但如果使用得当,它可能是一个很好的策略。

【讨论】:

这可能只是解决方法。为了简化,我确实有很多小的变化,但这看起来很有希望。 @dee 还请注意,有时可以将这样的复杂文字条件从表中排除,我会更新我的答案。 我花了几天时间研究您提出的想法。不幸的是,我无法加入第三个视图“Schema.VIEW_Three U”,因为它更改了我大约一半项目的结果集。知道是否可以找到某种方法来根据 inItem 确定 FROM 子句吗?谢谢。 @dee 从您的原始示例来看,您似乎需要一个 CROSS JOIN(这将阻止 INNER JOIN 在条件下工作) - 这对我来说是一种代码味道,因为在给定的系统中如果有的话,我很少使用 CROSS JOIN。表驱动方法只能在逻辑结构适合该方法的情况下工作。在您的代码中,除了一个以上满足条件时,可能会乘以行数(然后使用 DISTINCT 进行重复数据删除),U 提供的内容并不十分清楚。甚至没有使用来自 U 的列。 感谢您这么快回复。事实证明,需要这个存储过程的应用程序需要我构建一个更正式的设置,而无需临时表。因此,无论如何我都会重新构建一个更笨重但更稳定的系统,其中每个项目都有自己的存储过程。我真的很感谢您的时间和帮助,我从中学到了很多东西。【参考方案2】:

要使用不同的 froms 和 wheres,您需要使用动态 sql。您可以使用execute immediate 执行此操作。例如:

set serveroutput on size 10000
declare
  vtest  varchar2(1);
begin
  execute immediate 'select ''X'' from dual' into vtest;
  dbms_output.put_line(vtest);
end;

所以你可以创建一个变量来存储你的sql,并使用大小写来填充你的变量。

更多信息here

【讨论】:

以上是关于如何使用 case 语句来确定存储过程中的 from 和 where 子句?的主要内容,如果未能解决你的问题,请参考以下文章

存储过程中如何使用Case嵌套

SQL Server:CASE 语句中的 OR 语句

如何从我的结果中删除 CASE 语句?

MySQL 存储过程CASE语句用法

MySQL中的存储过程(详细篇)

oracle的存储过程中,使用select into 语句的错误