News zu Oracle

SQL Tuning Tipp: Subquery in SELECT-Klausel

Im Rahmen von SQL-Tuning-Maßnahmen sieht man zuweilen, dass Sub­queries zur Er­mitt­lung von Spal­ten­wer­ten direkt in der SELECT-Klausel verwendet werden. Obwohl syn­tak­tisch korrekt, ist das re­gel­mä­ßig ein Stol­per­stein für den Optimizer. Er kommt of­fen­kun­dig mit der Ab­schät­zung “echter” Joins besser zurecht.

Da der Rück­ga­be­wert der Subquery nur ein ein­zei­li­ger Skalar oder NULL sein kann (da an­dern­falls die Query fehl­schla­gen würde), erreicht man mit Um­stel­lung auf LEFT OUTER JOIN das selbe Ergebnis. Aus dem genannten Grund sind weder mehr als eine Zeile auf der “rechten” Seite des Joins zu erwarten noch besteht die Gefahr der Eli­mi­nie­rung von Er­geb­nis­zei­len der “linken” Seite durch den Join, da es sich ja um einen OUTER Join handelt.

Subquery in der SELECT-Klausel

SELECT first_name, last_name,
  (SELECT department_name
  FROM departments
  WHERE department_id = e.department_id)
FROM employees e;
-------------------------------------------------------------------------------------------
| Id  | Operation                   | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |             |   107 |  1926 |    14   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| DEPARTMENTS |     1 |    16 |     1   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | DEPT_ID_PK  |     1 |       |     0   (0)| 00:00:01 |
|   3 |  TABLE ACCESS FULL          | EMPLOYEES   |   107 |  1926 |     3   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------

Die ur­sprüng­li­che Query wird ähnlich einem NESTED LOOP aus­ge­führt. Für jede der 107 Zeilen aus EMPLOYEES wird über je einen INDEX UNIQUE SCAN der In­dex­ein­trag des zu­ge­hö­ri­gen De­part­ments gesucht und über je einen weiteren ROWID-Zugriff der department_name aus der DE­PART­MENTS-Tabelle ermittelt. Das kann bei aus­rei­chend großer “äußerer” Tabelle ein sehr un­per­for­man­ter Zu­griffs­pfad sein. NESTED LOOPs performen nur bei geringer Kar­di­na­li­tät der “äußeren” Tabelle gut.

Ersetzung durch einen Join

SELECT first_name, last_name, department_name
FROM employees e 
  LEFT JOIN departments d using (department_id);
----------------------------------------------------------------------------------
| Id  | Operation          | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |             |   107 |  3638 |     6   (0)| 00:00:01 |
|*  1 |  HASH JOIN OUTER   |             |   107 |  3638 |     6   (0)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| EMPLOYEES   |   107 |  1926 |     3   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| DEPARTMENTS |    27 |   432 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------------

Der LEFT OUTER JOIN hingegen wird über einen (outer) HASH JOIN ab­ge­wi­ckelt, der ins­be­son­de­re bei großer “äußerer” Tabelle viel besser performt, als ein NESTED LOOP. Das spiegelt sich im an­ge­ge­be­nen Beispiel vor allem in der Cost-Schätzung wider.

Hier findest du weitere Posts zu den Themen SQL Tuning bzw. Per­for­mance Tuning aus unserem News Bereich. 
icon-arrow_right_medium-violet-blue.svg

Share this article

Facebook 
Twitter 
LinkedIn 
XING 
WhatsApp 
Email