在 where 子句中使用 case 语句的 Oracle Missing 关键字

Posted

技术标签:

【中文标题】在 where 子句中使用 case 语句的 Oracle Missing 关键字【英文标题】:Oracle Missing keyword using case statement in where clause 【发布时间】:2016-01-07 10:46:19 【问题描述】:

我有以下存储过程,我收到一个错误作为缺少关键字。我试图通过将它们放入变量来运行 sql 语句,因为我在 sql 查询中动态使用了数据库链接。在合并查询中使用 case statement 时出现错误。当我使用 dbms 输出行打印合并查询时,它会打印完整的 case 语句,而不仅仅是 ID。

    PROCEDURE                    "EXT_SOAP_MONITORING"(IN_DB_LINK IN varchar2) AS

    LAST_SM_ID Number := 0;
    LAST_CAPT_DATE DATE;
    LAST_SM_ID_MB Number := 0;
    LAST_CAPT_DATE_MB DATE;
    l_sql VARCHAR2(5000);
    l_sql1 VARCHAR2(5000);
    DB_CONNECTION_NAME VARCHAR2(100);

    BEGIN

    Select DB_LINK INTO DB_CONNECTION_NAME FROM RATOR_MONITORING_CONFIGURATION.DB_CONNECTION WHERE DB_LINK = IN_DB_LINK;
    --DELETE FROM TEMP_SOAP_MONITORING table before inserting new data into this table
    EXECUTE IMMEDIATE 'TRUNCATE TABLE TEMP_SOAP_MONITORING';

    -- first retrieve the last id (of the newest record) which has been imported at last extraction
    --FONIC
    SELECT LAST_TASK_ID INTO LAST_SM_ID FROM CAPTURING where DB_TABLE='TEMP_SOAP_MONITORING' and DB ='FONIC_RETAIL';
    SELECT CAPTURING_DATE INTO LAST_CAPT_DATE from CAPTURING WHERE DB_TABLE='TEMP_SOAP_MONITORING' and DB ='FONIC_RETAIL';

    --MB
    SELECT LAST_TASK_ID INTO LAST_SM_ID_MB FROM CAPTURING where DB_TABLE='TEMP_SOAP_MONITORING' and DB ='MB_RETAIL';
    SELECT CAPTURING_DATE INTO LAST_CAPT_DATE_MB from CAPTURING WHERE DB_TABLE='TEMP_SOAP_MONITORING' and DB ='MB_RETAIL';

    l_sql:=
'merge into TEMP_SOAP_MONITORING TSM
using (
   select * from
(select ID,REQUEST_XML,RESPONSE_XML,WEB_SERVICE_NAME,WEB_METHOD_NAME,CREATE_DATE,ERROR_CODE,ERROR_MESSAGE from
SOAP_MONITORING@'||DB_CONNECTION_NAME||'  WHERE 
ID > (CASE WHEN '||IN_DB_LINK||' = ''FONIC_RETAIL'' THEN ' || LAST_SM_ID || ' ELSE ' || LAST_SM_ID_MB ||' END) AND
WEB_SERVICE_NAME =''RatorWebShopService''  and WEB_METHOD_NAME = ''placeShopOrder'') where rownum <=1000
) data
ON (TSM.ID = data.ID)
when not matched then
insert(ID,REQUEST_XML,RESPONSE_XML,WEB_SERVICE_NAME,WEB_METHOD_NAME,CREATE_DATE,ERROR_CODE,ERROR_MESSAGE,DB_LINK)
values(data.ID,data.REQUEST_XML,data.RESPONSE_XML,data.WEB_SERVICE_NAME,data.WEB_METHOD_NAME,data.CREATE_DATE,data.ERROR_CODE,data.ERROR_MESSAGE,'||DB_CONNECTION_NAME ||')';

DBMS_OUTPUT.PUT_LINE('lsql' || l_sql);

    DBMS_OUTPUT.PUT_LINE('lsql' || l_sql);

    execute immediate l_sql;

    END EXT_SOAP_MONITORING;

以下是我的 DBMS 结果:

     merge into TEMP_SOAP_MONITORING TSM
using (
   select * from
(select ID,REQUEST_XML,RESPONSE_XML,WEB_SERVICE_NAME,WEB_METHOD_NAME,CREATE_DATE,ERROR_CODE,ERROR_MESSAGE from
SOAP_MONITORING@FONIC_RETAIL  WHERE 
ID > (CASE WHEN FONIC_RETAIL = 'FONIC_RETAIL' THEN 201601071130573261 ELSE 201601071130573261 END) AND
WEB_SERVICE_NAME ='RatorWebShopService'  and WEB_METHOD_NAME = 'placeShopOrder') where rownum <=1000
) data
ON (TSM.ID = data.ID)
when not matched then
insert(ID,REQUEST_XML,RESPONSE_XML,WEB_SERVICE_NAME,WEB_METHOD_NAME,CREATE_DATE,ERROR_CODE,ERROR_MESSAGE,DB_LINK)
values(data.ID,data.REQUEST_XML,data.RESPONSE_XML,data.WEB_SERVICE_NAME,data.WEB_METHOD_NAME,data.CREATE_DATE,data.ERROR_CODE,data.ERROR_MESSAGE,FONIC_RETAIL)

