Oracle Εκμάθηση PL/SQL Dynamic SQL: Execute Immediate & DBMS_SQL

⚡ Έξυπνη Σύνοψη

Δυναμική SQL σε Oracle Η PL/SQL δημιουργεί και εκτελεί εντολές κατά τον χρόνο εκτέλεσης, προσαρμόζοντας τα ερωτήματα στις μεταβαλλόμενες απαιτήσεις μέσω δύο προσεγγίσεων: της εγγενούς δυναμικής SQL με EXECUTE IMMEDIATE και OPEN-FOR, και του ευέλικτου πακέτου DBMS_SQL για πολύπλοκες περιπτώσεις.

  • ⚙️ SQL κατά τον χρόνο εκτέλεσης: Η δυναμική SQL δημιουργεί και εκτελεί εντολές όταν τα ονόματα πινάκων ή στηλών είναι άγνωστα εκ των προτέρων.
  • Εγγενής Δυναμική SQL: Η εντολή EXECUTE IMMEDIATE δημιουργεί και εκτελεί SQL γρήγορα με τον λιγότερο δυνατό κώδικα.
  • 🔁 ΑΝΟΙΧΤΑ ΓΙΑ: Χειρίζεται δυναμικά ερωτήματα πολλαπλών γραμμών που η εντολή EXECUTE IMMEDIATE δεν μπορεί να ανακτήσει από μόνη της.
  • 🧩 ΣΔΒΔ_SQL: Κατάλληλες για εντολές των οποίων ο αριθμός ή οι τύποι στηλών είναι άγνωστοι μέχρι τον χρόνο εκτέλεσης.
  • 🔐 Μεταβλητές σύνδεσης: Η ρήτρα USING μεταβιβάζει τιμές κατά θέση και μπλοκάρει την SQL injection.
  • 🤖 Βοήθεια AI: Τα εργαλεία τεχνητής νοημοσύνης σχεδιάζουν δυναμικό SQL και επισημαίνουν κινδύνους έγχυσης κατά την αναθεώρηση.

Oracle Εκμάθηση PL/SQL Dynamic SQL

Τι είναι το Dynamic SQL;

Δυναμικός SQL είναι μια μεθοδολογία προγραμματισμού για τη δημιουργία και την εκτέλεση εντολών κατά τον χρόνο εκτέλεσης. Χρησιμοποιείται κυρίως για τη σύνταξη προγραμμάτων γενικής χρήσης και ευέλικτων προγραμμάτων όπου οι εντολές SQL δημιουργούνται και εκτελούνται κατά τον χρόνο εκτέλεσης με βάση την απαίτηση, για παράδειγμα όταν τα ονόματα πινάκων, οι λίστες στηλών ή οι συνθήκες WHERE δεν είναι γνωστά μέχρι να εκτελεστεί το πρόγραμμα.

Τρόποι Συγγραφής Δυναμικής SQL

Η PL/SQL παρέχει δύο τρόπους για να γράψετε δυναμική SQL:

  1. NDS – Native Dynamic SQL (οι εντολές EXECUTE IMMEDIATE και OPEN-FOR)
  2. DBMS_SQL (παρεχόμενη συσκευασία)

Ο γενικός κανόνας είναι απλός: εάν ο αριθμός και οι τύποι δεδομένων των μεταβλητών εισόδου και εξόδου είναι γνωστοί κατά τον χρόνο μεταγλώττισης, χρησιμοποιήστε το Native Dynamic SQL επειδή είναι ταχύτερο και απαιτεί λιγότερο κώδικα. Όταν αυτές οι πληροφορίες είναι γνωστές μόνο κατά τον χρόνο εκτέλεσης, χρησιμοποιήστε το πακέτο DBMS_SQL.

NDS (Native Dynamic SQL) – Εκτέλεση Άμεση

