ODP.Net Merge ExecuteNonQuery() Affected Row Count??????????????? :: iopeni - Think of C#

어느날 아는 동생으로 부터... (나 보다 훨 스킬이 좋은 개발자다.) ODP.Net을 이용하여 Merge 쿼리를 작성 하면... 반환값이 언제나 무조건... -1이 나온다는 제보를 받았다.


물론 동생은 이걸 해결 하기 위하여 정말 열심히 검색을 했고 결국 얻어 낼 수 있는 정보는 오라클의 버그라는 것이었다.


이거 참... 나보다 훨 스킬이 좋은 동생이기에 이 이야기를 의심할 수 조차 없었다. 그 동생이 그렇다면 그런것이기에...


그럼 이걸 어떻게 해결 해야 할까? 오라클이 이런 버그를 만들어 놓고.. 패치는 하지 않은 걸까? 정말 열심히 찾았다... 그런데... 발견 할 수 있는 모든 글은.... 몇개 되지도 않았고 전부 다들 이걸 어떻게 해결 해야 할지에 대한 문의 와.. 간단하게 PL/SQL을 이용하여 sql%rowcount 를 활용하라는 내용이 전부 였다.


해결 방법 : 닭치고.... 

System.Data.OracleClient

을 쓰면 된다......


이렇게 이야기 하면... 정말 속 편하겠다... 근데 이눔의 호기심이 젠장..... 그냥 넘길 수 없었다.


일단 System.Data.OracleClient에 속한 ExecuteNonQuery 는 잘 동작하니까..... 마이크로소프트는 뭐라고 하는지 MSDN을 봤다...


https://msdn.microsoft.com/ko-kr/library/vstudio/system.data.oracleclient.oraclecommand.executenonquery(v=vs.100).aspx


물론 여기서 중요한 이야기는 


UPDATE, INSERT 및 DELETE 문의 경우, 반환 값은 해당 명령의 영향을 받는 행의 수입니다. CREATE TABLE 및 DROP TABLE 문의 경우 반환 값은 0입니다. 다른 형식의 문의 경우에는 반환 값이 -1입니다.


이 이야기가 되시겠다...


자 그렇다면.... ODP.Net은 그럼 Merge문을 다른 형식의 문장으로 보고 있다는 말인가? 이런.. 퐝당한..... 


Oracle.DataAccess.dll을 .Net Reflector로 까 봤다...


---- 중략 ----

if ((errCode != -1) && (((this.m_pOpoSqlValCtx.CommandType == 4) || 

                           (this.m_pOpoSqlValCtx.CommandType == 2)) || 

                           (this.m_pOpoSqlValCtx.CommandType == 3)))

{

    this.m_rowsAffected = this.m_pOpoSqlValCtx.RowsAffected;

    

    if ((this.m_arrayBindCount > 1) && (null != this.m_pOpoSqlValCtx.RowsAffectedForArrayBind))

    {

        this.m_rowsAffectedPerBind = new long[this.m_pOpoSqlValCtx.NoOfRowsInRowsAffectedForArrayBind];

        for (num7 = 0; num7 < this.m_pOpoSqlValCtx.NoOfRowsInRowsAffectedForArrayBind; num7++)

        {

           this.m_rowsAffectedPerBind[num7] = this.m_pOpoSqlValCtx.RowsAffectedForArrayBind[num7];

        }

     }

 }

 else

 {

     this.m_rowsAffected = -1;

 }


---- 중략 ----


보셨는가? 명령어 타입이.. 딱 3가지... 4, 2, 3이 아니면 무조건... 반환값은 -1 이다. 뭐 이쯤 되면 대충 짐작으로도 저건.. Insert, Delete, Update 쯤 될꺼란 짐작 될것이다.


아 퐝당~~~~


그럼 System.Data.OracleClient.dll 에 있는 코드는 어떻게 되지????? 그냥 넘길 수 없다. 

역시나 .Net Reflector 로 까 봤다.. 


---- 중략 ----