【问题讨论】:

案例表达,不是案例陈述... 我猜你在THEN'之后缺少一个空格 我已经更正了它现在显示的 ID,但我仍然遇到同样的错误。它在合并查询中打印完整的案例语句。我只想从那个 case 表达式中获取 ID @Rahul 顺便说一句,为什么要使用两条 sql 语句从同一行获取两列?你知道你可以做到select col1, col2, ... into v1, v2, ... 对吗? 【参考方案1】:

除了@KevinEsche 指出的THEN 之后缺少的空格外,您还在case 语句之后缺少了一个AND(或OR):

<snip>
    || ' END)
WEB_SERVICE_NAME =''RatorWebShopService'' <snip> -- <---- missing and/or before web_service_name

所以,看看你的程序输出的合并语句,有两个问题跳出来,巧合地在同一行:

ID > (CASE WHEN DB_LINK = 'FONIC_RETAIL' THEN201601071130573261 ELSE  END) AND

您忘记在THEN' || LAST_SM_ID 中的THEN 之后添加空格,而且您还没有满足LAST_SM_ID_MB 为空。


我会这样写程序

procedure ext_soap_monitoring (in_db_link in varchar2)
as
  last_sm_id number := 0;
  last_capt_date date;
  last_sm_id_mb number := 0;
  last_capt_date_mb date;
  l_sql varchar2(5000);
  l_sql1 varchar2(5000);
  db_connection_name varchar2(100);

begin
  select db_link
  into   db_connection_name
  from   rator_monitoring_configuration.db_connection
  where  db_link = in_db_link;

  --DELETE FROM TEMP_SOAP_MONITORING table before inserting new data into this table
  execute immediate 'TRUNCATE TABLE TEMP_SOAP_MONITORING';

  -- first retrieve the last id (of the newest record) which has been imported at last extraction
  --FONIC
  select last_task_id, capturing_date
  into   last_sm_id, last_capt_date
  from   capturing
  where  db_table = 'TEMP_SOAP_MONITORING'
  and    db = 'FONIC_RETAIL';

  --MB
  select last_task_id, capturing_date
  into   last_sm_id_mb, last_capt_date_mb
  from   capturing
  where  db_table = 'TEMP_SOAP_MONITORING'
  and    db = 'MB_RETAIL';

  l_sql:=
'merge into temp_soap_monitoring tsm
using (select *
       from   (select id,
                      request_xml,
                      response_xml,
                      web_service_name,
                      web_method_name,
                      create_date,
                      error_code,
                      error_message
               from   soap_monitoring@'||db_connection_name||'
               where  id > (case when :db_connection_name = ''FONIC_RETAIL'' then ' || last_sm_id || ' else ' || last_sm_id_mb ||' end)
               and    web_service_name = ''RatorWebShopService''
               and    web_method_name = ''placeShopOrder'')
       where rownum <= 1000) data
on (tsm.id = data.id)
when not matched then
insert (id,
        request_xml,
        response_xml,
        web_service_name,
        web_method_name,
        create_date,
        error_code,
        error_message,
        db_link)
values (data.id,
        data.request_xml,
        data.response_xml,
        data.web_service_name,
        data.web_method_name,
        data.create_date,
        data.error_code,
        data.error_message,
        :db_connection_name)';

  dbms_output.put_line('lsql' || l_sql);

  execute immediate l_sql using db_connection_name, db_connection_name;

end ext_soap_monitoring;
/

注意使用绑定变量来删除变量在 sql 本身中的文字位置。我知道当你从表中选择时,你无法避免使用动态 sql 来添加数据库链接名称,但是你可以在其他地方使用绑定变量,这应该会让你的代码更不容易被 sql 注入。

另外,看看我是如何格式化合并语句的 - 是的,它在变量中占用了更多空间,但现在它的可读性要好得多!

【讨论】:

嗨 Bonist 我已经编辑了我的问题并添加了 DBMS 结果,正如你所说的正确,我在 case 语句之后缺少 AND 或 (OR),我已经更正了,但我仍然得到同样的错误。 你可以看到它在合并查询中打印case表达式 好的,我也会添加条件来检查空条件 您好 Boniest 我已经更新了我的 DBMS 结果。现在以前的错误不存在,但现在我收到错误为“TSM”。“DB_LINK”:无效标识符。我在 TEMP_SOAP_MONITORING 表中有 DB_LINK 列,但我仍然收到此错误。 太棒了!我还更新了我的答案,我将如何编写该程序,fwiw。

以上是关于在 where 子句中使用 case 语句的 Oracle Missing 关键字的主要内容,如果未能解决你的问题,请参考以下文章

Oracle SQL 不同的 where 子句与 case when

在 where 子句 SQL 中的 case 语句中使用参数

如何在 SQL Server 的 Where 子句中使用 case 语句

在 where 子句中使用 case 语句的 Oracle Missing 关键字

在 where 子句的 Case 语句中定义一个变量

用于返回结果集的 where 子句中的 case 语句包含空值