动态SQL for Update不起作用

时间:2015-07-25 19:05:22

标签: sql oracle plsql

请帮我查一下这个查询有什么问题。以下是Oracle过程的代码片段。

create or replace PROCEDURE sp_Insert_ProfileStatus(
APP_EXT_CODE IN PROFILE_STATUS.APP_EXT_CODE%TYPE,
ZONE_ENV_CODE IN PROFILE_STATUS.ZONE_ENV_CODE%TYPE,
VERSION_NUMBER IN PROFILE_STATUS.VERSION_NUMBER%TYPE,
DEPLOY_STATUS IN PROFILE_STATUS.DEPLOY_STATUS%TYPE
)
IS
tmpCnt number;
tmp_query_str varchar2(2000);
BEGIN
tmp_query_str := 'select count(*) FROM PROFILE_STATUS WHERE APP_EXT_CODE =:1 '||
   ' AND ZONE_ENV_CODE =:2 AND VERSION_NUMBER =:3'; 
EXECUTE IMMEDIATE tmp_query_str
INTO tmpCnt
USING APP_EXT_CODE,ZONE_ENV_CODE,VERSION_NUMBER;
dbms_output.put_line('count:'||tmpCnt);
IF tmpCnt > 0 THEN
  tmp_query_str := 'UPDATE PROFILE_STATUS SET DEPLOY_STATUS =:4 WHERE APP_EXT_CODE =:1 '||
   ' AND ZONE_ENV_CODE =:2 AND VERSION_NUMBER =:3'; 
   EXECUTE IMMEDIATE tmp_query_str
   USING APP_EXT_CODE,ZONE_ENV_CODE,VERSION_NUMBER,DEPLOY_STATUS;
ELSE
  tmp_query_str := 'INSERT INTO PROFILE_STATUS(WPS_ID,APP_EXT_CODE,ZONE_ENV_CODE,'||
                   'VERSION_NUMBER,DEPLOY_STATUS)'||
                  'VALUES(SYS_GUID(),:1,:2,:3,:4)';
   EXECUTE IMMEDIATE tmp_query_str
   USING APP_EXT_CODE,ZONE_ENV_CODE,VERSION_NUMBER,DEPLOY_STATUS;
END IF;

执行程序:

EXEC sp_Insert_ProfileStatus('APP1','PROD','1.0.1','i');result ---count:1, one row is inserted.

EXEC sp_Insert_ProfileStatus('APP1','PROD','1.0.1','u');result ---count:1, no row is affected.

更新:

create or replace PROCEDURE sp_Insert_ProfileStatus(
APP_EXT_CODE IN PROFILE_STATUS.APP_EXT_CODE%TYPE,
ZONE_ENV_CODE IN PROFILE_STATUS.ZONE_ENV_CODE%TYPE,
VERSION_NUMBER IN PROFILE_STATUS.VERSION_NUMBER%TYPE,
DEPLOY_STATUS IN PROFILE_STATUS.DEPLOY_STATUS%TYPE
)
    IS
    tmpCnt number;    
    BEGIN
    SELECT COUNT(*) INTO tmpCnt FROM WAF1_PROFILE_STATUS WHERE APP_EXT_CODE =APP_EXT_CODE
        AND ZONE_ENV_CODE =ZONE_ENV_CODE AND VERSION_NUMBER =VERSION_NUMBER; 
    dbms_output.put_line('count:'||tmpCnt);
    IF tmpCnt > 0 THEN
      UPDATE WAF1_PROFILE_STATUS SET DEPLOY_STATUS =DEPLOY_STATUS WHERE APP_EXT_CODE =APP_EXT_CODE 
        AND ZONE_ENV_CODE =ZONE_ENV_CODE AND VERSION_NUMBER =VERSION_NUMBER;
    ELSE
      INSERT INTO WAF1_PROFILE_STATUS(WPS_ID,APP_EXT_CODE,ZONE_ENV_CODE,VERSION_NUMBER,DEPLOY_STATUS)
       VALUES(SYS_GUID(),APP_EXT_CODE,ZONE_ENV_CODE,VERSION_NUMBER,DEPLOY_STATUS);
    END IF;
    END sp_Insert_ProfileStatus;

这里的第二部分 count 来了,但没有发生更新。请帮我找到问题。

2 个答案:

答案 0 :(得分:1)

当您说update isnt happening表示错误时?在这种情况下,您应该发布该错误。

在任何情况下都不要将您的变量命名为字段名称。其他明智的你说set a = a

UPDATE WAF1_PROFILE_STATUS 
SET DEPLOY_STATUS = DEPLOY_STATUS 
WHERE 
    APP_EXT_CODE = APP_EXT_CODE 
AND ZONE_ENV_CODE = ZONE_ENV_CODE 
AND VERSION_NUMBER = VERSION_NUMBER;

使用类似的东西来识别变量或字段。使用你喜欢的任何符号。

UPDATE WAF1_PROFILE_STATUS 
SET DEPLOY_STATUS = i_DEPLOY_STATUS 
WHERE 
    APP_EXT_CODE = i_APP_EXT_CODE 
AND ZONE_ENV_CODE = i_ZONE_ENV_CODE 
AND VERSION_NUMBER = i_iVERSION_NUMBER;

