it-swarm.com.de

Zusammengesetzte Indizes: Selektivste Spalte zuerst?

Ich habe über composite indexes und ich bin etwas verwirrt über die Bestellung. Diese Dokumentation (etwas weniger als auf halber Strecke) sagt

Im Allgemeinen sollten Sie die Spalte, die voraussichtlich am häufigsten verwendet wird, zuerst in den Index aufnehmen.

Jedoch kurz danach heißt es

erstellen Sie einen zusammengesetzten Index, wobei die selektivste Spalte an erster Stelle steht. Das heißt, die Spalte mit den meisten Werten.

Oracle sagt es auch hier mit anderen Worten

Wenn alle Schlüssel in WHERE-Klauseln gleich häufig verwendet werden, wird die Abfrageleistung am besten verbessert, wenn diese Schlüssel in der Anweisung CREATE INDEX von der selektivsten zur am wenigsten selektiven Reihenfolge geordnet werden.

Ich habe jedoch eine SO Antwort gefunden, die anders sagt. Es sagt

Ordnen Sie die Spalten mit der am wenigsten selektiven Spalte zuerst und der am meisten selektiven Spalte zuletzt an. Im Falle einer Verbindungsleitung mit der Säule, die eher alleine verwendet wird.

Die erste Dokumentation, auf die ich verwiesen habe, besagt, dass Sie zuerst die am häufigsten verwendete verwenden sollten, während die Antwort SO besagt, dass dies nur zum Brechen von Krawatten dienen soll. Dann unterscheiden sie sich auch in der Reihenfolge.

Diese Dokumentation spricht auch über skip scanning und sagt

Das Überspringen des Scannens ist vorteilhaft, wenn in der führenden Spalte des zusammengesetzten Index nur wenige unterschiedliche Werte und im nicht führenden Schlüssel des Index viele unterschiedliche Werte vorhanden sind.

Ein anderer Artikel sagt

Die Präfixspalte sollte die diskriminierendste und in Abfragen am häufigsten verwendete sein

was meiner Meinung nach am diskriminierendsten bedeuten würde.

All diese Forschungen führen mich immer noch zu derselben Frage; sollte die selektivste Spalte die erste oder letzte sein? Sollte die erste Spalte die am häufigsten verwendete und nur die selektivste bei einem Gleichstand sein?

Diese Artikel scheinen sich zu widersprechen, bieten aber einige Beispiele. Nach dem, was ich gesammelt habe, scheint es für die least selective column als zuerst in der Bestellung sein, wenn Sie Index Skip Scans. Aber ich bin mir nicht sicher, ob das richtig ist.

17
Eric

Von AskTom

(In 9i gibt es einen neuen "Index-Skip-Scan" - suchen Sie dort danach, um darüber zu lesen. Dadurch ist der Index (a, b) OR (b, a) nützlich in beide oben genannten Fälle manchmal!)

Die Reihenfolge der Spalten in Ihrem Index hängt also davon ab, wie Ihre Abfragen geschrieben werden. Sie möchten in der Lage sein, den Index für so viele Abfragen wie möglich zu verwenden (um die Gesamtzahl der vorhandenen Indizes zu verringern) - dies bestimmt die Reihenfolge der Spalten. Sonst nichts (Selektivität von a oder b zählt überhaupt nicht).

Eines der Argumente für die Anordnung von Spalten im zusammengesetzten Index in der Reihenfolge von den am wenigsten unterscheidenden (weniger unterschiedliche Werte) bis zu den am meisten unterscheidenden (stärker unterschiedliche Werte) ist die Komprimierung von Indexschlüsseln.

SQL> create table t as select * from all_objects;

Table created.

SQL> create index t_idx_1 on t(owner,object_type,object_name);

Index created.

SQL> create index t_idx_2 on t(object_name,object_type,owner);

Index created.

SQL> select count(distinct owner), count(distinct object_type), count(distinct object_name ), count(*)  from t;

COUNT(DISTINCTOWNER) COUNT(DISTINCTOBJECT_TYPE) COUNT(DISTINCTOBJECT_NAME)      COUNT(*)
-------------------- -------------------------- --------------------------      ----------
                 30                         45                       52205      89807

