Corso
PL/SQL, l’estensione procedurale di SQL di Oracle, è qualcosa che devi conoscere se lavorerai con database Oracle. Questo vale per ruoli chiave come amministratore di database, sviluppatore e data analyst, che devono tutti gestire logiche di business complesse all’interno del database.
Secondo me, i selezionatori valutano i candidati su tre aspetti chiave:
- Comprensione concettuale: Principi di base, tipi di dato, strutture di controllo e gestione delle eccezioni.
- Capacità pratica di coding: Scrittura di stored procedure, funzioni, trigger e package efficienti.
- Considerazioni sulle performance: Ottimizzare il codice PL/SQL per migliorare i tempi di esecuzione, ridurre il consumo di risorse ed evitare errori come il passaggio di contesto eccessivo tra SQL e PL/SQL.
Per aiutarti, ti propongo una guida sulle domande dei colloqui PL/SQL. Partirò dai concetti fondamentali per arrivare a temi più avanzati come l’elaborazione bulk, l’SQL dinamico e il tuning delle performance.
Se vuoi comprendere i concetti di base del database Oracle, ti consiglio il nostro corso Introduction to Oracle SQL, una risorsa davvero importante per imparare a interagire con il database Oracle e capire come il database utilizza PL/SQL per elaborare le query.
Domande per principianti su PL/SQL
Nella fase iniziale del colloquio, l’intervistatore può porre domande di base per valutare la tua conoscenza dei concetti fondamentali di database e PL/SQL. Studia queste domande e risposte per prepararti alla prima fase del colloquio.
1. Che cos’è PL/SQL?
PL/SQL è l’estensione procedurale di SQL di Oracle. A differenza di SQL, che è dichiarativo e si concentra su recupero e manipolazione dei dati, PL/SQL consente agli sviluppatori di implementare logica procedurale, rendendolo uno strumento potente per scrivere regole di business complesse all’interno del database. Supporta variabili, cicli, condizioni, gestione delle eccezioni e programmazione modulare tramite procedure, funzioni e package.
2. Qual è la struttura di base di un blocco PL/SQL?
Un blocco PL/SQL è l’unità fondamentale di esecuzione in PL/SQL e si compone di quattro sezioni principali:
-
DECLARE(Opzionale): usato per definire variabili, costanti, cursori e tipi definiti dall’utente. -
BEGIN: la sezione eseguibile in cui si scrivono query SQL e istruzioni procedurali. -
EXCEPTION(Opzionale): gestisce gli errori e le eccezioni a runtime per garantire un recupero degli errori ordinato. -
END;: indica la fine del blocco.
3. Quali sono i tipi di dato essenziali in PL/SQL?
PL/SQL supporta vari tipi di dato, categorizzati come segue:
-
Tipi scalari: tipi a singolo valore come
NUMBER,VARCHAR2,DATE,BOOLEAN. -
Tipi compositi: collezioni come
RECORD(strutture personalizzate) eTABLE/VARRAY(array). -
Tipi reference: puntatori a oggetti del database, come
REF CURSORper l’elaborazione dinamica delle query.
4. Quali sono le strutture di controllo di base in PL/SQL?
PL/SQL include diverse strutture di controllo che aiutano a gestire il flusso di un programma:
-
Cicli: includono
LOOP,FOR LOOPeWHILE LOOP, che consentono l’esecuzione ripetuta di istruzioni. -
Istruzioni condizionali: includono le istruzioni
IFeCASE, che eseguono blocchi di codice diversi in base alle condizioni. La funzione DECODE() è un altro buon esempio di condizionale che vale la pena studiare.
Domande intermedie su PL/SQL
Dopo aver coperto le domande di base, passiamo ad alcune domande di livello intermedio sulle strutture dati. Dopo aver testato le tue conoscenze di base, gli intervistatori valuteranno ora la tua competenza tecnica nell’implementare e utilizzare i concetti PL/SQL.
5. Qual è la differenza tra stored procedure e funzioni?
Le stored procedure e le funzioni sono entrambe porzioni di codice PL/SQL riutilizzabili, ma servono a scopi piuttosto diversi.
Le stored procedure vengono usate per eseguire operazioni che non restituiscono un valore, come inserire, aggiornare o eliminare dati. Sono usate per attività che modificano i dati o eseguono operazioni complesse senza restituire un risultato.
Per esempio, la procedura qui sotto aggiorna lo salary dell’impiegato con il employee_id fornito aggiungendo il p_increment specificato, in modo che lo stipendio si aggiorni dinamicamente in base ai parametri di input,
CREATE PROCEDURE update_salary(p_emp_id NUMBER, p_increment NUMBER) AS
BEGIN
-- Update the salary of the employee with the given ID
UPDATE employees
SET salary = salary + p_increment
WHERE employee_id = p_emp_id;
END;
Le funzioni, invece, restituiscono un valore dopo aver eseguito delle operazioni. Sono adatte a calcoli o recuperi di dati che devono restituire un risultato.
La funzione seguente semplifica il recupero dello stipendio di un dipendente, rendendola riutilizzabile nelle query SQL o in altre procedure.
CREATE FUNCTION get_employee_salary(p_emp_id NUMBER) RETURN NUMBER AS
v_salary NUMBER;
BEGIN
-- Retrieve the salary for the given employee ID
SELECT salary INTO v_salary
FROM employees
WHERE employee_id = p_emp_id;
-- Return the retrieved salary
RETURN v_salary;
END;
6. Cosa sono i package PL/SQL?
I package PL/SQL sono raccolte di procedure, funzioni e variabili correlate che incapsulano il codice per una migliore organizzazione e riusabilità. Si compongono di due parti:
- Specifica del package: dichiara gli elementi pubblici (procedure, funzioni, variabili).
- Body del package: contiene i dettagli di implementazione delle procedure e funzioni dichiarate nella specifica.
Ad esempio, la query seguente crea un package employee_pkg che definisce una procedura per aumentare lo stipendio di un dipendente e una funzione per recuperare il numero totale di dipendenti, con le implementazioni fornite nel body del package.
-- Create a package named 'employee_pkg'
CREATE PACKAGE employee_pkg AS
-- Procedure to increase an employee's salary by a percentage
PROCEDURE raise_salary(p_emp_id NUMBER, p_percent NUMBER);
-- Function to return the total number of employees
FUNCTION get_total_employees RETURN NUMBER;
END employee_pkg;
7. Cosa sono i trigger PL/SQL?
I trigger sono blocchi PL/SQL che si eseguono automaticamente in risposta a specifici eventi del database, come inserimenti, aggiornamenti o cancellazioni. I trigger vengono usati per far rispettare le regole di business. Sono anche comunemente usati per l’audit. Si classificano in:
- Trigger a livello di riga: si eseguono una volta per ogni riga interessata.
- Trigger a livello di istruzione: si eseguono una volta per istruzione SQL, indipendentemente dal numero di righe interessate.
La query seguente crea un trigger AFTER UPDATE trg_salary_audit sulla tabella employees che registra le variazioni di stipendio nella tabella salary_audit, acquisendo l’ID del dipendente, lo stipendio vecchio e nuovo e la data di aggiornamento.
-- Create or replace a trigger 'trg_salary_audit'
CREATE OR REPLACE TRIGGER trg_salary_audit
AFTER UPDATE OF salary ON employees -- Fires after salary updates in 'employees' table
FOR EACH ROW -- Executes for each updated row
BEGIN
-- Inserts old and new salary details into 'salary_audit' table
INSERT INTO salary_audit (employee_id, old_salary, new_salary, change_date)
VALUES (:OLD.employee_id, :OLD.salary, :NEW.salary, SYSDATE);
END;
Studia il nostro tutorial sui trigger SQL per essere pronto nel caso in cui si parli di trigger al colloquio.
8. Quali sono i metodi di gestione delle eccezioni in PL/SQL?
PL/SQL fornisce meccanismi di gestione degli errori per la stabilità e per prevenire crash. I tipi di eccezioni includono:
-
Eccezioni predefinite: eccezioni integrate come
NO_DATA_FOUND,TOO_MANY_ROWSeZERO_DIVIDE. -
Eccezioni definite dall’utente: eccezioni personalizzate dichiarate con
EXCEPTIONe sollevate conRAISE.
Per esempio, il seguente blocco PL/SQL recupera lo stipendio del dipendente con ID 100, solleva un’eccezione personalizzata se lo stipendio è inferiore a 1000 e gestisce i possibili errori, inclusi record mancanti e eccezioni inaspettate.
DECLARE
v_salary NUMBER; -- Variable to store the employee's salary
e_low_salary EXCEPTION; -- Custom exception for low salary
BEGIN
-- Retrieve salary of employee with ID 100
SELECT salary INTO v_salary FROM employees WHERE employee_id = 100;
-- Raise exception if salary is below 1000
IF v_salary < 1000 THEN
RAISE e_low_salary;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN -- Handle case where employee ID is not found
DBMS_OUTPUT.PUT_LINE('Employee not found.');
WHEN e_low_salary THEN -- Handle custom low salary exception
DBMS_OUTPUT.PUT_LINE('Salary is below the allowed threshold.');
WHEN OTHERS THEN -- Handle any other unexpected errors
DBMS_OUTPUT.PUT_LINE('An unexpected error occurred: ' || SQLERRM);
END;
Le best practice per la gestione delle eccezioni includono:
- Prevedi sempre le possibili eccezioni.
- Usa messaggi di errore significativi.
- Registra le eccezioni per audit.
- Mantieni il codice di gestione delle eccezioni conciso e focalizzato sul recupero.
9. Come puoi verificare se un’istruzione UPDATE è stata eseguita oppure no?
L’attributo SQL %NOTFOUND può essere usato per determinare se l’istruzione UPDATE ha modificato o meno dei record. Se l’ultima istruzione SQL eseguita non ha interessato alcuna riga, questa variabile restituisce TRUE.
Per esempio, la query seguente aggiorna lo stipendio degli impiegati nel reparto 10 aumentandolo del 10% e poi verifica se l’istruzione UPDATE ha interessato delle righe usando l’attributo %NOTFOUND. Se nessuna riga è stata aggiornata, stampa il messaggio "No rows were updated." Se invece sono state aggiornate delle righe, stampa il numero di righe interessate usando l’attributo SQL%ROWCOUNT.
DECLARE
-- Declare a variable to store the number of rows updated
rows_updated INTEGER;
BEGIN
-- Perform an UPDATE statement on the 'employees' table
UPDATE employees
SET salary = salary * 1.1
WHERE department_id = 10;
-- Check if any rows were updated by using %NOTFOUND
IF SQL%NOTFOUND THEN
-- If no rows were updated, print a message
DBMS_OUTPUT.PUT_LINE('No rows were updated.');
ELSE
-- If rows were updated, print how many rows were affected
rows_updated := SQL%ROWCOUNT; -- Store the number of rows updated
DBMS_OUTPUT.PUT_LINE(rows_updated || ' rows were updated.');
END IF;
END;
/
Domande avanzate su PL/SQL
Esploriamo ora alcune domande avanzate, nel caso tu stia facendo domanda per un ruolo più senior che richiede maggiore esperienza.
10. Quali sono i metodi per ottimizzare le performance in PL/SQL?
Minimizzare i passaggi di contesto tra SQL e PL/SQL è fondamentale per ottimizzare le performance. Ogni passaggio comporta un overhead, che può rallentare i tempi di esecuzione, specialmente in situazioni che prevedono passaggi frequenti tra i due.
11. Come si usano le operazioni bulk per ridurre i passaggi di contesto?
PL/SQL fornisce tecniche di elaborazione bulk per ottimizzare l’interazione SQL–PL/SQL recuperando o modificando più righe in una volta sola.
Per esempio, il blocco PL/SQL seguente recupera tutti i dipendenti del reparto 10 usando BULK COLLECT in una collezione e vi itera per stampare il nome di ciascun dipendente, migliorando le performance riducendo i passaggi di contesto tra SQL e PL/SQL.
DECLARE
-- Define a table-type collection based on the 'employees' table structure
TYPE emp_table IS TABLE OF employees%ROWTYPE;
v_emps emp_table; -- Declare a variable of this type
BEGIN
-- Bulk fetch employees from department 10 into the collection
SELECT * BULK COLLECT INTO v_emps FROM employees WHERE department_id = 10;
-- Loop through the collection and print employee names
FOR i IN 1..v_emps.COUNT LOOP
DBMS_OUTPUT.PUT_LINE('Employee: ' || v_emps(i).employee_name);
END LOOP;
END;
Inoltre, questo blocco PL/SQL usa FORALL per aggiornamenti bulk, aumentando del 10% lo stipendio dei dipendenti con ID 101, 102 e 103.
DECLARE
-- Define a table-type collection for employee IDs
TYPE t_emp_ids IS TABLE OF employees.employee_id%TYPE;
-- Initialize collection with specific employee IDs
v_emp_ids t_emp_ids := t_emp_ids(101, 102, 103);
BEGIN
-- Bulk update salaries by 10% for specified employee IDs
FORALL i IN 1..v_emp_ids.COUNT
UPDATE employees SET salary = salary * 1.10 WHERE employee_id = v_emp_ids(i);
END;
12. Cosa sono SQL dinamico e Ref Cursor?
L’SQL dinamico consente di eseguire istruzioni SQL dinamicamente a runtime, utile quando si lavora con nomi di tabelle, colonne o strutture di query variabili.
Il seguente blocco PL/SQL usa SQL dinamico per contare il numero di righe nella tabella employees e stampa il risultato. Mi piace questo approccio perché offre flessibilità.
DECLARE
v_table_name VARCHAR2(50) := 'employees'; -- Store table name
v_count NUMBER; -- Variable to hold row count
BEGIN
-- Dynamically count rows in the specified table
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ' || v_table_name INTO v_count;
-- Print the total count
DBMS_OUTPUT.PUT_LINE('Total Employees: ' || v_count);
END;
I Ref Cursor sono cursori dinamici che possono essere aperti, letti e chiusi a runtime, consentendo di passare i risultati delle query tra unità di programma.
Il blocco PL/SQL sottostante usa un REF CURSOR per recuperare e stampare i nomi dei dipendenti del reparto 20. Il cursore viene aperto dinamicamente, iterato con un ciclo e chiuso dopo l’elaborazione.
DECLARE
-- Define a REF CURSOR type
TYPE emp_ref_cursor IS REF CURSOR;
v_cursor emp_ref_cursor; -- Declare a cursor variable
v_name employees.employee_name%TYPE; -- Variable to store employee name
BEGIN
-- Open the cursor for employees in department 20
OPEN v_cursor FOR SELECT employee_name FROM employees WHERE department_id = 20;
-- Fetch and print employee names in a loop
LOOP
FETCH v_cursor INTO v_name;
EXIT WHEN v_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('Employee: ' || v_name);
END LOOP;
-- Close the cursor
CLOSE v_cursor;
END;
13. Come gestisci gli errori di mutating table?
Gli errori di mutating table si verificano quando un trigger tenta di modificare la tabella su cui è innescato. Un trigger composito consente di suddividere l’esecuzione del trigger in più fasi come BEFORE, AFTER e FOR EACH ROW per evitare problemi di modifica diretta.
Il trigger composito di seguito registra le modifiche di stipendio nella tabella salary_audit in modo efficiente raccogliendo i dati prima di ogni aggiornamento di riga ed eseguendo un inserimento bulk dopo l’istruzione, riducendo i passaggi di contesto e migliorando le performance.
CREATE OR REPLACE TRIGGER trg_salary_audit
FOR UPDATE OF salary ON employees -- Trigger fires on salary updates
COMPOUND TRIGGER
-- Declare an associative array to store audit records
TYPE t_salary_audit IS TABLE OF salary_audit%ROWTYPE INDEX BY PLS_INTEGER;
v_audit_data t_salary_audit;
v_idx PLS_INTEGER := 0;
-- Before updating each row, store old and new salary details
BEFORE EACH ROW IS
BEGIN
v_idx := v_idx + 1;
v_audit_data(v_idx).employee_id := :OLD.employee_id;
v_audit_data(v_idx).old_salary := :OLD.salary;
v_audit_data(v_idx).new_salary := :NEW.salary;
v_audit_data(v_idx).change_date := SYSDATE;
END BEFORE EACH ROW;
-- After the statement, insert all audit records in bulk
AFTER STATEMENT IS
BEGIN
FORALL i IN 1..v_idx
INSERT INTO salary_audit VALUES v_audit_data(i);
END AFTER STATEMENT;
END trg_salary_audit;
14. Che cos’è l’overloading in PL/SQL?
L’overloading consente di definire più procedure o funzioni con lo stesso nome ma parametri diversi all’interno di un package. Ciò migliora la leggibilità e la manutenibilità del codice offrendo più modi per eseguire operazioni simili.
Nella query seguente, il body del package implementa due procedure sovraccaricate chiamate update_salary: una aumenta lo stipendio di un dipendente di un importo specificato, l’altra imposta un nuovo stipendio con una data di efficacia, aggiornando di conseguenza la tabella employees.
-- Create the package specification
CREATE OR REPLACE PACKAGE emp_pkg AS
-- Procedure to increment salary by a specified amount
PROCEDURE update_salary(p_emp_id NUMBER, p_increment NUMBER);
-- Overloaded procedure to set a new salary with an effective date
PROCEDURE update_salary(p_emp_id NUMBER, p_new_salary NUMBER, p_effective_date DATE);
END emp_pkg;
/
-- Create the package body
CREATE OR REPLACE PACKAGE BODY emp_pkg AS
-- Procedure to increment salary by a specified amount
PROCEDURE update_salary(p_emp_id NUMBER, p_increment NUMBER) AS
BEGIN
UPDATE employees
SET salary = salary + p_increment
WHERE employee_id = p_emp_id;
END update_salary;
-- Overloaded procedure to set a new salary with an effective date
PROCEDURE update_salary(p_emp_id NUMBER, p_new_salary NUMBER, p_effective_date DATE) AS
BEGIN
UPDATE employees
SET salary = p_new_salary, last_update = p_effective_date
WHERE employee_id = p_emp_id;
END update_salary;
END emp_pkg;
/
15. Quali sono le direttive del compilatore e le pragma in PL/SQL?
PL/SQL fornisce direttive del compilatore (PRAGMA) per ottimizzare il codice e gestire le eccezioni. Le pragma più comuni includono:
-
PRAGMA EXCEPTION_INIT: associa un’eccezione definita dall’utente a un codice di errore Oracle. -
PRAGMA SERIALLY_REUSABLE: ottimizza l’uso della memoria dei package per la scalabilità.
Il blocco PL/SQL seguente gestisce l’inserimento di un dipendente con stipendio non valido usando un’eccezione personalizzata e_invalid_salary mappata al codice di errore -20001. Se l’eccezione viene sollevata, stampa un messaggio di errore.
DECLARE
e_invalid_salary EXCEPTION; -- Declare custom exception for invalid salary
PRAGMA EXCEPTION_INIT(e_invalid_salary, -20001); -- Associate exception with error code -20001
BEGIN
-- Attempt to insert an employee with invalid salary
INSERT INTO employees (employee_id, salary) VALUES (999, -1000);
EXCEPTION
-- Handle the custom exception and print a message
WHEN e_invalid_salary THEN
DBMS_OUTPUT.PUT_LINE('Invalid salary detected!');
END;
16. Quali sono i diversi metodi per tracciare e fare debug del codice PL/SQL?
PL/SQL fornisce diversi package integrati per tracciare e fare il debug delle performance del codice. Metodi comuni includono l’uso di DBMS_TRACE per tracciare il flusso di esecuzione, DBMS_APPLICATION_INFO per monitorare l’attività di sessione e DBMS_SESSION per raccogliere informazioni diagnostiche a livello di sessione.
La query seguente abilita innanzitutto il tracing SQL usando DBMS_SESSION.set_sql_trace(TRUE), quindi imposta informazioni specifiche del client di sessione con DBMS_APPLICATION_INFO.set_client_info() per il monitoraggio. Viene eseguito il blocco PL/SQL, che simula un’operazione di UPDATE. Infine, disabilita il tracing dopo l’esecuzione con DBMS_SESSION.set_sql_trace(FALSE).
-- Enable tracing for the current session using DBMS_SESSION
BEGIN
-- Start session-level tracing
DBMS_SESSION.set_sql_trace(TRUE);
END;
/
-- Set application information using DBMS_APPLICATION_INFO
BEGIN
-- Set the application name and action for session monitoring
DBMS_APPLICATION_INFO.set_client_info('Trace Debug Session');
DBMS_APPLICATION_INFO.set_action('Debugging PL/SQL Code');
END;
/
-- Example PL/SQL block that simulates a process for debugging
DECLARE
v_employee_id NUMBER := 100;
BEGIN
-- Example query to fetch employee details
FOR rec IN (SELECT first_name, last_name FROM employees WHERE employee_id = v_employee_id) LOOP
DBMS_OUTPUT.put_line('Employee: ' || rec.first_name || ' ' || rec.last_name);
END LOOP;
-- Simulate some logic that could be traced
IF v_employee_id = 100 THEN
DBMS_OUTPUT.put_line('Employee ID is 100');
END IF;
END;
/
-- Disable tracing after the session is complete
BEGIN
-- Stop session-level tracing
DBMS_SESSION.set_sql_trace(FALSE);
END;
/
Domande su PL/SQL basate su scenari
Nelle applicazioni enterprise reali, PL/SQL viene utilizzato per gestire logiche di business complesse. Spesso gli intervistatori valutano la capacità di applicare PL/SQL in scenari pratici. Di seguito alcune sfide chiave, con strategie per risolverle.
Per questa prossima e ultima sezione, se stai sostenendo un colloquio, ti incoraggio a pensare a come rispondere alle domande seguendo questo modello, ma prova, ovviamente, a riorientare la risposta sul tuo settore specifico o per riflettere la tua esperienza. Lo stesso vale se stai intervistando un candidato e vuoi idee su come porre una domanda e valutare la completezza della risposta, nel qual caso spero che questa sezione serva da ispirazione sia per le tue domande che per i criteri di valutazione.
17. Come progetti i trigger in ambienti con elevata concorrenza?
Immagina uno scenario in cui devi far rispettare una regola di business secondo cui lo stipendio di un dipendente non può essere aggiornato più di una volta al giorno. Tuttavia, il database presenta un’alta concorrenza di transazioni e un semplice trigger potrebbe causare contention o problemi di performance.
Invece di utilizzare un trigger a livello di riga che si attiva a ogni aggiornamento e può rallentare le performance, usa un trigger a livello di istruzione con una tabella di log per impedire aggiornamenti multipli dello stipendio nello stesso giorno.
Per esempio, il trigger seguente impedisce aggiornamenti multipli dello stipendio per un dipendente nello stesso giorno controllando la tabella salary_update_log prima di consentire un aggiornamento. Se lo stipendio è già stato aggiornato oggi, viene sollevato un errore; altrimenti, la data di aggiornamento viene registrata.
-- Create a table to log the last salary update for each employee
CREATE TABLE salary_update_log (
employee_id NUMBER PRIMARY KEY, -- Employee ID as primary key
last_update DATE -- Date of the last salary update
);
-- Create or replace a compound trigger to prevent multiple salary updates on the same day
CREATE OR REPLACE TRIGGER trg_prevent_multiple_salary_update
FOR UPDATE OF salary ON employees
COMPOUND TRIGGER
-- Declare a variable to store last salary update date per row
TYPE emp_log_type IS TABLE OF DATE INDEX BY PLS_INTEGER;
emp_log emp_log_type;
BEFORE STATEMENT IS
BEGIN
-- Load existing salary update logs into memory for reference
FOR rec IN (SELECT employee_id, last_update FROM salary_update_log) LOOP
emp_log(rec.employee_id) := rec.last_update;
END LOOP;
END BEFORE STATEMENT;
BEFORE EACH ROW IS
BEGIN
-- Check if an entry exists for this employee
IF emp_log.EXISTS(:NEW.employee_id) THEN
-- Validate if salary was updated today
IF emp_log(:NEW.employee_id) = TRUNC(SYSDATE) THEN
RAISE_APPLICATION_ERROR(-20010, 'Salary can only be updated once per day');
ELSE
-- Update log in memory
emp_log(:NEW.employee_id) := TRUNC(SYSDATE);
END IF;
ELSE
-- Insert a new log entry into memory
emp_log(:NEW.employee_id) := TRUNC(SYSDATE);
END IF;
END BEFORE EACH ROW;
AFTER STATEMENT IS
BEGIN
-- Apply changes to the log table after all row updates
FOR i IN emp_log.FIRST .. emp_log.LAST LOOP
MERGE INTO salary_update_log l
USING (SELECT i AS employee_id, emp_log(i) AS last_update FROM DUAL) s
ON (l.employee_id = s.employee_id)
WHEN MATCHED THEN
UPDATE SET l.last_update = s.last_update
WHEN NOT MATCHED THEN
INSERT (employee_id, last_update) VALUES (s.employee_id, s.last_update);
END LOOP;
END AFTER STATEMENT;
END trg_prevent_multiple_salary_update;
L’esempio sopra ridurrebbe il locking a livello di riga e minimizzerebbe i conflitti di transazione durante aggiornamenti bulk.
18. Come suddividi grandi transazioni in blocchi più piccoli per maggiore affidabilità?
Immagina uno scenario in cui un sistema bancario richiede un aggiornamento bulk per adeguare i tassi di interesse per milioni di conti. Eseguire una singola grande transazione potrebbe bloccare le tabelle troppo a lungo o portare a fallimenti del rollback.
In questo caso, usa l’elaborazione bulk con COMMIT a lotti per elaborare i dati in modo incrementale ed evitare contention.
Il blocco PL/SQL seguente implementa la soluzione usando BULK COLLECT per recuperare gli ID dei conti e poi itera su di essi per aggiornare il tasso d’interesse del 5%. Esegue il commit ogni 1000 aggiornamenti per migliorare le performance e ridurre l’uso di risorse. Un commit finale assicura che gli aggiornamenti rimanenti vengano salvati.
DECLARE
-- Define a collection type for account IDs
TYPE t_accounts IS TABLE OF NUMBER; -- Use NUMBER instead of referencing accounts.account_id
v_account_ids t_accounts; -- Variable to store account IDs
v_batch_size CONSTANT NUMBER := 1000; -- Batch size for commits
BEGIN
-- Bulk collect all account IDs into the collection
SELECT account_id BULK COLLECT INTO v_account_ids FROM accounts;
-- Loop through each account ID to update the interest rate
FOR i IN 1 .. v_account_ids.COUNT LOOP
UPDATE accounts
SET interest_rate = interest_rate * 1.05 -- Increase interest rate by 5%
WHERE account_id = v_account_ids(i);
-- Commit after every 1000 updates
IF MOD(i, v_batch_size) = 0 THEN
COMMIT;
END IF;
END LOOP;
COMMIT; -- Final commit for any remaining updates
END;
/
Questa soluzione garantirebbe affidabilità in sistemi finanziari come i payroll prevenendo fallimenti di transazione.
19. Come organizzi la logica complessa in package per la manutenibilità?
Un’azienda retail deve implementare un sistema di gestione degli ordini che prevede diversi passaggi: verifica della disponibilità a magazzino, prenotazione dello stock, calcolo degli sconti e registrazione dello storico delle transazioni.
Invece di scrivere procedure autonome separate, devi organizzare questa logica in modo strutturato e manutenibile. Usa quindi i package PL/SQL per incapsulare procedure e funzioni correlate, migliorando riusabilità e manutenibilità del codice
La specifica del package seguente definisce funzioni e procedure per l’elaborazione degli ordini, inclusi il controllo dell’inventario, la prenotazione dello stock, il calcolo degli sconti e il logging degli stati delle transazioni. Fornisce un approccio modulare alla gestione delle attività legate agli ordini.
-- Create a package specification for order processing functions and procedures
CREATE PACKAGE order_processing_pkg AS
-- Function to check if enough inventory is available for the product
FUNCTION check_inventory(p_product_id NUMBER, p_quantity NUMBER) RETURN BOOLEAN;
-- Procedure to reserve stock for a specific order
PROCEDURE reserve_stock(p_order_id NUMBER, p_product_id NUMBER, p_quantity NUMBER);
-- Function to calculate discount based on customer and total amount
FUNCTION calculate_discount(p_customer_id NUMBER, p_total_amount NUMBER) RETURN NUMBER;
-- Procedure to log the status of a transaction
PROCEDURE log_transaction(p_order_id NUMBER, p_status VARCHAR2);
END order_processing_pkg;
Usiamo quindi il seguente body del package per implementare le funzioni e le procedure definite nella specifica. Gestisce attività chiave come il controllo della disponibilità a magazzino, la prenotazione dello stock, il calcolo degli sconti e il logging degli stati delle transazioni, garantendo un’elaborazione degli ordini fluida.
-- Create the package body implementing the functions and procedures for order processing
CREATE PACKAGE BODY order_processing_pkg AS
-- Function to check if sufficient inventory is available for the product
FUNCTION check_inventory(p_product_id NUMBER, p_quantity NUMBER) RETURN BOOLEAN AS
v_available_qty NUMBER; -- Variable to store available quantity
BEGIN
-- Retrieve the available quantity from inventory
SELECT stock_quantity INTO v_available_qty FROM inventory WHERE product_id = p_product_id;
-- Return true if enough stock is available, otherwise false
RETURN v_available_qty >= p_quantity;
END check_inventory;
-- Procedure to reserve stock for a specific order
PROCEDURE reserve_stock(p_order_id NUMBER, p_product_id NUMBER, p_quantity NUMBER) AS
BEGIN
-- Deduct the ordered quantity from the inventory
UPDATE inventory SET stock_quantity = stock_quantity - p_quantity WHERE product_id = p_product_id;
END reserve_stock;
-- Function to calculate a discount based on the total order amount
FUNCTION calculate_discount(p_customer_id NUMBER, p_total_amount NUMBER) RETURN NUMBER AS
v_discount NUMBER := 0; -- Initialize discount to 0
BEGIN
-- Apply 10% discount if the total amount is greater than 500
IF p_total_amount > 500 THEN
v_discount := p_total_amount * 0.10;
END IF;
-- Return the calculated discount
RETURN v_discount;
END calculate_discount;
-- Procedure to log the transaction status
PROCEDURE log_transaction(p_order_id NUMBER, p_status VARCHAR2) AS
BEGIN
-- Insert a log entry for the order status
INSERT INTO order_log (order_id, status, log_date) VALUES (p_order_id, p_status, SYSDATE);
END log_transaction;
END order_processing_pkg;
20. Come gestisci i deadlock in sistemi ad alta transazionalità?
Immagina uno scenario in cui un sistema finanziario aggiorna frequentemente più tabelle correlate in simultanea. I deadlock si verificano quando due transazioni attendono le risorse bloccate l’una dell’altra, causando colli di bottiglia nelle performance.
Per risolvere il problema, blocca sempre le righe in un ordine coerente tra le transazioni. Inoltre, usa le clausole NOWAIT o SKIP LOCKED per evitare attese indefinitamente.
Per esempio, il blocco PL/SQL seguente tenta di bloccare una riga specifica della tabella accounts per l’aggiornamento usando la clausola FOR UPDATE NOWAIT, che fa fallire immediatamente la transazione se un’altra sessione ha già bloccato la riga. Dopo il locking, aggiorna lo stato delle transazioni e fa il commit delle modifiche. In caso di errore, intercetta l’eccezione e stampa un messaggio di errore.
DECLARE
v_balance NUMBER(15,2); -- Declare variable to store the account balance
BEGIN
-- Lock the account row for update to prevent other sessions from modifying it
SELECT balance INTO v_balance FROM accounts
WHERE account_id = 101 FOR UPDATE NOWAIT;
-- Update the transaction status to 'Processed' for all transactions related to this account
UPDATE transactions
SET status = 'Processed'
WHERE account_id = 101 AND status = 'Pending'; -- Update only pending transactions
COMMIT; -- Commit the changes
EXCEPTION
-- Handle errors, such as locking issues or unexpected exceptions
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Transaction failed: ' || SQLERRM);
ROLLBACK; -- Ensure rollback in case of failure
END;
/
Analogamente, questo blocco PL/SQL elabora le transazioni in sospeso bloccandole per l’aggiornamento con FOR UPDATE SKIP LOCKED, consentendo di saltare le righe già bloccate da altre sessioni. Aggiorna lo stato di ogni transazione a "Processing" e fa il commit delle modifiche alla fine.
DECLARE
-- Declare a cursor to select pending transactions and lock rows for update
CURSOR c_pending_txns IS
SELECT transaction_id FROM transactions WHERE status = 'Pending' FOR UPDATE SKIP LOCKED; -- Skip locked rows
BEGIN
-- Loop through the pending transactions
FOR txn IN c_pending_txns LOOP
-- Update the status of each transaction to 'Processing'
UPDATE transactions SET status = 'Processing' WHERE transaction_id = txn.transaction_id;
END LOOP;
COMMIT; -- Commit the changes to finalize the transaction updates
END;
Conclusioni e risorse aggiuntive
Per ruoli incentrati su Oracle, i datori di lavoro cercano candidati in grado di scrivere codice PL/SQL corretto, gestire sfide reali e ottimizzare le performance del database. Ti consiglio di consultare la documentazione ufficiale Oracle sulla PL/SQL Language Reference per una guida approfondita e best practice su PL/SQL. Oracle Live SQL (Interactive PL/SQL Playground) offre accesso alla piattaforma cloud di Oracle per eseguire query PL/SQL online. Ti incoraggio anche a unirti alla Oracle Community per confrontarti con esperti e sviluppatori Oracle e al forum Stack Overflow per trovare soluzioni a domande relative a PL/SQL.
Tuttavia, nulla batte un percorso di apprendimento strutturato. Per questo motivo, consiglio vivamente di seguire il nostro corso Introduction to Oracle SQL, che è davvero, secondo me, il modo migliore per diventare davvero esperto. Inoltre, se vuoi avanzare nella conoscenza del lavoro con i database, ti consiglio anche il nostro corso Database Design, dove imparerai a creare e gestire database e a scegliere il DBMS più adatto alle tue esigenze. Infine, prova il nostro career track Associate Data Engineer in SQL per apprendere le basi dell’ingegneria dei dati e del data warehousing.
Ultimo ma non meno importante, se stai intervistando candidati e vuoi anche migliorare le competenze degli altri, mettiti in contatto con il team di DataCamp for Business. DataCamp può far crescere interi team con progetti pratici e percorsi di apprendimento personalizzati. È un ottimo modo per i datori di lavoro di colmare i gap di competenze e aumentare fiducia e credibilità del team, quindi contatta oggi stesso il nostro team.
FAQ su PL SQL
Che cos’è PL/SQL e in cosa differisce da SQL?
PL/SQL è un linguaggio procedurale sviluppato da Oracle, che consente strutture di controllo e gestione degli errori, mentre SQL è dichiarativo e si occupa del recupero/manipolazione dei dati.
Che cos’è un package PL/SQL?
Una raccolta di procedure, funzioni, variabili e cursori correlati per la programmazione modulare e la riusabilità.
In cosa una procedura è diversa da una funzione?
Le procedure eseguono un’azione che non restituisce un valore, mentre le funzioni restituiscono un valore che può essere usato nelle query.
Cosa sono i trigger in PL/SQL?
Azioni automatiche eseguite prima o dopo le operazioni di INSERT, UPDATE, DELETE.
Che cos’è il bulk processing in PL/SQL?
L’elaborazione bulk (BULK COLLECT, FORALL) migliora le performance riducendo i passaggi di contesto tra i motori PL/SQL e SQL nella gestione di grandi dataset.