答案 1 :(得分:0)

您的代码中存在一些不同的问题。

首先,没有理由使用动态SQL。如果在编译时不知道要执行的SQL语句,那么您只使用动态SQL - 例如,如果您需要确定在运行时更新哪个表。在设计良好的应用程序中,您几乎不需要动态SQL。

如果您要使用动态SQL,则update语句没有意义。 using子句中的值按照它们在语句中出现的顺序绑定到各种绑定变量,而不是基于绑定变量的名称。

   tmp_query_str := 'UPDATE PROFILE_STATUS SET DEPLOY_STATUS =:4 WHERE APP_EXT_CODE =:1 '||
   ' AND ZONE_ENV_CODE =:2 AND VERSION_NUMBER =:3'; 
   EXECUTE IMMEDIATE tmp_query_str
   USING APP_EXT_CODE,ZONE_ENV_CODE,VERSION_NUMBER,DEPLOY_STATUS;

因此,例如,当您说set deploy_status = :4但这是语句中的第一个绑定变量时,会从using子句中分配您传入的第一个值。在这种情况下,您要将deploy_status设置为app_ext_code中的值,您的where子句会将app_ext_code列中的值与该值中的值进行比较zone_env_code参数等

如果您出于某种原因要使用动态SQL,则需要在using子句中以正确的顺序指定参数

   EXECUTE IMMEDIATE tmp_query_str
   USING deploy_status, APP_EXT_CODE,ZONE_ENV_CODE,VERSION_NUMBER;

但是,就像我说的那样,当静态SQL完成工作时,在这里使用动态SQL没有任何意义。但是,当您摆脱动态SQL时,您遇到的问题是您的参数名称可能选择不当。大多数开发人员和项目确保参数和本地变量不共享数据库列的名称,否则通常会导致名称解析问题。

在你的第二个例子中,你写的是

UPDATE WAF1_PROFILE_STATUS 
   SET DEPLOY_STATUS =DEPLOY_STATUS 
 WHERE APP_EXT_CODE =APP_EXT_CODE       
   AND ZONE_ENV_CODE =ZONE_ENV_CODE 
   AND VERSION_NUMBER =VERSION_NUMBER;

您的目标可能是将app_ext_code列中的数据与作为app_ext_code参数传入的值进行比较。但那不是你的代码所做的。相反,两个app_ext_code引用都解析为表中的列而不是参数。实际上,您的更新实际上是更新表中的每一行,只是将deploy_status设置为表中的现有值,而不管您传入的值。同样,您的select count(*)将计算在内表格中app_ext_codezone_env_codeversion_numbernot null的所有行都是p_,无论它们是否具有您传入的值。

避免这种情况的最常见方法是对参数和局部变量使用一致的前缀或后缀,以区别于列的名称。例如,我使用l_作为参数名称的前缀,使用create or replace PROCEDURE sp_Insert_ProfileStatus( P_APP_EXT_CODE IN PROFILE_STATUS.APP_EXT_CODE%TYPE, P_ZONE_ENV_CODE IN PROFILE_STATUS.ZONE_ENV_CODE%TYPE, P_VERSION_NUMBER IN PROFILE_STATUS.VERSION_NUMBER%TYPE, P_DEPLOY_STATUS IN PROFILE_STATUS.DEPLOY_STATUS%TYPE ) IS tmpCnt number; BEGIN SELECT COUNT(*) INTO tmpCnt FROM WAF1_PROFILE_STATUS WHERE APP_EXT_CODE = P_APP_EXT_CODE AND ZONE_ENV_CODE = P_ZONE_ENV_CODE AND VERSION_NUMBER = P_VERSION_NUMBER; dbms_output.put_line('count:'||tmpCnt); IF tmpCnt > 0 THEN UPDATE WAF1_PROFILE_STATUS SET DEPLOY_STATUS = P_DEPLOY_STATUS WHERE APP_EXT_CODE = P_APP_EXT_CODE AND ZONE_ENV_CODE = P_ZONE_ENV_CODE AND VERSION_NUMBER = P_VERSION_NUMBER; ELSE INSERT INTO WAF1_PROFILE_STATUS(WPS_ID, APP_EXT_CODE, ZONE_ENV_CODE, VERSION_NUMBER, DEPLOY_STATUS) VALUES(SYS_GUID(), P_APP_EXT_CODE, P_ZONE_ENV_CODE, P_VERSION_NUMBER, P_DEPLOY_STATUS); END IF; END sp_Insert_ProfileStatus; 作为局部变量的前缀。像

这样的东西
  SELECT COUNT(*) 
    INTO tmpCnt 
    FROM WAF1_PROFILE_STATUS 
   WHERE APP_EXT_CODE = sp_Insert_ProfileStatus.APP_EXT_CODE
     AND ZONE_ENV_CODE = sp_Insert_ProfileStatus.ZONE_ENV_CODE 
     AND VERSION_NUMBER = sp_Insert_ProfileStatus.VERSION_NUMBER; 

如果您确实希望参数名称与列名相同,您还可以通过在其前面添加过程名称来明确限定所有代码中的参数名称

{{1}}

但是,这种方法会产生对我来说过于冗长的代码。