J'utilise du SQL dynamique où j'utilise dynamiquement la valeur du nom de la colonne à lier et sa valeur à lier
OLD CODE
<Inner Loop> FOR j IN lvaMappingTab.FIRST..lvaMappingTab.LAST LOOP lvsColForBinding := ':'||lvaMappingTab(j).MstRptColCds; lvsColValForBind := 'lvrCurDBOBJDTL(i).'||lvaMappingTab(j).RptColCd; DBMS_SQL.BIND_VARIABLE ( lvnInsertCursorId,lvsColForBinding, lvsColValForBind); END LOOP;
Au lieu d'écrire BIND_VARIABLE pour 100 fois, je veux accéder dynamiquement à la valeur de la collection. Je suis capable de récupérer la valeur des colonnes de manière dynamique, qui doivent être bind (lvsColForBinding), mais la valeur de lvsColValForBind arrive en tant que 'lvrCurDBOBJDTL (i) .DBONAME', 'lvrCurDBOBJDTL (i) .DBOTYPE' et même pour le reste des 98 colonnes,
<Outer Loop> FOR i IN lvaDBOBJDTLRecTab.FIRST .. lvaDBOBJDTLRecTab.LAST LOOP DBMS_SQL.BIND_VARIABLE ( lvnInsertCursorId, ':RTTEXT2VC100', lvaDBOBJDTLRecTab(i).DBONAME ); DBMS_SQL.BIND_VARIABLE ( lvnInsertCursorId, ':RTTEXT3VC100', lvaDBOBJDTLRecTab(i).DBOTYPE ); 3. . . . 100 END LOOP;
quand DBMS_SQL.BIND_VARIABLE est exécuté pour chaque ligne, comme mentionné précédemment Column to be bind est correct mais la valeur doit être bind, à la place de venir comme valeur de 'XYZ' = lvrCurDBOBJDTL (i) .DBONAME il vient comme ceci entre guillemets simples 'lvrCurDBOBJDTL (i) .DBONAME' idem pour toutes les colonnes. comment pouvons-nous extraire la valeur de chaque élément dans la boucle interne. quelle étape devons-nous faire pour récupérer la valeur de lvsColValForBind?
Lors du débogage via SQLDEveloper Watches, je peux voir le nom, la valeur et le type de l'élément, en ajoutant et en double-cliquant sur la variable d'enregistrement plsql quel est le SQL derrière cela, pouvons-nous l'utiliser dans le codage?
3 Réponses :
Lorsque vous appelez bind_variable
, vous liez une valeur réelle à un espace réservé. Donc, si vous fournissez une chaîne qui est le nom de votre variable, eh bien, cette chaîne est la valeur liée à l'espace réservé.
Si le tableau contient ces valeurs, alors faites simplement référence à l'élément du tableau et non au nom de cet élément, comme dans:
DBMS_SQL.BIND_VARIABLE ( lvnInsertCursorId, lvaMappingTab(j).MstRptColCds, lvrCurDBOBJDTL(i).lvaMappingTab(j).RptColCd);
Mais je suis presque sûr que ce n'est pas ce vous y êtes. J'espère que cela vous aidera!
J'ai essayé cela aussi, cela donne une erreur: PLS-00302: Le composant 'lvaMappingTab' doit être déclaré. Comme étape suivante, j'ai déclaré «lvaMappingTab» au niveau du package. Même erreur PLS-00302. C’est la raison pour laquelle j’essayais d’utiliser la fonctionnalité / SQL qui est utilisé par SQLDEVELOPER lors de la génération de la valeur des éléments de collection dans la fenêtre «WATCHES». il montre le nom de l'élément, la valeur de l'élément et le type de l'élément (+ plus de détails) Similaire à cela, si je peux accéder aux éléments de manière dynamique, je devrais pouvoir comparer et faire correspondre les valeurs de la variable de liaison.
Ma première recommandation est que vous utilisiez du SQL dynamique pour générer beaucoup de code stupide au lieu d'utiliser une petite quantité de PL / SQL intelligent. Si la génération de code ne fonctionne pas, vous pouvez utiliser ANYDATA et ANYTYPE pour créer une réflexion PL / SQL pour parcourir dynamiquement les éléments d'un enregistrement au moment de l'exécution. > écrivez BIND_VARIABLE 100 fois, mais créez un petit programme pour générer les 100 lignes de code pour vous. Si les données proviennent finalement d'une table et vont dans une autre table, l'entrée et la sortie peuvent être prévisibles sur la base de vues de dictionnaire de données comme DBA_TAB_COLUMNS.
Espérons qu'une petite requête comme celle-ci pourrait aider à générer tout le code pour une seule table:
1 A 2 B
Ensuite, vous pouvez copier-coller la sortie dans le bloc PL / SQL. Vous voudrez probablement aussi un avertissement, comme "ne pas modifier, ce code est généré automatiquement par la procédure CODE_TRON_2000".
Cette approche ne fonctionnera que si le code PL / SQL est prévisible, basé sur les données dictionnaire ou d'autres métadonnées.
Il n'y a pas de réflexion PL / SQL pure pour les types PL / SQL * mais il existe une solution simple si vous êtes prêt à créer le les types d'enregistrement comme des objets SQL à la place. Si tous vos enregistrements PL / SQL sont basés sur des types d'objets, ANYDATA et ANYTYPE peuvent être utilisés pour accéder dynamiquement aux attributs. Les types d'objets et les types d'enregistrements PL / SQL sont assez similaires, il devrait être relativement simple de les convertir l'un en l'autre.
Par exemple, si vous créez un type d'objet contenant un nombre et une chaîne: p>
declare type v_nt_type is table of v_type; v_values v_nt_type := v_nt_type(v_type(1, 'A'), v_type(2, 'B')); begin --For each record: for i in 1 .. v_values.count loop declare v_anydata anydata := anydata.ConvertObject(v_values(i)); v_number number; v_varchar2 varchar2(4000); v_result pls_integer; v_anytype anytype; v_dummy_num pls_integer; v_dummy_char varchar2(4000); v_dummy_anytype anytype; v_number_of_elements number; begin --Get the ANYTYPE and the number of elements. v_result := v_anydata.getType(v_anytype); v_result := v_anytype.getInfo ( prec => v_dummy_num, scale => v_dummy_num, len => v_dummy_num, csid => v_dummy_num, csfrm => v_dummy_num, schema_name => v_dummy_char, type_name => v_dummy_char, version => v_dummy_char, numelems => v_number_of_elements ); --For each element in the record: for i in 1 .. v_number_of_elements loop --Find the type of the element: v_anydata.piecewise; v_result := v_anytype.getAttrElemInfo( pos => i, prec => v_dummy_num, scale => v_dummy_num, len => v_dummy_num, csid => v_dummy_num, csfrm => v_dummy_num, attr_elt_type => v_dummy_anytype, aname => v_dummy_char); --This is where you do something interesting with the values. --(The same code merely prints the values.) if v_result = dbms_types.typecode_number then v_result := v_anydata.getNumber(num => v_number); dbms_output.put_line(v_number); elsif v_result = dbms_types.typecode_varchar2 then v_result := v_anydata.getVarchar2(c => v_varchar2); dbms_output.put_line(v_varchar2); --TODO: Add other potential types here. end if; end loop; end; end loop; end; /
Ce (douloureux) bloc PL / SQL montre comment parcourir tous les enregistrements d'une collection, puis parcourir tous les attributs de chaque enregistrement. (Le code imprime les valeurs pour, vous devrez ajouter vous-même les éléments de liaison.)
create or replace type v_type is object(a number, b varchar2(1));
Résultats:
--Generate PL/SQL statements for binds. select 'DBMS_SQL.BIND_VARIABLE(lvnInsertCursorId, '':RTTEXT'||column_id||'VC100'', lvaDBOBJDTLRecTab(i).'||column_name||');' from dba_tab_columns where owner = 'SOME_OWNER' and table_name = 'SOME_TABLE' order by 1;
* Vous avez raison de dire qu'il doit y avoir un moyen de trouver ces informations d'exécution, si le débogueur les obtient. Mais pour autant que je sache, il n'y a aucun moyen pour PL / SQL de récupérer ces informations de débogage. Peut-être n'est-il disponible que pour une interface OCI (?)?
@Jon Merci pour vos contributions, cela a aidé. aussi je suis capable d'itérer des cols sans créer des OBJETS en utilisant DBMS_SQL.DESCRIBE_COLUMNS.
** Le code ci-dessous nécessite encore un peu de réglage fin, mais fonctionne principalement :)
BEGIN COLS_TRAVERSE('SELECT * FROM ALL_OBJECTS WHERE ROWNUM<=100'); END; create or replace PROCEDURE COLS_TRAVERSE ( p_query in varchar2 ) AS v_curid NUMBER; v_desctab DBMS_SQL.DESC_TAB; v_colcnt NUMBER; v_RowNumcnt NUMBER := 1; v_Colname_var VARCHAR2(10000); v_name_var VARCHAR2(10000); v_num_var NUMBER; v_date_var DATE; v_row_num NUMBER; p_sql_stmt VARCHAR2(1000); BEGIN v_curid := DBMS_SQL.OPEN_CURSOR; DBMS_SQL.PARSE(v_curid, p_query, DBMS_SQL.NATIVE); DBMS_SQL.DESCRIBE_COLUMNS(v_curid, v_colcnt, v_desctab); -- Define columns: FOR i IN 1 .. v_colcnt LOOP IF v_desctab(i).col_type = 2 THEN DBMS_SQL.DEFINE_COLUMN(v_curid, i, v_num_var); ELSIF v_desctab(i).col_type = 12 THEN DBMS_SQL.DEFINE_COLUMN(v_curid, i, v_date_var); ELSE DBMS_SQL.DEFINE_COLUMN(v_curid, i, v_name_var, 50); END IF; END LOOP; v_row_num := dbms_sql.execute(v_curid); -- Fetch rows with DBMS_SQL package: WHILE DBMS_SQL.FETCH_ROWS(v_curid) > 0 LOOP FOR i IN 1 .. v_colcnt LOOP v_Colname_var := v_desctab(i).col_name; dbms_output.put_line( 'Name:' ||v_Colname_var ); IF (v_desctab(i).col_type = 1) THEN DBMS_SQL.COLUMN_VALUE(v_curid, i, v_name_var); dbms_output.put_line( 'String Value:' || v_name_var ); ELSIF (v_desctab(i).col_type = 2) THEN DBMS_SQL.COLUMN_VALUE(v_curid, i, v_num_var); dbms_output.put_line( 'Number Value:' || v_num_var); ELSIF (v_desctab(i).col_type = 12) THEN DBMS_SQL.COLUMN_VALUE(v_curid, i, v_date_var); dbms_output.put_line( 'Date Value:' || v_date_var ); END IF; END LOOP; dbms_output.put_line( 'End of Row Number # ' ||v_RowNumcnt ); v_RowNumcnt := v_RowNumcnt+1; END LOOP; DBMS_SQL.CLOSE_CURSOR(v_curid); END; / DBMS_OUT PUT Name:OWNER String Value:SYS Name:OBJECT_NAME String Value:ORA$BASE Name:SUBOBJECT_NAME String Value: Name:OBJECT_ID Number Value:134 Name:DATA_OBJECT_ID Number Value: Name:OBJECT_TYPE String Value:EDITION Name:CREATED Date Value:30-03-18 Name:LAST_DDL_TIME Date Value:30-03-18 Name:TIMESTAMP String Value:2018-03-30:21:37:22 Name:STATUS String Value:VALID Name:TEMPORARY String Value:N Name:GENERATED String Value:N Name:SECONDARY String Value:N Name:NAMESPACE Number Value:64 Name:EDITION_NAME String Value: Name:SHARING String Value:NONE Name:EDITIONABLE String Value: Name:ORACLE_MAINTAINED String Value:Y Name:APPLICATION String Value:N Name:DEFAULT_COLLATION String Value: Name:DUPLICATED String Value:N Name:SHARDED String Value:N Name:CREATED_APPID Number Value: Name:CREATED_VSNID Number Value: Name:MODIFIED_APPID Number Value: Name:MODIFIED_VSNID Number Value: End of Row Number # 1 Name:OWNER String Value:SYS Name:OBJECT_NAME String Value:DUAL Name:SUBOBJECT_NAME String Value: Name:OBJECT_ID Number Value:143 Name:DATA_OBJECT_ID Number Value:143 Name:OBJECT_TYPE String Value:TABLE Name:CREATED Date Value:30-03-18 Name:LAST_DDL_TIME Date Value:31-03-18 Name:TIMESTAMP String Value:2018-03-30:21:37:22 Name:STATUS String Value:VALID Name:TEMPORARY String Value:N Name:GENERATED String Value:N Name:SECONDARY String Value:N Name:NAMESPACE Number Value:1 Name:EDITION_NAME String Value: Name:SHARING String Value:METADATA LINK Name:EDITIONABLE String Value: Name:ORACLE_MAINTAINED String Value:Y Name:APPLICATION String Value:N Name:DEFAULT_COLLATION String Value:USING_NLS_COMP Name:DUPLICATED String Value:N Name:SHARDED String Value:N Name:CREATED_APPID Number Value: Name:CREATED_VSNID Number Value: Name:MODIFIED_APPID Number Value: Name:MODIFIED_VSNID Number Value: End of Row Number # 2