SQLite3 verwendet keinen Abdeckungsindex mit dem Ausdruck json_extract

8

Ich versuche, in SQLite3(3.18) einen Index mit json_extractAusdrücken zu erstellen . Mein Ziel ist es, Abfragen auszuführen, für die nur der Index erforderlich ist, um Ergebnisse zu erzielen. Der Grund dafür ist, dass json_extractes sich um einen teuren Vorgang handelt, der die Leistung beim Betrieb mit größeren Datenmengen und / oder Werten beeinträchtigen würde. Ich kam zu dem Schluss, dass ich einen Deckungsindex brauche, der meinen Bedürfnissen entspricht.

Schritt 1 - Testen der Theorie unter Verwendung einer normalen Tabellenstruktur

CREATE TABLE Player (
    Id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
    FirstName TEXT NOT NULL,
    MiddleName TEXT,
    LastName TEXT NOT NULL
);

CREATE INDEX Player_FirstName ON Player (
    FirstName ASC,
    LastName ASC
);

EXPLAIN QUERY PLAN SELECT
    FirstName, LastName
FROM
    Player
WHERE
    LENGTH(LastName) > 10
ORDER BY
    FirstName
LIMIT
    10
OFFSET
    0

Erträge

SCAN TABLE Player USING COVERING INDEX Player_FirstName

Genau das erwarte ich. Der Abfrageplaner stellte fest, dass der Player_FirstNameIndex aufgrund der ORDER BYKlausel angemessen ist. Da die WHEREAnweisung nur mit einem Wert arbeitet, der sich auch in diesem Index befindet, muss die Tabelle nicht gelesen werden. Schließlich ist die SELECTenthält Anweisung nur die indizierten Spalten daher in einer Abfrage führt , dass die Tabelle nicht berühren überhaupt .

Schritt 2 - Testen der Theorie mit einem Extraktausdruck

CREATE TABLE PlayerJ (
    Id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
    Data TEXT NOT NULL
);

CREATE INDEX PlayerJ_FirstName ON PlayerJ (
    JSON_EXTRACT(Data, '$.FirstName') ASC,
    JSON_EXTRACT(Data, '$.LastName') ASC
);

EXPLAIN QUERY PLAN SELECT
    JSON_EXTRACT(Data, '$.FirstName') AS FirstName,
    JSON_EXTRACT(Data, '$.LastName') AS LastName
FROM
    PlayerJ
WHERE
    LENGTH(LastName) > 10
ORDER BY
    FirstName
LIMIT
    10
OFFSET
    0

Erträge

SCAN TABLE PlayerJ USING INDEX PlayerJ_FirstName

Das ist nicht , was ich erwartet hatte. Der Abfrageplaner scheint herausgefunden zu haben, dass die ORDER BYKlausel aktiviert ist JSON_EXTRACT(Data, '$.FirstName'), und scheint daher den entsprechenden Index ausgewählt zu haben. Aber hier endet meine Argumentation abrupt. Was geht hier vor sich? Ich hätte erwartet, dass der Abfrageplaner herausfindet, dass dies derselbe wie der vorherige Test ist und der Index als Deckungsindex verwendet wird. Aber das tut es nicht.

Warum nicht? Und wie kann dieser zweite Test so geändert werden, dass er nur für den Index ausgeführt wird?

Hozuki
quelle

Antworten:

2

Die Dokumentation sagt:

Der SQLite-Abfrageplaner wird die Verwendung eines Index für einen Ausdruck in Betracht ziehen, wenn der indizierte Ausdruck in der WHERE-Klausel oder in der ORDER BY-Klausel einer Abfrage angezeigt wird.

Ausdrücke in der SELECT-Klausel verwenden daher nicht den Ausdrucksindex.

Die Verwendung eines Abdeckungsindex ist weniger eine Verbesserung gegenüber der Verwendung eines normalen Index zum Suchen / Sortieren als die Verwendung eines normalen Index, wenn überhaupt kein Index verwendet wird. Daher wurde diese Optimierung (noch?) Für Ausdrucksindizes nicht implementiert.

CL.
quelle
Die Verwendung der JSON_EXTRACT-Ausdrücke in der WHERE- und / oder ORDER BY-Anweisung ist äquivalent, da ich lediglich einen Alias ​​mit den SELECT-Feldern erstellt habe. Das Umschreiben der Abfrage zur Verwendung von JSON_EXTRACT anstelle des Alias ​​führt zu keinem anderen Ergebnis.
Hozuki