Η εγγενής δυναμική SQL είναι ο ευκολότερος τρόπος για να γράψετε δυναμική SQL. Χρησιμοποιεί την εντολή EXECUTE IMMEDIATE για να δημιουργήσει και να εκτελέσει την SQL κατά τον χρόνο εκτέλεσης. Για να χρησιμοποιήσετε αυτήν την προσέγγιση, ο τύπος δεδομένων και ο αριθμός των μεταβλητών που χρησιμοποιούνται κατά τον χρόνο εκτέλεσης πρέπει να είναι γνωστοί εκ των προτέρων. Προσφέρει επίσης καλύτερη απόδοση και χαμηλότερη πολυπλοκότητα σε σύγκριση με το DBMS_SQL.

Σύνταξη

EXECUTE IMMEDIATE dynamic_sql_string
[INTO {variable[, variable]... | record}]
[USING [IN | OUT | IN OUT] bind_argument[, ...]]
[RETURNING INTO bind_argument[, ...]];
  • δυναμική_sql_συμβολοσειρά: Μια έκφραση συμβολοσειράς (VARCHAR2 ή CHAR, όχι NVARCHAR2/NCHAR) που περιέχει μία μόνο πρόταση SQL ή ένα μπλοκ PL/SQL.
  • Πρόταση INTO: Προαιρετικό. Χρησιμοποιείται μόνο όταν η δυναμική SQL είναι μια SELECT μίας γραμμής. Καταγράφει τις τιμές που επιστρέφονται σε μεταβλητές ή σε μια εγγραφή. Κάθε επιλεγμένη στήλη χρειάζεται μια μεταβλητή συμβατή με τύπο.
  • ΧΡΗΣΗ όρου: Προαιρετικό. Παρέχει μεταβλητές σύνδεσης. Η προεπιλεγμένη λειτουργία είναι IN. Οι OUT και IN OUT χρησιμοποιούνται για την επιστροφή τιμών.
  • ΕΠΙΣΤΡΟΦΗ ΣΤΗ ρήτρα: Χρησιμοποιείται με εντολές DML που φέρουν έναν όρο RETURNING, για την καταγραφή τιμών επηρεαζόμενων γραμμών σε ορίσματα σύνδεσης.

Παράδειγμα 1: Σε αυτό το παράδειγμα, ανακτούμε τα δεδομένα από τον πίνακα emp για το emp_no '1001' χρησιμοποιώντας μια πρόταση NDS με μια μεταβλητή bind.

NDS - Εκτέλεση Άμεση

DECLARE
   lv_sql       VARCHAR2(500);
   lv_emp_name  VARCHAR2(50);
   ln_emp_no    NUMBER;
   ln_salary    NUMBER;
   ln_manager   NUMBER;
BEGIN
   lv_sql := 'SELECT emp_name, emp_no, salary, manager FROM emp WHERE emp_no = :empno';
   EXECUTE IMMEDIATE lv_sql
      INTO lv_emp_name, ln_emp_no, ln_salary, ln_manager
      USING 1001;
   DBMS_OUTPUT.PUT_LINE('Employee Name: '   || lv_emp_name);
   DBMS_OUTPUT.PUT_LINE('Employee Number: ' || ln_emp_no);
   DBMS_OUTPUT.PUT_LINE('Salary: '          || ln_salary);
   DBMS_OUTPUT.PUT_LINE('Manager ID: '      || ln_manager);
END;
/

Παραγωγή

Employee Name  : XXX
Employee Number: 1001
Salary         : 15000
Manager ID     : 1000

Code Επεξήγηση:

  • Γραμμές 2-6: Δήλωση των μεταβλητών.
  • Γραμμή 8: Πλαίσιο της SQL κατά τον χρόνο εκτέλεσης. Η SQL περιέχει τη μεταβλητή σύνδεσης ':empno' στη συνθήκη WHERE.
  • Γραμμές 9-11: Εκτέλεση της πλαισιωμένης SQL με EXECUTE IMMEDIATE. Οι μεταβλητές της πρότασης INTO περιέχουν τις τιμές που ανακτώνται και η πρόταση USING παρέχει την τιμή για τη μεταβλητή bind :empno.
  • Γραμμές 12-15: Εμφάνιση των τιμών που ανακτήθηκαν.

Χρήση Δυναμικής SQL για DDL

