sql >> データベース >  >> RDS >> Oracle

Oracle PL/SQL-ローカルで定義された表の列名を反復処理します

    PL / SQLスコープで作成されたレコードではなく、SQLスコープで作成されたオブジェクトを使用する場合は、ANYDATAを使用できます。 属性とその値を確認します。

    たとえば、タイプが次の場合:

    CREATE TYPE book is OBJECT(
      title           varchar(50),
      author          varchar(50),
      subject         varchar(100),
      book_id         number,
      first_published DATE
    );
    
    CREATE TYPE table_of_books IS TABLE OF book;
    

    次に、パッケージを作成できます:

    CREATE PACKAGE reflection IS
      TYPE type_info IS RECORD(
        prec        PLS_INTEGER,
        scale       PLS_INTEGER,
        len         PLS_INTEGER,
        csid        PLS_INTEGER,
        csfrm       PLS_INTEGER,
        schema_name VARCHAR2(30),
        type_name   VARCHAR2(30),
        version     VARCHAR2(100),
        count       PLS_INTEGER
      );
    
      TYPE attr_info IS RECORD(
        prec           PLS_INTEGER,
        scale          PLS_INTEGER,
        len            PLS_INTEGER,
        csid           PLS_INTEGER,
        csfrm          PLS_INTEGER,
        attr_elt_type  ANYTYPE,
        aname          VARCHAR2(30)
      );
    
      FUNCTION get_size(
        p_anydata IN ANYDATA
      ) RETURN PLS_INTEGER;
    
      FUNCTION get_attr_name_at(
        p_anydata IN ANYDATA,
        p_index   IN PLS_INTEGER DEFAULT 1
      ) RETURN VARCHAR2;
    
      FUNCTION get_attr_value_at(
        p_anydata IN ANYDATA,
        p_index   IN PLS_INTEGER DEFAULT 1
      ) RETURN VARCHAR2;
    END;
    /
    

    パッケージ本体付き:

    CREATE PACKAGE BODY reflection IS
      DEBUG BOOLEAN := FALSE;
    
      FUNCTION get_type(
        p_anydata IN ANYDATA
      ) RETURN ANYTYPE
      IS
        v_typeid    PLS_INTEGER;
        v_anytype   ANYTYPE;
        v_type_info REFLECTION.TYPE_INFO;
      BEGIN
        v_typeid := p_anydata.GetType( typ => v_anytype );
        RETURN v_anytype;
      END;
    
      FUNCTION get_info(
        p_anytype IN ANYTYPE
      ) RETURN type_info
      IS
        v_typeid    PLS_INTEGER;
        v_type_info REFLECTION.TYPE_INFO;
      BEGIN
        v_typeid := p_anytype.GetInfo (
          v_type_info.prec, 
          v_type_info.scale,
          v_type_info.len, 
          v_type_info.csid,
          v_type_info.csfrm,
          v_type_info.schema_name, 
          v_type_info.type_name, 
          v_type_info.version,
          v_type_info.count
        );
    
        IF v_typeid <> DBMS_TYPES.TYPECODE_OBJECT THEN
          RAISE_APPLICATION_ERROR( -20000, 'Not an object.' );
        END IF;
    
        RETURN v_type_info;
      END;
    
      FUNCTION get_size(
        p_anydata IN ANYDATA
      ) RETURN PLS_INTEGER
      IS
      BEGIN
        RETURN Get_Info( Get_Type( p_anydata ) ).COUNT;
      END;
      
      FUNCTION get_attr_name_at(
        p_anydata IN ANYDATA,
        p_index   IN PLS_INTEGER DEFAULT 1
      ) RETURN VARCHAR2
      IS
        v_anydata     ANYDATA := p_anydata;
        v_anytype     ANYTYPE;
        v_type_info   REFLECTION.TYPE_INFO;
        v_output      VARCHAR2(4000);
        v_attr_typeid PLS_INTEGER;
        v_attr_info   REFLECTION.ATTR_INFO;
      BEGIN
        v_anytype := Get_Type( v_anydata );
        v_type_info := Get_Info( v_anytype );
        
        IF p_index < 1 OR p_index > v_type_info.COUNT THEN
          RETURN NULL;
        END IF;
        
        v_anydata.PIECEWISE;
        v_attr_typeid := v_anytype.getAttrElemInfo(
          pos            => p_index,
          prec           => v_attr_info.prec,
          scale          => v_attr_info.scale,
          len            => v_attr_info.len,
          csid           => v_attr_info.csid,
          csfrm          => v_attr_info.csfrm,
          attr_elt_type  => v_attr_info.attr_elt_type,
          aname          => v_attr_info.aname
        );
        RETURN v_attr_info.aname;
      END;
            
      FUNCTION get_attr_value_at(
        p_anydata IN ANYDATA,
        p_index   IN PLS_INTEGER DEFAULT 1
      ) RETURN VARCHAR2
      IS
        v_anydata   ANYDATA := p_anydata;
        v_anytype   ANYTYPE;
        v_type_info REFLECTION.TYPE_INFO;
        v_output    VARCHAR2(4000);
      BEGIN
        v_anytype := Get_Type( v_anydata );
        v_type_info := Get_Info( v_anytype );
        
        IF p_index < 1 OR p_index > v_type_info.COUNT THEN
          RETURN NULL;
        END IF;
        
        v_anydata.PIECEWISE;
        
        FOR i IN 1 .. p_index LOOP
          DECLARE
            v_attr_typeid PLS_INTEGER;
            v_attr_info   REFLECTION.ATTR_INFO;
            v_result_code PLS_INTEGER;
          BEGIN
            v_attr_typeid := v_anytype.getAttrElemInfo(
              pos            => i,
              prec           => v_attr_info.prec,
              scale          => v_attr_info.scale,
              len            => v_attr_info.len,
              csid           => v_attr_info.csid,
              csfrm          => v_attr_info.csfrm,
              attr_elt_type  => v_attr_info.attr_elt_type,
              aname          => v_attr_info.aname
            );
    
            IF DEBUG THEN
              DBMS_OUTPUT.PUT_LINE(
                'Attribute ' || i || ': '
                || v_attr_info.aname
                || ' (type ' || v_attr_typeid || ')'
              );
            END IF;
    
            CASE v_attr_typeid
            WHEN DBMS_TYPES.TYPECODE_NUMBER THEN
              DECLARE
                v_value NUMBER;
              BEGIN
                v_result_code := v_anydata.GetNumber( v_value );
                IF i = p_index THEN
                  RETURN TO_CHAR( v_value );
                END IF;
              END;
             WHEN DBMS_TYPES.TYPECODE_VARCHAR2 THEN
              DECLARE
                v_value VARCHAR2(4000);
              BEGIN
                v_result_code := v_anydata.GetVarchar2( v_value );
                IF i = p_index THEN
                  RETURN v_value;
                END IF;
              END;
             WHEN DBMS_TYPES.TYPECODE_DATE THEN
              DECLARE
                v_value DATE;
              BEGIN
                v_result_code := v_anydata.GetDate( v_value );
                IF i = p_index THEN
                  RETURN TO_CHAR( v_value, 'YYYY-MM-DD HH24:MI:SS' );
                END IF;
              END;
            ELSE
              NULL;
            END CASE;
          END;
        END LOOP;
        RETURN NULL;
      END;
    END;
    /
    

    次に、値を取得するためのコードは次のようになります。

    DECLARE
       list_of_books table_of_books;
       idx           PLS_INTEGER := 1;
       p_anydata     ANYDATA;
       p_attr_name   VARCHAR2(30);
       p_attr_value  VARCHAR2(4000);
    BEGIN
      dbms_output.enable;
      list_of_books := table_of_books(
        book(
          'First book',
          'Me',
          'Simple Ones',
          94321,
          DATE '1970-01-01'
        ),
        book(
          'Second book',
          'You',
          'Intermediate Ones',
          55555,
          DATE '2020-01-01'
        ),
        book(
          'Third book',
          NULL,
          'Advanced Ones',
          77777,
          DATE '2099-12-31' + INTERVAL '0 23:59:59' DAY TO SECOND
        )
      );
      
      FOR book_no IN 1 .. list_of_books.COUNT LOOP
        p_anydata := ANYDATA.ConvertObject( list_of_books(book_no) );
        DBMS_OUTPUT.PUT_LINE( 'Book ' || book_no || ':' );
        FOR attr_no IN 1 .. REFLECTION.get_size( p_anydata ) LOOP
          p_attr_name  := REFLECTION.get_attr_name_at( p_anydata, attr_no );
          p_attr_value := REFLECTION.get_attr_value_at( p_anydata, attr_no );
          DBMS_OUTPUT.PUT_LINE( '  ' || p_attr_name || ': ' || p_attr_value );
        END LOOP;
      END LOOP;
    END;
    /
    

    どの出力:

    db <> fiddle こちら



    1. VPS/専用サーバーでPostgreSQLを入手する方法

    2. cx_Oracle接続タイムアウト

    3. 混合エンコーディングタイプを含むSQL_ASCIIからUTF-8へのPostgresqlデータベースの変換

    4. mysql接続を開いたままにします