Oracle
 sql >> база данни >  >> RDS >> Oracle

Oracle PL/SQL - Итериране на имена на колони на локално дефинирана таблица

Ако използвате обект, създаден в обхвата на SQL, а не запис, създаден в обхвата на PL/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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да създадете съхранена процедура на Oracle, която може да връща конкретни обекти, както и всички обекти

  2. Функция TRUNC(дата) в Oracle

  3. SQL - Връща редове с повечето съвпадения на колони

  4. PL/SQL присвоява резултати от заявка към CLOB

  5. Как да тествате съхранена процедура на Oracle с тип връщане на RefCursor?