Η στατική PL/SQL δεν μπορεί να εκτελέσει απευθείας DDL όπως CREATE, ALTER ή DROP. Η εντολή EXECUTE IMMEDIATE λύνει αυτό το πρόβλημα δημιουργώντας την πρόταση ως συμβολοσειρά, η οποία είναι επίσης χρήσιμη όταν παρέχεται ένα όνομα αντικειμένου κατά τον χρόνο εκτέλεσης:

DECLARE
   l_table_name VARCHAR2(30) := 'my_table';
   l_sql_stmt   VARCHAR2(200);
BEGIN
   l_sql_stmt := 'CREATE TABLE ' || l_table_name ||
                 ' (id NUMBER, name VARCHAR2(30))';
   EXECUTE IMMEDIATE l_sql_stmt;
END;
/

Τα ονόματα αντικειμένων (πίνακας, στήλη, σχήμα) δεν μπορούν να διαβιβαστούν ως μεταβλητές σύνδεσης, επομένως πρέπει να συνενωθούν στη συμβολοσειρά. Να επικυρώνετε πάντα τέτοια δεδομένα εισόδου, για παράδειγμα με το DBMS_ASSERT.SIMPLE_SQL_NAME, για να αποφύγετε την εισαγωγή SQL.

DBMS_SQL για Dynamic SQL

Η PL/SQL παρέχει το πακέτο DBMS_SQL για εργασία με δυναμική SQL όταν η δομή της πρότασης δεν είναι γνωστή μέχρι τον χρόνο εκτέλεσης. Η διαδικασία δημιουργίας και εκτέλεσης της δυναμικής SQL περιλαμβάνει τα ακόλουθα βήματα:

  • ΑΝΟΙΓΜΑ ΔΡΟΜΕΑ: Η δυναμική SQL εκτελείται ως εξής: δρομέαςΓια να εκτελέσουμε την πρόταση SQL, πρέπει πρώτα να ανοίξουμε τον κέρσορα.
  • ΑΝΑΛΥΣΗ SQL: Ανάλυση της δυναμικής SQL. Αυτό ελέγχει τη σύνταξη και διατηρεί το ερώτημα έτοιμο για εκτέλεση.
  • Τιμές μεταβλητής σύνδεσης: Αντιστοιχίστε τις τιμές για τις μεταβλητές σύνδεσης, εάν υπάρχουν.
  • ΟΡΙΣΜΟΣ ΣΤΗΛΗΣ: Ορίστε κάθε στήλη χρησιμοποιώντας τη σχετική της θέση στην πρόταση select.
  • ΕΚΤΕΛΩ: Εκτελέστε το αναλυμένο ερώτημα.
  • ΤΙΜΕΣ ΑΝΑΚΤΗΣΗΣ: Ανάκτηση των εκτελεσμένων τιμών.
  • ΚΛΕΙΣΙΜΟ ΔΡΟΜΕΑ: Μόλις ληφθούν τα αποτελέσματα, κλείστε τον κέρσορα.

Παράδειγμα 1: Σε αυτό το παράδειγμα, ανακτούμε τα δεδομένα από τον πίνακα emp για το emp_no '1001' χρησιμοποιώντας μια πρόταση DBMS_SQL. Το μπλοκ EXCEPTION κλείνει τον κέρσορα ακόμα και αν παρουσιαστεί σφάλμα.

DBMS_SQL για Dynamic SQL

DECLARE
   lv_sql            VARCHAR2(500);
   lv_emp_name       VARCHAR2(50);
   ln_emp_no         NUMBER;
   ln_salary         NUMBER;
   ln_manager        NUMBER;
   ln_cursor_id      NUMBER;
   ln_rows_processed NUMBER;