SQL> analyze index t_idx_1 validate structure; 

Index analyzed.

SQL> select btree_space, pct_used, opt_cmpr_count, opt_cmpr_pctsave from index_stats;

BTREE_SPACE   PCT_USED OPT_CMPR_COUNT OPT_CMPR_PCTSAVE
----------- ---------- -------------- ----------------
    5085584     90          2           28

SQL> analyze index t_idx_2 validate structure; 

Index analyzed.

SQL> select btree_space, pct_used, opt_cmpr_count, opt_cmpr_pctsave  from index_stats; 

BTREE_SPACE   PCT_USED OPT_CMPR_COUNT OPT_CMPR_PCTSAVE
----------- ---------- -------------- ----------------
    5085584     90          1           14

Laut Indexstatistik ist der erste Index komprimierbarer.

Zum anderen wird der Index in Ihren Abfragen verwendet. Wenn Ihre Anfragen meistens col1 Verwenden,

Zum Beispiel, wenn Sie Fragen haben wie:

  • select * from t where col1 = :a and col2 = :b;
  • 9
    JSapkota

    Die selektivste zuerst ist nur dann nützlich, wenn sich diese Spalte in der tatsächlichen WHERE-Klausel befindet.

    Wenn SELECT von einer größeren Gruppe (weniger selektiv) und möglicherweise von anderen, nicht indizierten Werten stammt, kann ein Index mit weniger selektiven Spalten weiterhin nützlich sein (wenn es einen Grund gibt, keine weitere zu erstellen).

    Wenn es eine Tabellenadresse gibt, mit

    COUNTRY CITY STREET, etwas anderes ...

    indizierung STREET, CITY, COUNTRY liefert die schnellsten Abfragen mit einem Straßennamen. Wenn Sie jedoch alle Straßen einer Stadt abfragen, ist der Index unbrauchbar, und die Abfrage führt wahrscheinlich einen vollständigen Tabellenscan durch.

    Indizierung LAND, STADT, STRASSE kann für einzelne Straßen etwas langsamer sein, der Index kann jedoch für andere Abfragen verwendet werden, wobei nur nach Land und/oder Stadt ausgewählt wird.

    3
    Erik Hart

    Bei der Auswahl der Reihenfolge der Indexspalten ist das übergeordnete Anliegen:

    Gibt es in meinen Abfragen (Gleichheits-) Prädikate für diese Spalte ?

    Wenn eine Spalte niemals in einer where-Klausel erscheint, lohnt es sich nicht, sie zu indizieren (1).

    OK, Sie haben also eine Tabelle und Abfragen für jede Spalte. Manchmal mehr als eine.

    Wie entscheiden Sie, was indiziert werden soll?

    Schauen wir uns ein Beispiel an. Hier ist eine Tabelle mit drei Spalten. Einer hält 10 Werte, ein weiterer 1.000, die letzten 10.000:

    create table t(
      few_vals  varchar2(10),
      many_vals varchar2(10),
      lots_vals varchar2(10)
    );
    
    insert into t 
    with rws as (
      select lpad(mod(rownum, 10), 10, '0'), 
             lpad(mod(rownum, 1000), 10, '0'), 
             lpad(rownum, 10, '0') 
      from dual connect by level <= 10000
    )
      select * from rws;
    
    commit;
    
    select count(distinct few_vals),
           count(distinct many_vals) ,
           count(distinct lots_vals) 
    from   t;
    
    COUNT(DISTINCTFEW_VALS)  COUNT(DISTINCTMANY_VALS)  COUNT(DISTINCTLOTS_VALS)  
    10                       1,000                     10,000     
    

    Dies sind Zahlen, die mit Nullen aufgefüllt bleiben. Dies wird dazu beitragen, später auf die Komprimierung hinzuweisen.

    Sie haben also drei häufig gestellte Fragen:

    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals = '0000000001';
    
    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001';
    
    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001'
    and    few_vals = '0000000001';
    

    Was indizieren Sie?

    Ein Index für nur wenige_Werte ist nur unwesentlich besser als ein vollständiger Tabellenscan:

    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals = '0000000001';
    
    select * 
    from table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------  
    | Id  | Operation            | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT     |          |      1 |        |      1 |00:00:00.01 |      61 |  
    |   1 |  SORT AGGREGATE      |          |      1 |      1 |      1 |00:00:00.01 |      61 |  
    |   2 |   VIEW               | VW_DAG_0 |      1 |   1000 |   1000 |00:00:00.01 |      61 |  
    |   3 |    HASH GROUP BY     |          |      1 |   1000 |   1000 |00:00:00.01 |      61 |  
    |   4 |     TABLE ACCESS FULL| T        |      1 |   1000 |   1000 |00:00:00.01 |      61 |  
    -------------------------------------------------------------------------------------------
    
    select /*+ index (t (few_vals)) */
           count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      58 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      58 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |   1000 |   1000 |00:00:00.01 |      58 |  
    |   3 |    HASH GROUP BY                       |          |      1 |   1000 |   1000 |00:00:00.01 |      58 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |   1000 |   1000 |00:00:00.01 |      58 |  
    |   5 |      INDEX RANGE SCAN                  | FEW      |      1 |   1000 |   1000 |00:00:00.01 |       5 |  
    -------------------------------------------------------------------------------------------------------------
    

    Es ist also unwahrscheinlich, dass es sich lohnt, allein zu indizieren. Abfragen zu lot_vals geben nur wenige Zeilen zurück (in diesem Fall nur 1). Das ist also definitiv eine Indizierung wert.

    Aber was ist mit den Abfragen für beide Spalten?

    Sollten Sie indizieren:

    ( few_vals, lots_vals )
    

    OR

    ( lots_vals, few_vals )
    

    Fangfrage!

    Die Antwort ist weder.

    Sicher, paar_Werte sind eine lange Zeichenfolge. So können Sie eine gute Komprimierung erzielen. Und Sie erhalten (möglicherweise) einen Index-Skip-Scan für die Abfragen mit (paar_Werte, viele_Werte), die nur Prädikate für viele_Werte haben. Aber ich bin nicht hier, obwohl es deutlich besser abschneidet als ein vollständiger Scan:

    create index few_lots on t(few_vals, lots_vals);
    
    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------  
    | Id  | Operation            | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT     |          |      1 |        |      1 |00:00:00.01 |      61 |  
    |   1 |  SORT AGGREGATE      |          |      1 |      1 |      1 |00:00:00.01 |      61 |  
    |   2 |   VIEW               | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |      61 |  
    |   3 |    HASH GROUP BY     |          |      1 |      1 |      1 |00:00:00.01 |      61 |  
    |   4 |     TABLE ACCESS FULL| T        |      1 |      1 |      1 |00:00:00.01 |      61 |  
    -------------------------------------------------------------------------------------------  
    
    select /*+ index_ss (t few_lots) */count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    ----------------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  
    ----------------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      13 |     11 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
    |   3 |    HASH GROUP BY                       |          |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
    |   5 |      INDEX SKIP SCAN                   | FEW_LOTS |      1 |     40 |      1 |00:00:00.01 |      12 |     11 |  
    ----------------------------------------------------------------------------------------------------------------------
    

    Spielen Sie gerne? (2)

    Sie benötigen also noch einen Index mit lot_vals als führende Spalte. Und zumindest in diesem Fall erledigt der zusammengesetzte Index (wenige, viele) die gleiche Menge an Arbeit wie einer für nur (viele)

    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001'
    and    few_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |       3 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |       3 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |       3 |  
    |   3 |    HASH GROUP BY                       |          |      1 |      1 |      1 |00:00:00.01 |       3 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      1 |      1 |00:00:00.01 |       3 |  
    |   5 |      INDEX RANGE SCAN                  | FEW_LOTS |      1 |      1 |      1 |00:00:00.01 |       2 |  
    -------------------------------------------------------------------------------------------------------------  
    
    create index lots on t(lots_vals);
    
    select /*+ index (t (lots_vals)) */count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001'
    and    few_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    ----------------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  
    ----------------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |       3 |      1 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
    |   3 |    HASH GROUP BY                       |          |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
    |   5 |      INDEX RANGE SCAN                  | LOTS     |      1 |      1 |      1 |00:00:00.01 |       2 |      1 |  
    ----------------------------------------------------------------------------------------------------------------------  
    

    Es wird Fälle geben, in denen der zusammengesetzte Index 1-2 IOs speichert. Aber lohnt es sich, zwei Indizes für diese Einsparung zu haben?

    Und es gibt noch ein anderes Problem mit dem zusammengesetzten Index. Vergleichen Sie den Clustering-Faktor für die drei Indizes einschließlich LOTS_VALS:

    create index lots on t(lots_vals);
    create index lots_few on t(lots_vals, few_vals);
    create index few_lots on t(few_vals, lots_vals);
    
    select index_name, leaf_blocks, distinct_keys, clustering_factor
    from   user_indexes
    where  table_name = 'T';
    
    INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
    FEW_LOTS    47           10,000         530                
    LOTS_FEW    47           10,000         53                 
    LOTS        31           10,000         53                 
    FEW         31           10             530    
    

    Beachten Sie, dass der Clustering-Faktor für wenige_Lose 10x höher ist als für viele und viele_Lose! Und dies ist eine Demo-Tabelle mit zunächst perfektem Clustering. In realen Datenbanken ist der Effekt wahrscheinlich schlimmer.

    Was ist daran so schlimm?

    Der Clustering-Faktor ist einer der Schlüsselfaktoren dafür, wie "attraktiv" ein Index ist. Je höher es ist, desto weniger wahrscheinlich ist es, dass der Optimierer es wählt. Insbesondere, wenn lot_vals nicht eindeutig sind, aber normalerweise nur wenige Zeilen pro Wert haben. Wenn Sie Pech haben, könnte dies ausreichen, um den Optimierer glauben zu lassen, dass ein vollständiger Scan billiger ist ...

    OK, also haben zusammengesetzte Indizes mit wenigen_Valen und vielen_Valen nur Vorteile für Edge-Fälle.

    Was ist mit Abfragen, die einige_Werte und viele_Werte filtern?

    Einzelspaltenindizes bieten nur geringe Vorteile. Zusammen ergeben sie jedoch nur wenige Werte. Ein zusammengesetzter Index ist also eine gute Idee. Aber wohin?

    Wenn Sie zuerst einige platzieren, wird diese durch Komprimieren der führenden Spalte kleiner

    create index few_many on t(many_vals, few_vals);
    create index many_few on t(few_vals, many_vals);
    
    select index_name, leaf_blocks, distinct_keys, clustering_factor 
    from   user_indexes
    where  index_name in ('FEW_MANY', 'MANY_FEW');
    
    INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
    FEW_MANY    47           1,000          10,000             
    MANY_FEW    47           1,000          10,000   
    
    alter index few_many rebuild compress 1;
    alter index many_few rebuild compress 1;
    
    select index_name, leaf_blocks, distinct_keys, clustering_factor 
    from   user_indexes
    where  index_name in ('FEW_MANY', 'MANY_FEW');
    
    INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
    MANY_FEW    31           1,000          10,000             
    FEW_MANY    34           1,000          10,000      
    

    Mit weniger unterschiedlichen Werten in der führenden Spalte wird besser komprimiert. Das Lesen dieses Index erfordert also geringfügig weniger Arbeit. Aber nur geringfügig. Und beide sind bereits ein gutes Stück kleiner als das Original (25% weniger Größe).

    Und Sie können noch weiter gehen und den gesamten Index komprimieren!

    alter index few_many rebuild compress 2;
    alter index many_few rebuild compress 2;
    
    select index_name, leaf_blocks, distinct_keys, clustering_factor 
    from   user_indexes
    where  index_name in ('FEW_MANY', 'MANY_FEW');
    
    INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
    FEW_MANY    20           1,000          10,000             
    MANY_FEW    20           1,000          10,000   
    

    Jetzt haben beide Indizes wieder die gleiche Größe. Beachten Sie, dass dies die Tatsache ausnutzt, dass es eine Beziehung zwischen wenigen und vielen gibt. Auch hier ist es unwahrscheinlich, dass Sie diese Art von Nutzen in der realen Welt sehen werden.

    Bisher haben wir nur über Gleichstellungsprüfungen gesprochen. Bei zusammengesetzten Indizes tritt häufig eine Ungleichung mit einer der Spalten auf. z.B. Fragen wie "Bestellungen/Sendungen/Rechnungen für einen Kunden in den letzten N Tagen abrufen".

    Wenn Sie diese Art von Abfragen haben, möchten Sie die Gleichheit mit der ersten Spalte des Index:

    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals < '0000000002'
    and    many_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      12 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      12 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |     10 |     10 |00:00:00.01 |      12 |  
    |   3 |    HASH GROUP BY                       |          |      1 |     10 |     10 |00:00:00.01 |      12 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |     10 |     10 |00:00:00.01 |      12 |  
    |   5 |      INDEX RANGE SCAN                  | FEW_MANY |      1 |     10 |     10 |00:00:00.01 |       2 |  
    -------------------------------------------------------------------------------------------------------------  
    
    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals = '0000000001'
    and    many_vals < '0000000002';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    ----------------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  
    ----------------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      12 |      1 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      12 |      1 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |      2 |     10 |00:00:00.01 |      12 |      1 |  
    |   3 |    HASH GROUP BY                       |          |      1 |      2 |     10 |00:00:00.01 |      12 |      1 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      2 |     10 |00:00:00.01 |      12 |      1 |  
    |   5 |      INDEX RANGE SCAN                  | MANY_FEW |      1 |      1 |     10 |00:00:00.01 |       2 |      1 |  
    ----------------------------------------------------------------------------------------------------------------------  
    

    Beachten Sie, dass sie den entgegengesetzten Index verwenden.

    TL; DR

    • Spalten mit Gleichheitsbedingungen sollten im Index an erster Stelle stehen.
    • Wenn Ihre Abfrage mehrere Spalten mit Gleichheiten enthält, bietet die Platzierung der Spalte mit den wenigsten unterschiedlichen Werten den besten Komprimierungsvorteil
    • Obwohl Index-Skip-Scans möglich sind, müssen Sie sicher sein, dass dies auf absehbare Zeit eine praktikable Option bleibt
    • Zusammengesetzte Indizes mit nahezu eindeutigen Spalten bieten nur minimale Vorteile. Stellen Sie sicher, dass Sie die 1-2 E/A wirklich speichern müssen

    1: In einigen Fällen kann es sinnvoll sein, eine Spalte in einen Index aufzunehmen, wenn dies bedeutet, dass sich alle Spalten in Ihrer Abfrage im Index befinden. Dies ermöglicht einen Index-Scan, sodass Sie nicht auf die Tabelle zugreifen müssen.

    2: Wenn Sie für Diagnose und Optimierung lizenziert sind, können Sie den Plan mit SQL Plan Management zu einem Skip-Scan zwingen

    [~ # ~] hinzugefügt und [~ # ~]

    PS - die Dokumente, die Sie dort zitiert haben, stammen von 9i. Das ist wirklich alt. Ich würde bei etwas bleiben neueren

    3
    Chris Saxon

    Es gibt weitere Elemente der Abfrage, die zur endgültigen Entscheidung beitragen, mit was ein zusammengesetzter Index beginnen und/oder neben der Selektivität der Spalte enthalten soll.

    zum Beispiel:

    1. welche Art von Abfrageoperator wird verwendet: Wenn Abfragen Operatoren wie haben
      ">,> =, <, <="
    2. Wie viele tatsächliche Zeilen werden als Ergebnis der Abfrage erwartet: Wird das Abfrageergebnis die meisten Zeilen aus der Tabelle sein?.
    3. Werden während der Where-Klausel Funktionen für die Tabellenspalte verwendet? Wenn die Abfrage eine Funktion UPPER, LOWER, TRIM, SUBSTRING hat, die für die Spalte verwendet wird, die in der WHERE-Bedingung verwendet wird.

    um das Gespräch relevant zu halten, gilt meine folgende Antwort für die folgende Situation:

    1. "90% der Abfragetypen in einer bestimmten Tabelle haben die WHERE-Klausel mit operator ="
    2. "höchstens die Abfrage gibt die 10% der gesamten Zeilen in der Tabelle als Ergebnis zurück"
    3. "Für die Tabellenspalte in der WHERE-Klausel werden keine Funktionen verwendet."
    4. "Die meisten der in der WHERE-Klausel verwendeten Zeitspalten haben die Typennummer.
      string "

    Nach meiner Erfahrung ist es beides, worauf DBA achten sollte.

    Stellen wir uns vor, es wird nur eine Regel angewendet:

    1) Wenn ich einen Index erstelle, wobei die selektivste Spalte an erster Stelle steht, diese Spalte jedoch von den meisten Abfragen in dieser Tabelle nicht verwendet wird, ist sie für die DB-Engine nicht geeignet.

    2) Wenn ich einen Index erstelle, wobei die am häufigsten verwendete Spalte in einer Abfrage an erster Stelle im Index steht, die Spalte jedoch eine geringe Selektivität aufweist, ist auch meine Abfrageleistung nicht gut.

    Ich werde Spalten auflisten, die meistens in 90% der Tabellenabfragen verwendet werden. Dann setzen Sie diese nur in der Reihenfolge der meisten Kardinalität bis zur geringsten Kardinalität.

    Wir verwenden Indizes, um die Leistung der Leseabfrage zu verbessern, und dieser Workflow (Typen einer Leseabfrage) sollte nur die Indexerstellung vorantreiben. Wenn die Daten wachsen (Milliarden von Zeilen), kann der komprimierte Index zwar Speicherplatz sparen, die Leseabfrageleistung jedoch beeinträchtigen.

    1
    Anup Shah

    Theoretisch ergibt die selektivste Spalte die schnellste Suche. Aber bei der Arbeit bin ich gerade auf eine Situation gestoßen, in der wir einen zusammengesetzten Index von 3 Teilen haben, wobei der selektivste Teil zuerst ist. (Datum, Autor, Verlag, sagen wir, in dieser Reihenfolge überwacht die Tabelle die Daumen nach oben auf den Posts) und ich habe eine Abfrage, die alle drei Teile verwendet. MySQL verwendet standardmäßig nur den Index des Autors, wobei der zusammengesetzte Index mit Firma und Datum übersprungen wird, obwohl diese in meiner Abfrage vorhanden sind. Ich habe Force Index verwendet, um das Composite zu verwenden, und die Abfrage lief tatsächlich langsamer. Warum ist das passiert? Ich werde dir sagen:

    Ich habe einen Bereich für das Datum ausgewählt. Obwohl das Datum sehr selektiv ist, hat die Tatsache, dass wir ihn für Bereichsscans verwenden (obwohl der Bereich relativ kurz ist, 6 Monate aus 6 Jahren Daten), das Komposit schädlich gemacht MySQL. Um das Komposit in diesem speziellen Fall zu verwenden, muss mysql alle Artikel erfassen, die seit Neujahr geschrieben wurden, und dann untersuchen, wer der Autor ist. Da der Autor im Vergleich zu anderen Autoren nicht so viele Artikel geschrieben hat, hat mysql es vorgezogen, nur diesen Autor zu finden .

    In einem anderen Fall lief die Abfrage auf dem Composite viel schneller. In diesem Fall war ein Autor sehr beliebt und besaß die meisten Datensätze. Eine Sortierung nach Datum war sinnvoll. Aber MySQL hat diesen Fall nicht automatisch erkannt, ich musste den Index erzwingen ... Sie wissen also, es variiert. Entfernungsscans können Ihre selektive Spalte unbrauchbar machen. Die Verteilung der Daten kann dazu führen, dass Spalten für verschiedene Datensätze selektiver sind ...

    Was ich anders machen würde, ist das Datum (das theoretisch am selektivsten ist) nach rechts zu verschieben, da ich weiß, dass ich jetzt einen Entfernungsscan durchführen werde, und das macht einen Unterschied.

    1
    Joe Yahchouchi