如何使用 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 子句?的主要内容,如果未能解决你的问题,请参考以下文章