BEGIN
   lv_sql := 'SELECT emp_name, emp_no, salary, manager FROM emp WHERE emp_no = :empno';
   ln_cursor_id := DBMS_SQL.OPEN_CURSOR;
   DBMS_SQL.PARSE(ln_cursor_id, lv_sql, DBMS_SQL.NATIVE);
   DBMS_SQL.BIND_VARIABLE(ln_cursor_id, ':empno', 1001);
   DBMS_SQL.DEFINE_COLUMN(ln_cursor_id, 1, lv_emp_name, 50);
   DBMS_SQL.DEFINE_COLUMN(ln_cursor_id, 2, ln_emp_no);
   DBMS_SQL.DEFINE_COLUMN(ln_cursor_id, 3, ln_salary);
   DBMS_SQL.DEFINE_COLUMN(ln_cursor_id, 4, ln_manager);
   ln_rows_processed := DBMS_SQL.EXECUTE(ln_cursor_id);
   LOOP
      IF DBMS_SQL.FETCH_ROWS(ln_cursor_id) = 0 THEN
         EXIT;
      ELSE
         DBMS_SQL.COLUMN_VALUE(ln_cursor_id, 1, lv_emp_name);
         DBMS_SQL.COLUMN_VALUE(ln_cursor_id, 2, ln_emp_no);
         DBMS_SQL.COLUMN_VALUE(ln_cursor_id, 3, ln_salary);
         DBMS_SQL.COLUMN_VALUE(ln_cursor_id, 4, ln_manager);
         DBMS_OUTPUT.PUT_LINE('Employee Name: '   || lv_emp_name);
         DBMS_OUTPUT.PUT_LINE('Employee Number: ' || ln_emp_no);
         DBMS_OUTPUT.PUT_LINE('Salary: '          || ln_salary);
         DBMS_OUTPUT.PUT_LINE('Manager ID: '      || ln_manager);
      END IF;
   END LOOP;
   DBMS_SQL.CLOSE_CURSOR(ln_cursor_id);
EXCEPTION
   WHEN OTHERS THEN
      DBMS_SQL.CLOSE_CURSOR(ln_cursor_id);
END;
/

Παραγωγή

Employee Name  : XXX
Employee Number: 1001
Salary         : 15000
Manager ID     : 1000

Code Επεξήγηση:

  • Γραμμές 1-8: Δήλωση μεταβλητής.
  • Γραμμή 10: Πλαίσιο της πρότασης SQL.
  • Γραμμή 11: Άνοιγμα του δρομέα χρησιμοποιώντας την εντολή DBMS_SQL.OPEN_CURSOR, η οποία επιστρέφει το αναγνωριστικό του ανοιχτού δρομέα.
  • Γραμμή 12: Αφού ανοίξει ο κέρσορας, η SQL αναλύεται.
  • Γραμμή 13: Η τιμή σύνδεσης '1001' αντιστοιχίζεται στη θέση του ':empno'.
  • Γραμμές 14-17: Ορισμός των στηλών με βάση τη σχετική τους θέση: (1) emp_name, (2) emp_no, (3) μισθός, (4) διευθυντής.
  • Γραμμή 18: Εκτέλεση του ερωτήματος με την εντολή DBMS_SQL.EXECUTE, η οποία επιστρέφει τον αριθμό των εγγραφών που έχουν υποστεί επεξεργασία.
  • Γραμμές 19-32: Ανάκτηση των εγγραφών σε βρόχο. Η συνάρτηση FETCH_ROWS επιστρέφει 0 όταν δεν υπάρχουν γραμμές, γεγονός που τερματίζει τον βρόχο.
  • Μπλοκ ΕΞΑΙΡΕΣΗΣ: Εξασφαλίζει ότι ο κέρσορας είναι κλειστός, ώστε οι ανοιχτοί κέρσορες να μην παρουσιάζουν διαρροή σε περίπτωση σφάλματος.

NDS vs DBMS_SQL: Πότε να χρησιμοποιήσετε ποιο

