用于更新的动态SQL不起作用

用于更新的动态SQL不起作用,sql,oracle,plsql,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

请帮我找出这个问题的症结所在。下面是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;
执行程序:

更新:

这里是第二部分计数,但更新并没有发生。请帮我找到问题。

当你说更新没有发生时,你的意思是错误?在这种情况下,您应该发布该错误

在任何情况下,都不要将变量的名称与字段名完全一致。另一方面,你说的是set a=a

使用类似的方法来识别什么是变量或字段。使用你喜欢的任何符号

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;

代码中有一些不同的问题

首先,没有理由使用动态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但这是语句中的第一个bind变量时,它会被分配从using子句传入的第一个值。在本例中,您将deploy_status设置为app_ext_code中的值,where子句将app_ext_code列中的值与zone_env_code参数中的值进行比较,等等

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

但是,正如我所说,当静态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_代码引用都解析为表中的列,而不是参数。因此,实际上,您的更新是更新表中的每一行,并将deploy_status设置为表中的现有值,而不考虑传入的值。类似地,select count*将对表中的每一行进行计数,其中app_ext_代码、zone_env_代码和version_编号不为null,而不管它们是否具有您要传递的值

避免这种情况的最常见方法是为参数和局部变量使用一致的前缀或后缀,以区别于列的名称。例如,我使用p_u作为参数名的前缀,使用l_u作为局部变量的前缀。大概是

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; 

不过,这种方法生成的代码过于冗长,我不喜欢。

为什么要使用动态SQL?这里没有理由使用动态SQL。UPDATE语句有4个绑定变量,但它们有一些奇怪的名称。您是否希望:1始终是using子句中的第一个值?它不是-按值在语句中出现的顺序使用绑定值。命名绑定变量:1与命名绑定变量:foo或:fuzzyBunny没有多大影响。感谢您的回复和指导。您是否说更改名称将允许我执行查询?@JustinCave,即使静态sql也不起作用。请帮助检查更新的绑定变量。为变量使用列名是一种不好的做法。其中APP_EXT_CODE=APP_EXT_CODE。对于IN参数,如APP_EXT_CODE=IN_APP_EXT_CODE=IN_APP_CODE如果计数为1,那么为什么插入行?由于1>0,它应该转到更新块。您确定o/p吗?我不同意设计良好的应用程序中的语句,您几乎不需要动态SQL。在具有1000多个表的数据仓库应用程序中,您很乐意使用动态SQL@Wernfried-也许我们对几乎没有意义的东西意见不一。当然,在某些情况下,动态SQL在设计良好的应用程序中很有用。在某些情况下,它在构建仓库负载时很有用。但我认为这类应用是一个例外,它证明了这个规则,特别是在与至少PL/SQL相对较新的人交谈时。
   EXECUTE IMMEDIATE tmp_query_str
   USING deploy_status, APP_EXT_CODE,ZONE_ENV_CODE,VERSION_NUMBER;
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;
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;