programing

DBMS_OUTPUT에서 출력 버퍼를 가져오는 중입니다.C#의 GET_LINES

lastmoon 2023. 9. 4. 20:33
반응형

DBMS_OUTPUT에서 출력 버퍼를 가져오는 중입니다.C#의 GET_LINES

저는 그 결과물을 얻으려고 노력하고 있습니다.DBMS_OUTPUT.PUT_LINE()C#을 통한 익명 PL/SQL 블록의 메서드입니다.여기서 다른 관련 질문 몇 가지를 살펴보았지만 여전히 문제가 있습니다.익명 블록 실행의 반환 코드가 반환됩니다.-1서류에 근거하여 정확해야 합니다.

내가 설정하는 중입니다.DBMS_OUTPUT.ENABLE()로.NULL특정 버퍼 크기를 설정하지 않으려면 다음을 사용합니다.DBMS_OUTPUT.GET_LINES()버퍼에서 줄을 가져오는 방법입니다.

버퍼에서 아무것도 반환하지 않습니다(공백).OracleString[]) 및 반환0lines. 제 익명 PL/SQL 블록은 이렇게 간단하지만, 어떤 경우에도 작동합니다.

DECLARE
    lvsName VARCHAR2(6) := 'Oracle';
BEGIN
    DBMS_OUTPUT.PUT_LINE('Do you see me?');
    DBMS_OUTPUT.PUT_LINE('My name is: ' || lvsName);    
END;

제가 무엇을 빠뜨리고 있나요?

using (OracleDataAdapter oda = new OracleDataAdapter())
using (OracleCommand cmd = new OracleCommand(sql, _connection))
{
    // Execute anonymous PL/SQL block
    cmd.CommandType = CommandType.Text;
    var res = cmd.ExecuteNonQuery();

    // Set output Buffer
    cmd.CommandText = "BEGIN DBMS_OUTPUT.ENABLE(NULL); END;";
    cmd.CommandType = CommandType.Text;
    cmd.ExecuteNonQuery();

    // Get output
    cmd.CommandText = "BEGIN DBMS_OUTPUT.GET_LINES(:outString, :numLines); END;";
    cmd.CommandType = CommandType.Text;
    cmd.Parameters.Clear();
    cmd.Parameters.Add(new OracleParameter("outString", OracleDbType.Varchar2, int.MaxValue, ParameterDirection.Output));
    cmd.Parameters["outString"].CollectionType = OracleCollectionType.PLSQLAssociativeArray;
    cmd.Parameters["outString"].Size = sql.Length;
    cmd.Parameters["outString"].ArrayBindSize = new int[sql.Length];
    cmd.Parameters.Add(new OracleParameter("numLines", OracleDbType.Int32, ParameterDirection.InputOutput));
    cmd.Parameters["numLines"].Value = 10; // Get 10 lines
    cmd.ExecuteNonQuery();

     int numLines = Convert.ToInt32(cmd.Parameters["numLines"].Value.ToString());
     string outString = string.Empty;

     // Try to get more lines until there are zero left
     while (numLines > 0)
     {
         for (int i = 0; i < numLines; i++)
         {
             OracleString s = (OracleString)cmd.Parameters["outString"].Value;
             outString += s.ToString();
         }

         cmd.ExecuteNonQuery();
         numLines = Convert.ToInt32(cmd.Parameters["numLines"].Value.ToString());
     }

     return outString;
}

코드의 주요 문제는 출력 버퍼의 각 요소에 대한 바인딩 크기를 설정하지 않았다는 것입니다.또한 결과를 검색할 때 출력 버퍼를 제대로 인덱싱하지 않았습니다.마지막으로, 실행 순서도 역할을 합니다. 익명 코드 블록을 실행하기 전에 먼저 출력을 활성화해야 합니다.모든 변경 사항은 다음 MCVE에 설명되어 있습니다.작동하기 위해 필요한 변경 사항만 수행되었습니다.