Και οι δύο προσεγγίσεις εκτελούν SQL κατά το χρόνο εκτέλεσης, αλλά ταιριάζουν σε διαφορετικές περιπτώσεις:

  • Χρήση εγγενούς δυναμικής SQL (ΕΚΤΕΛΕΣΗ ΑΜΕΣΑ / ΑΝΟΙΓΜΑ-ΓΙΑ) όταν ο αριθμός και οι τύποι δεδομένων των εισόδων και των εξόδων είναι γνωστοί κατά το χρόνο μεταγλώττισης. Είναι ταχύτερο, πιο εύκολο στην ανάγνωση και χρειάζεται λιγότερο κώδικα.
  • Χρήση DBMS_SQL όταν η δομή είναι άγνωστη μέχρι το χρόνο εκτέλεσης, για παράδειγμα ένα ερώτημα του οποίου ο αριθμός των επιλεγμένων στηλών ή μεταβλητών σύνδεσης ποικίλλει, γνωστό ως δυναμική SQL μεθόδου-4, ή μια πρόταση πολύ μεγάλη για να χωρέσει σε μία μόνο μεταβλητή VARCHAR2 32K.

Συχνές Ερωτήσεις

Οι μεταβλητές σύνδεσης μεταβιβάζουν την είσοδο του χρήστη ως δεδομένα, ποτέ ως εκτελέσιμο κώδικα. Η πρόταση USING παρέχει τιμές κατά θέση, επομένως το κακόβουλο κείμενο δεν μπορεί να αλλάξει τη δομή της πρότασης. Να συνδέετε πάντα μη αξιόπιστη είσοδο αντί να την συνενώνετε.

Όχι. Oracle συνδέει μόνο τιμές δεδομένων, όχι ονόματα αντικειμένων. Συνδυάστε αναγνωριστικά στη συμβολοσειρά SQL και επικυρώστε τα με το DBMS_ASSERT.SIMPLE_SQL_NAME για να παραμείνετε ασφαλείς από την εισαγωγή.

Η εντολή EXECUTE IMMEDIATE ανακτά μόνο μία γραμμή. Για πολλές γραμμές, ανοίξτε έναν REF CURSOR με την εντολή OPEN-FOR, στη συνέχεια εκτελέστε επανάληψη μέσω της FETCH μέχρι να εμφανιστεί το %NOTFOUND και ΚΛΕΙΣΤΕ τον κέρσορα.

Προσθέστε έναν όρο RETURNING στις παραμέτρους INSERT, UPDATE ή DELETE και, στη συνέχεια, χρησιμοποιήστε τον όρο RETURNING INTO της εντολής EXECUTE IMMEDIATE για να καταγράψετε τις τιμές της επηρεαζόμενης γραμμής σε ορίσματα σύνδεσης.

Η δυναμική SQL προσθέτει επιπλέον κόστος ανάλυσης επειδή οι εντολές μεταγλωττίζονται κατά τον χρόνο εκτέλεσης. Η επαναχρησιμοποίηση μεταβλητών σύνδεσης επιτρέπει Oracle κοινή χρήση δρομέων και μείωση σκληρών αναλύσεων, διατηρήστεping απόδοση κοντά σε στατική SQL.

Η συμβολοσειρά πρέπει να είναι VARCHAR2 ή CHAR. Δεν επιτρέπονται εθνικοί τύποι χαρακτήρων όπως NVARCHAR2 και NCHAR. Για κείμενο άνω των 32K, η DBMS_SQL δέχεται μια συλλογή κομματιών VARCHAR2.

Ναι. Οι βοηθοί τεχνητής νοημοσύνης, όπως το GitHub Copilot, δημιουργούν σχέδια για τα μπλοκ EXECUTE IMMEDIATE και DBMS_SQL από απλές προτροπές, προτείνουν placeholders μεταβλητής σύνδεσης και εξηγούν κάθε όρο, αν και ένας προγραμματιστής θα πρέπει να εξετάσει το αποτέλεσμα.

Οι σαρωτές κώδικα με τεχνητή νοημοσύνη επισημαίνουν τις συνενωμένες εισόδους χρηστών και προτείνουν μεταβλητές σύνδεσης ή ελέγχους DBMS_ASSERT. Επισημαίνουν επικίνδυνα μοτίβα κατά την αναθεώρηση, βοηθώντας...ping Οι ομάδες εντοπίζουν ελαττώματα έγχυσης πριν από την ανάπτυξη.

Συνοψίστε αυτήν την ανάρτηση με: