1

Ich habe ein Problem mit einem Stück Code, ich kann nicht verstehen, warum die folgende Abfrage einen vollständigen Tabellenscan in der Arbeitstabelle durchführt, wenn wrk.cre_surr_id der Primärschlüssel ist. Die Statistiken auf beiden Tabellen sind beide auf dem neuesten Stand und sind die Indizes für beide Tabellen.Oracle SQL-Abfrage läuft langsam, vollständige Tabelle Scan auf Primärschlüssel, warum?

TABLE INDEXES 
WORKS 

INDEX NAME   UNIQUE LOGGING  COLUMN NAME    ORDER 
WRK_I1    N  NO   LOGICALLY_DELETED_Y  Asc 
WRK_ICE_WRK_KEY N  YES   ICE_WRK_KEY    Asc 
WRK_PK    Y  NO   CRE_SURR_ID    Asc 
WRK_TUNECODE_UK Y  NO   TUNECODE    Asc 

TLE_TITLE_TOKENS 

INDEX NAME   UNIQUE LOGGING  COLUMN NAME    ORDER 
TTT_I1    N  YES   TOKEN_TYPE,    Asc 
             SEARCH_TOKEN, 
             DN_WRK_CRE_SURR_ID 
TTT_TLE_FK_1  N  YES   TLE_SURR_ID 

Problem Abfrage unten. Es hat einen Preis von 245.876, was hoch erscheint, es macht einen vollständigen Tabellenscan der Tabelle WORKS, die 21.938.384 Zeilen in der Tabelle hat. Es führt einen INDEX RANGE SCAN der Tabelle TLE_TITLE_TOKENS durch, die 19.923.002 Zeilen enthält. Auf dem Erklärungs-Plan ist auch ein INLIST ITERATOR, von dem ich keine Ahnung habe, was es bedeutet, aber ich denke, es hat damit zu tun, dass ich ein "in ('E', 'N')" in meiner SQL-Abfrage habe.

SELECT wrk.cre_surr_id 
FROM works wrk, 
     tle_title_tokens ttt  
WHERE ttt.dn_wrk_cre_surr_id = wrk.cre_surr_id 
AND wrk.logically_deleted_y IS NULL 
AND ttt.token_type in ('E','N') 
AND ttt.search_token LIKE 'BELIEVE'||'%' 

Wenn ich die Abfrage nach unten und machen eine einfache wählen Sie aus der TLE_TITLE_TOKENS Tabelle brechen erhalte ich 280.000 Datensätze zurück.

select ttt.dn_wrk_cre_surr_id 
from tle_title_tokens ttt 
where ttt.token_type in ('E','N') 
and ttt.search_token LIKE 'BELIEVE'||'%' 

Wie höre ich es auf, wenn ich einen FULL TABLE-Scan in der WORKS-Tabelle durchführe. Ich könnte einen Hinweis auf die Frage geben, aber ich hätte gedacht, dass Oracle clever genug wäre, um den Index ohne einen Hinweis zu verwenden.

Auch in der Tabelle TLE_TITLE_TOKENS wäre es besser, einen funktionsbasierten Index für die Spalte SEARCH_TOKEN zu erstellen, da Benutzer LIKE% in diesem Feld suchen. Wie würde dieser auf Funktionen basierende Index aussehen?

Ich bin auf einer Oracle 11g-Datenbank ausgeführt.

Vielen Dank im Voraus für alle Antworten.

+0

Wie viele Zeilen gibt die Abfrage zurück? – Thilo

+0

Hilft ein zusammengesetzter Index für cre_surr_id und logically_deleted_y? – Thilo

+0

Versuchen Sie, explainplan für Ihre Abfrage zu verwenden, um das Problem in Ihrer Abfrage zu identifizieren. Überprüfen Sie https://docs.oracle.com/cd/B19306_01/server.102/b14211/ex_plan.htm#g42231 – Phoenician

Antwort

0

versuchen Sie dies:

SELECT /*+ leading(ttt) */ wrk.cre_surr_id 
FROM works wrk, 
     tle_title_tokens ttt  
WHERE ttt.dn_wrk_cre_surr_id = wrk.cre_surr_id 
AND wrk.logically_deleted_y IS NULL 
AND ttt.token_type in ('E','N') 
AND ttt.search_token LIKE 'BELIEVE'||'%' 
+0

macht immer noch einen vollständigen Tabellenscan auf funktioniert. Dies ist Teil einer größeren Abfrage, aber ich habe es auf diesen Teil reduziert, da ich denke, dass dies das Problem ist. Die Abfrage wird in einen String l_sql: = 'select .... from' aufgebaut und dann als Cursor ausgeführt. Die letzte Zeile der Abfrage in der Zeichenfolge ist "ttt.search_token LIKE: title || '%'", könnte es die Bind-Variable sein, wenn ich die Abfrage mit der Zeile "'ttt.search_token LIKE' GLAUBEN '|| '%' benutzt den Index aber mit der Bindevariable, die es nicht tut –

+0

Die Bind-Variable wird in die Abfrage übergeben, gibt es eine andere Möglichkeit, dies ohne die Bindevariable in der SQL-aufgebauten Zeichenfolge –

1

Zuerst schreiben Sie die Abfrage ein join mit:

SELECT wrk.cre_surr_id 
FROM tle_title_tokens ttt JOIN 
    works wrk 
    ON ttt.dn_wrk_cre_surr_id = wrk.cre_surr_id 
WHERE wrk.logically_deleted_y IS NULL AND 
     ttt.token_type in ('E', 'N') AND 
     ttt.search_token LIKE 'BELIEVE'||'%'; 

sollten Sie in der Lage sein, diese Abfrage zu beschleunigen, indem Indizes verwenden. Es ist nicht klar, was der beste Index ist. Ich würde entweder tle_title_tokens(search_token, toekn_type, dn_wrk_cre_surr_id) und works(cre_surr_id, logically_deleted_y) vorschlagen.

Eine weitere Möglichkeit besteht darin, die Abfrage mit EXISTS, wie zu schreiben:

SELECT wrk.cre_surr_id 
FROM works wrk 
WHERE wrk.logically_deleted_y IS NULL AND 
     EXISTS (SELECT 1 
       FROM tle_title_tokens ttt 
       WHERE ttt.dn_wrk_cre_surr_id = wrk.cre_surr_id AND 
        ttt.token_type IN ('N', 'E') AND 
        ttt.search_token LIKE 'BELIEVE'||'%' 
      ) ; 

Für diese Version möchten Sie Indizes für works(logically_deleted_y, cre_surr_id) und tle_title_tokens(dn_wrk_cre_surr_id, token_type, search_token).

+1

Hinzufügen dieses Indexes auf ' funktioniert (cre_surr_id, logically_deleted_y) 'sollte die Dinge verbessern, es würde den Zugriff auf die Tabelle selbst unnötig machen, da nur diese zwei Spalten für die Abfrage benötigt werden. – Thilo

0

Von den 19.923.002 Zeilen in LE_TITLE_TOKENS,

haben TOKEN_TYPE Wie viele Datensätze 'E', wie viele 'N' haben? Gibt es andere TokenTypes? Wenn ja, wie viele sind sie dann zusammen?

Wenn E und N zusammen einen kleinen Teil der Gesamtdatensätze bilden, prüfen Sie, ob die Histogrammstatistik für diese Spalte aktualisiert wurde.

Der Ausführungsplan hängt davon ab, wie viele Datensätze aus den 20M-Datensätzen für die angegebenen Filter aus LE_TITLE_TOKENS ausgewählt werden.

0

Ich gehe davon aus, diesen Index Definition

create index works_idx on works (cre_surr_id,logically_deleted_y); 
create index title_tokens_idx on tle_title_tokens(search_token,token_type,dn_wrk_cre_surr_id); 

Es sind in der Regel zwei mögliche Szenarien der

Nested Loops, die die innere Tabelle zugreifen auszuführen beitreten WORKS Index verwenden, aber wiederholt in einer Schleife für jede Zeile in der äußeren Tabelle

HASH JOIN die Zugriff auf die WORKS mit FULL S Kann aber nur einmal.

Es ist nicht möglich zu sagen, dass eine Option schlecht und die andere gut ist.

geschachtelten Schleifen sind besser, wenn es nur wenige Zeilen in der äußeren Tabelle (wenige Schlaufen) sind, jedoch mit in der äußeren Tabelle Anzahl von Datensatz zu erhöhen (TOKEN) wird langsamer und langsamer und an einem gewissen Anzahl von Zeilen der HASH JOIN ist besser.

Wie sehen Sie, welcher Ausführungsplan besser ist? Erzwingen Sie Oracle, indem Sie einen Hinweis verwenden, um beide Scannarios auszuführen und die verstrichene Zeit zu vergleichen.

In Ihrem Fall, dass Sie diese beiden Ausführungs sehen sollten plant

HASH

----------------------------------------------------------------------------------------------- 
| Id | Operation   | Name    | Rows | Bytes |TempSpc| Cost (%CPU)| Time  | 
----------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT |     | 207K| 10M|  | 2439 (1)| 00:00:30 | 
|* 1 | HASH JOIN   |     | 207K| 10M| 7488K| 2439 (1)| 00:00:30 | 
|* 2 | INDEX RANGE SCAN | TITLE_TOKENS_IDX | 207K| 5058K|  | 29 (0)| 00:00:01 | 
|* 3 | TABLE ACCESS FULL| WORKS   | 893K| 22M|  | 431 (2)| 00:00:06 | 
----------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    1 - access("TTT"."DN_WRK_CRE_SURR_ID"="WRK"."CRE_SURR_ID") 
    2 - access("TTT"."SEARCH_TOKEN" LIKE 'BELIEVE%') 
     filter("TTT"."SEARCH_TOKEN" LIKE 'BELIEVE%' AND ("TTT"."TOKEN_TYPE"='E' OR 
       "TTT"."TOKEN_TYPE"='N')) 
    3 - filter("WRK"."LOGICALLY_DELETED_Y" IS NULL) 

Nested Loops

-------------------------------------------------------------------------------------- 
| Id | Operation   | Name    | Rows | Bytes | Cost (%CPU)| Time  | 
-------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT |     | 207K| 10M| 414K (1)| 01:22:56 | 
| 1 | NESTED LOOPS  |     | 207K| 10M| 414K (1)| 01:22:56 | 
|* 2 | INDEX RANGE SCAN| TITLE_TOKENS_IDX | 207K| 5058K| 29 (0)| 00:00:01 | 
|* 3 | INDEX RANGE SCAN| WORKS_IDX  |  1 | 26 |  2 (0)| 00:00:01 | 
-------------------------------------------------------------------------------------- 


Predicate Information (identified by operation id): 
--------------------------------------------------- 

    2 - access("TTT"."SEARCH_TOKEN" LIKE 'BELIEVE%') 
     filter("TTT"."SEARCH_TOKEN" LIKE 'BELIEVE%' AND 
       ("TTT"."TOKEN_TYPE"='E' OR "TTT"."TOKEN_TYPE"='N')) 
    3 - access("TTT"."DN_WRK_CRE_SURR_ID"="WRK"."CRE_SURR_ID" AND 
       "WRK"."LOGICALLY_DELETED_Y" IS NULL) 

Mein gues JOIN ist die (mit 280K Schleifen) der Hash-Join (zB FULLTABLE SCAN) wird bettwer, aber es könnte sein, dass Sie erkennen, dass verschachtelte Schleifen verwendet werden sollten. In diesem Fall erkennt die Optimierung den Umschaltpunkt zwischen verschachtelten Schleifen und Hash-Join nicht korrekt. Häufige Ursache hierfür sind falsche oder fehlende Systemstatistiken oder falsche Optimierungsparameter.