static void Main(string[] args)
{
    string str = "User Id=xxx; password=xxx; Data Source=localhost:1521/xxx;";
    string sql = @"DECLARE lvsName VARCHAR2(6) := 'Oracle'; BEGIN  DBMS_OUTPUT.PUT_LINE('Do you see me?'); DBMS_OUTPUT.PUT_LINE('My name is: ' || lvsName); END;";

    OracleConnection _connection = new OracleConnection(str);

    try
    {
        _connection.Open();

        //adapter not being used
        //using (OracleDataAdapter oda = new OracleDataAdapter())

        using (OracleCommand cmd = new OracleCommand(sql, _connection))
        {
            // First enable buffer output
            // Set output Buffer
            cmd.CommandText = "BEGIN DBMS_OUTPUT.ENABLE(NULL); END;";
            cmd.CommandType = CommandType.Text;
            cmd.ExecuteNonQuery();

            // Then execute anonymous block
            // Execute anonymous PL/SQL block
            cmd.CommandText = sql;
            cmd.CommandType = CommandType.Text;
            var res = cmd.ExecuteNonQuery();


            // Get output
            cmd.CommandText = "BEGIN DBMS_OUTPUT.GET_LINES(:outString, :numLines); END;";
            cmd.CommandType = CommandType.Text;

            cmd.Parameters.Clear();

            cmd.Parameters.Add(new OracleParameter("outString", OracleDbType.Varchar2, int.MaxValue, ParameterDirection.Output));
            cmd.Parameters["outString"].CollectionType = OracleCollectionType.PLSQLAssociativeArray;
            cmd.Parameters["outString"].Size = sql.Length;
            cmd.Parameters["outString"].ArrayBindSize = new int[sql.Length];

            // set bind size for each array element
            for (int i = 0; i < sql.Length; i++)
            {
                cmd.Parameters["outString"].ArrayBindSize[i] = 32000;
            }


            cmd.Parameters.Add(new OracleParameter("numLines", OracleDbType.Int32, ParameterDirection.InputOutput));
            cmd.Parameters["numLines"].Value = 10; // Get 10 lines
            cmd.ExecuteNonQuery();

            int numLines = Convert.ToInt32(cmd.Parameters["numLines"].Value.ToString());
            string outString = string.Empty;

            // Try to get more lines until there are zero left
            while (numLines > 0)
            {
                for (int i = 0; i < numLines; i++)
                {
                    // use proper indexing here
                    //OracleString s = (OracleString)cmd.Parameters["outString"].Value;
                    OracleString s = ((OracleString[])cmd.Parameters["outString"].Value)[i];
                    outString += s.ToString();

                    // add new line just for formatting
                    outString += "\r\n";
                }

                cmd.ExecuteNonQuery();
                numLines = Convert.ToInt32(cmd.Parameters["numLines"].Value.ToString());
            }

            Console.WriteLine(outString);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }

    _connection.Close();
    _connection.Dispose();

    Console.WriteLine("Press RETURN to exit.");
    Console.ReadLine();
}

출력 결과는 다음과 같습니다.

Do you see me?
My name is: Oracle

Press RETURN to exit.

솔루션에 대한 좋은 근거를 제공한 jsanalytics의 위 답변에 감사드립니다.하지만 위 솔루션에는 몇 가지 문제가 있는데, 대부분은 sql 사용과 관련이 있습니다.길이는 말이 안 되는 곳이 많습니다.다음은 몇 가지 문제를 해결하는 재사용 가능한 솔루션입니다.

using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;

namespace MyNamespace
{
    public static class DbmsOutputHelper
    {
        public const int DefaultReadBatchSize = 10;

        public static void EnableDbmsOutput(this OracleConnection conn)
        {
            using (var cmd = conn.CreateCommand())
            {
                cmd.CommandText = "DBMS_OUTPUT.ENABLE";
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.ExecuteNonQuery();
            }
        }

        public static void DisableDbmsOutput(this OracleConnection conn)
        {
            using (var cmd = conn.CreateCommand())
            {
                cmd.CommandText = "DBMS_OUTPUT.DISABLE";
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.ExecuteNonQuery();
            }
        }

        public static List<string> ReadDbmsOutput(this OracleConnection conn, int readBatchSize = DefaultReadBatchSize)
        {
            if (readBatchSize <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(readBatchSize), "must be greater than zero");
            }

            using (var cmd = conn.CreateCommand())
            {
                cmd.CommandText = "DBMS_OUTPUT.GET_LINES";
                cmd.CommandType = CommandType.StoredProcedure;

                var linesParam = cmd.Parameters.Add(new OracleParameter("lines", OracleDbType.Varchar2, int.MaxValue, ParameterDirection.Output));
                linesParam.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
                linesParam.Size = readBatchSize;
                linesParam.ArrayBindSize = Enumerable.Repeat(32767, readBatchSize).ToArray();   // set bind size for each array element

                var numLinesParam = cmd.Parameters.Add(new OracleParameter("numlines", OracleDbType.Int32, ParameterDirection.InputOutput));

                var result = new List<string>();
                int numLinesRead;

                do
                {
                    numLinesParam.Value = readBatchSize;
                    cmd.ExecuteNonQuery();
                    numLinesRead = ((OracleDecimal)numLinesParam.Value).ToInt32();

                    var values = (OracleString[])linesParam.Value;

                    for (int i = 0; i < numLinesRead; i++)
                    {
                        result.Add(values[i].ToString());
                    }

                } while (numLinesRead == readBatchSize);

                return result;
            }
        }
    }
}