int num = -1;

            try

            {

                try

                {

                    ArrayList resultParameterOrdinals = new ArrayList();

                    statementHandle = this.GetStatementHandle();

                    this.Execute(statementHandle, CommandBehavior.Default, needRowid, out rowidDescriptor, out resultParameterOrdinals);

                    if (resultParameterOrdinals != null)

                    {

                        num = 0;

                        foreach (int num2 in resultParameterOrdinals)

                        {

                            OracleParameter parameter = this._parameterCollection[num2];

                            if (OracleType.Cursor != parameter.OracleType)

                            {

                                num += (int) parameter.Value;

                            }

                        }

                        return num;

                    }

                    if (OCI.STMT.OCI_STMT_SELECT != this._statementType)

                    {

                        statementHandle.GetAttribute(OCI.ATTR.OCI_ATTR_ROW_COUNT, out num, this.ErrorHandle);

                    }

                    return num;

                }

                finally

                {

                    if (statementHandle != null)

                    {

                        this.ReleaseStatementHandle(statementHandle);

                    }

                }

            }

            catch

            {

                throw;

            }

            return num;


---- 중략 ----


뭐 대충 보면... 처음에 -1로 초기화 하고 실행 후 파라미터 반복하며,,,, 조건이 맞으면 하나씩 카운트 한 다음에 반환하는 것을 볼 수 있다.. 


Oracle 프로그램 졸라 잘 만드는지 알았더니.. 이거 참... 실망이다...  이런... 멍청한 것들...


그럼 이걸 어떻게 해결 하면 되는 건지 아니 궁금하다 하면 좀 그럴것이다... 뭐 결국 업데이트 갯수가 몇개가 되었다 보다 적용 된게 있느냐 없느냐가 관건이겠으므로 나는 다음과 같은 방법을 생각 했다..



using (OracleConnection conn = new OracleConnection(oradb))

            {

                conn.Open();

                OracleCommand cmd = new OracleCommand();

                cmd.Connection = conn;

                cmd.CommandText = @"BEGIN

                                       MERGE INTO employee

                                            USING DUAL

                                               ON (companyid = 1235)

                                       WHEN MATCHED

                                       THEN

                                          UPDATE SET empname = '홍길동'

                                       WHEN NOT MATCHED

                                       THEN

                                          INSERT     (companyid, empname, phone)

                                              VALUES ('1111', 'choi', '111111');


                                       IF SQL%ROWCOUNT = 0

                                       THEN

                                          RAISE_APPLICATION_ERROR(-20801, '에러 났어요.');

                                       END IF;

                                    END;

                                    ";

                

                cmd.CommandType = CommandType.Text;


                try

                {

                    int dr = cmd.ExecuteNonQuery();

                }

                catch (OracleException ex)

                {

                    if (ex.Number == 20801)

                    {

                        System.Console.WriteLine(ex.Message);

                    }

                    else

                    {

                        throw;

                    }

                }

            }


뭐 대충 이렇게 말이다....... 만약 하나의 로우도 변경 되는 것이 없다면 오라클이 오류를 뱉을 것이다... 젠장 나 못해먹겠어... 라고 말이지......



BEGIN

   MERGE INTO employee

        USING DUAL

           ON (companyid = 1235)

   WHEN MATCHED

   THEN

      UPDATE SET empname = '홍길동'

   WHEN NOT MATCHED

   THEN

      INSERT     (companyid, empname, phone)

          VALUES ('1111', 'choi', '111111');


   IF SQL%ROWCOUNT > 0

   THEN

      RAISE_APPLICATION_ERROR(-20801, SQL%ROWCOUNT);

   END IF;

END;


이렇게 하면 뭐... 적용된 갯수를 Exeception으로 잡아 낼 수도 있겠다... 에효... ㅜ,.ㅜ;;; 이런 꼼수는 싫은 데.....


오라클은 당췌 이 버그는 언제 수정해 줄껀지..... 알고는 있으려나?????????

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,