제가 보기엔 당신이 순서를 잘못 알고 있는 것 같은데요...

// Execute anonymous PL/SQL block
cmd.CommandType = CommandType.Text;
var res = cmd.ExecuteNonQuery();

// Set output Buffer
cmd.CommandText = "BEGIN DBMS_OUTPUT.ENABLE(NULL); END;";
cmd.CommandType = CommandType.Text;
cmd.ExecuteNonQuery();

// Get output
cmd.CommandText = "BEGIN DBMS_OUTPUT.GET_LINES(:outString, :numLines); END;";

설정(활성화) 사이에DBMS_OUTPUT다음을 사용하여 출력을 얻습니다.GET_LINES쓰기 명령어가 되어야 하지만, 대신에 가장 먼저 실행하는 명령어입니다.

순서를 바꿔보세요.제가 안 해봐서 잘 되는지 알려주세요 (C#에 익숙하지 않아서요...자바로 가지고 있습니다.).

저는 C#을 말할 수 없지만 numLines 변수에 값을 할당하는 코드를 볼 수 없습니다.

  DBMS_OUTPUT.GET_LINES (
   lines       OUT     CHARARR,
   numlines    IN OUT  INTEGER);

plsql의 예:

DECLARE
   v_array       DBMS_OUTPUT.CHARARR;
   v_lines   NUMBER;
BEGIN
   DBMS_OUTPUT.PUT_LINE ('aaaaa');
   DBMS_OUTPUT.put_line ('bbbb');
   DBMS_OUTPUT.put_line ('ccccc');
   v_lines := 1000; -- Number of lines you want to retrieve from the buffer.  
   DBMS_OUTPUT.GET_LINES (v_array, v_lines);

   DBMS_OUTPUT.put_line(v_lines); -- Lines retrieved from buffer.
   FOR idx IN nvl(v_array.FIRST,1) .. nvl(v_array.LAST,-1)
   LOOP
      DBMS_OUTPUT.put_line (v_array (idx));
   END LOOP;
END;

언급URL : https://stackoverflow.com/questions/53402356/getting-output-buffer-from-dbms-output-get-lines-in-c-sharp

반응형