SQLite-Funktionen, die Sie möglicherweise übersehen haben

Wenn Sie SQLite verwenden, aber dessen Entwicklung nicht verfolgen , sind möglicherweise einige Dinge, die den Code einfacher machen und Abfragen beschleunigen, unbemerkt geblieben. Unter dem Schnitt habe ich versucht, die wichtigsten von ihnen aufzulisten.



Teilcodes (Teilindizes)

Beim Erstellen eines Index können Sie eine Bedingung für eine Zeile angeben, die in den Index aufgenommen werden soll. Beispielsweise ist eine der Spalten nicht leer, die andere entspricht dem angegebenen Wert.



create index idx_partial on tab1(a, b) where a is not null and b = 5;
select * from tab1 where a is not null and b = 5; --> search table tab1 using index


Indizes für den Ausdruck (Indizes für Ausdrücke)

Wenn ein Ausdruck häufig in Abfragen für eine Tabelle verwendet wird, können Sie einen Index darauf erstellen. Es sollte jedoch berücksichtigt werden, dass, obwohl der Optimierer nicht sehr flexibel ist, die Permutation von Spalten im Ausdruck dazu führt, dass die Verwendung des Index abgelehnt wird.



create index idx_expression on tab1(a + b);
select * from tab1 where a + b > 10; --> search table tab1 using index ...
select * from tab1 where b + a > 10; --> scan table


Berechnete Spalte (generierte Spalten)

Wenn die Spaltendaten das Ergebnis der Auswertung eines Ausdrucks in anderen Spalten sind, können Sie eine virtuelle Spalte erstellen. Es gibt zwei Typen: VIRTUAL (berechnet jedes Mal, wenn die Tabelle gelesen wird und keinen Speicherplatz beansprucht) und STORED (berechnet, wenn Daten in die Tabelle geschrieben werden und Speicherplatz beanspruchen). Natürlich können Sie keine Daten direkt in solche Spalten schreiben.



create table tab1 (
	a integer primary key,
	b int,
	c text,
	d int generated always as (a * abs(b)) virtual,
	e text generated always as (substr(c, b, b + 1)) stored
);


R-Tree-Index

Der Index ist für die schnelle Suche in einem Wertebereich / Verschachtelung von Objekten vorgesehen, d.h. Aufgaben, die für Geosysteme typisch sind, wenn rechteckige Objekte durch ihre Position und Größe angegeben werden und alle Objekte gefunden werden müssen, die sich mit dem aktuellen Objekt überschneiden. Dieser Index ist als virtuelle Tabelle implementiert (siehe unten) und dies ist nur im Wesentlichen ein Index. Für die Unterstützung des R-Tree-Index muss SQLite mit einem Flag erstellt werden SQLITE_ENABLE_RTREE(standardmäßig nicht aktiviert).



create virtual table idx_rtree using rtree (
	id,              -- 
	minx, maxx,      --   c x 
	miny, maxy,      --   c y 
	data             --    
);  

insert into idx_rtree values (1, -80.7749, -80.7747, 35.3776, 35.3778); 
insert into idx_rtree values (2, -81.0, -79.6, 35.0, 36.2);

select id from idx_rtree 
where minx >= -81.08 and maxx <= -80.58 and miny >= 35.00  and maxy <= 35.44;


Spalte umbenennen

SQLite unterstützt Änderungen in der Struktur von Tabellen nur unzureichend. Daher können Sie nach dem Erstellen einer Tabelle keine Einschränkung ändern oder eine Spalte löschen. Seit Version 3.25.0 können Sie eine Spalte umbenennen, aber ihren Typ nicht ändern.



alter table tbl1 rename column a to b;


Für andere Operationen wird auch alles vorgeschlagen, um eine Tabelle mit der gewünschten Struktur zu erstellen, Daten dort zu übertragen, die alte zu löschen und die neue umzubenennen.



Zeile hinzufügen , sonst aktualisieren (Upsert)

Mit der on conflictOperatorklasse insertkönnen Sie eine neue Zeile hinzufügen. Wenn Sie bereits eine Zeile mit demselben Wert pro Schlüssel haben, aktualisieren Sie diese.



create table vocabulary (word text primary key, count int default 1);
insert into vocabulary (word) values ('jovial') 
  on conflict (word) do update set count = count + 1;


Update von Anweisung

Wenn eine Zeile basierend auf Daten aus einer anderen Tabelle aktualisiert werden muss, mussten Sie zuvor für jede Spalte oder eine Unterabfrage verwenden with. Seit Version 3.33.0 wurde der Operator updateum ein Schlüsselwort erweitert, fromund jetzt können Sie dies tun



update inventory
   set quantity = quantity - daily.amt
  from (select sum(quantity) as amt, itemid from sales group by 2) as daily
 where inventory.itemid = daily.itemid;


CTE-Abfragen, Klasse mit (Common Table Expression)

Die Klasse withkann als temporäre Darstellung für eine Anforderung verwendet werden. In Version 3.34.0 wurde die Möglichkeit der Verwendung von withInnen erklärt with.



with tab2 as (select * from tab1 where a > 10), 
  tab3 as (select * from tab2 inner join ...)
select * from tab3;


Mit dem Zusatz von einem Schlüsselwort recursive, withkann es für Abfragen verwendet werden , wenn Sie auf die zugehörigen Daten bedienen möchten.



--  
with recursive cnt(x) as (
  values(1) union all select x + 1 from cnt where x < 1000
)
select x from cnt;

--         
create table tab1 (id, parent_id);
insert into tab1 values 
  (1, null), (10, 1), (11, 1), (12, 10), (13, 10),
  (2, null), (20, 2), (21, 2), (22, 20), (23, 21);

--    
with recursive tc (id) as (
	select id from tab1 where id = 10	
	union 
	select tab1.id from tab1, tc where tab1.parent_id = tc.id
)

--      
with recursive tc (id, parent_id) as (
	select id, parent_id from tab1 where id in (12, 21)
	union 
	select tc.parent_id, tab1.parent_id 
	from tab1, tc where tab1.id = tc.parent_id
)
select distinct id from tc where parent_id is null order by 1;

--    , .   
create table org(name text primary key, boss text references org);
insert into org values ('Alice', null), 
  ('Bob', 'Alice'), ('Cindy', 'Alice'), ('Dave', 'Bob'), 
  ('Emma', 'Bob'), ('Fred', 'Cindy'), ('Gail', 'Cindy');

with recursive
  under_alice (name, level) as (
    values('Alice', 0)
    union all
    select org.name, under_alice.level + 1
      from org join under_alice on org.boss = under_alice.name
     order by 2
  )
select substr('..........', 1, level * 3) || name from under_alice;


Fensterfunktion (Fensterfunktionen)

Seit Version 3.25.0 sind in SQLite Fensterfunktionen, manchmal auch als Analysefunktionen bezeichnet, verfügbar, mit denen Sie Berechnungen für ein Datenelement (Fenster) durchführen können.



--    
create table tab1 (x integer primary key, y text);
insert into tab1 values (1, 'aaa'), (2, 'ccc'), (3, 'bbb');
select x, y, row_number() over (order by y) as row_number from tab1 order by x;

--     
create table tab1 (a integer primary key, b, c);
insert into tab1 values (1, 'A', 'one'),
  (2, 'B', 'two'), (3, 'C', 'three'), (4, 'D', 'one'), 
  (5, 'E', 'two'), (6, 'F', 'three'), (7, 'G', 'one');

--        
select a, b, group_concat(b, '.') over (order by a rows between 1 preceding and 1 following) as prev_curr_next from tab1;

--    (,   c)       
select c, a, b, group_concat(b, '.') over (partition by c order by a range between current row and unbounded following) as curr_end from tab1 order by c, a;

--      
select c, a, b, group_concat(b, '.') filter (where c <> 'two') over (order by a) as exceptTwo from t1 order by a;


SQLite-Dienstprogramme

Neben der sqlite3- CLI stehen zwei weitere Dienstprogramme zur Verfügung. Mit dem ersten - sqldiff können Sie Datenbanken (oder eine separate Tabelle) nicht nur nach Struktur, sondern auch nach Daten vergleichen. Der zweite, sqlite3_analizer, wird verwendet, um Informationen darüber anzuzeigen, wie Speicherplatz von Tabellen und Indizes in der Datenbankdatei effektiv genutzt wird. Ähnliche Informationen können aus der virtuellen Tabelle dbstat abgerufen werden (erfordert SQLITE_ENABLE_DBSTAT_VTABbeim Kompilieren von SQLite ein Flag ).



Seit Version 3.22.0 enthält die CLI sqlite3 einen (experimentellen) .expert-Befehl, mit dem Sie feststellen können, welcher Index für die eingegebene Abfrage hinzugefügt werden soll.



Erstellen Sie ein Vakuum in Backup

Seit Version 3.27.0 wurde der Befehl vacuumum ein Schlüsselwort erweitert into, mit dem Sie eine Kopie der Datenbank erstellen können, ohne sie direkt aus SQL zu stoppen. Es ist eine einfache Alternative zur Backup-API .



vacuum into 'D:/backup/' || strftime('%Y-%M-%d', 'now') || '.sqlite';


Printf-Funktion

Die Funktion ist analog zur C-Funktion. In diesem Fall werden die NULL-Werte als leere Zeichenfolge für %sund 0für den Zahlenplatzhalter interpretiert .



select 'a' || ' 123 ' || null; --> null
select printf('%s %i %s', 'a', 123, null); --> 123 a
select printf('%s %i %i', 'a', 123, null); --> 123 a 0


Zeit und Datum

In SQLite DateundTime . Obwohl es möglich ist, eine Tabelle mit Spalten dieser Typen zu erstellen, entspricht dies der Erstellung von Spalten ohne Angabe eines Typs, sodass die Daten in solchen Spalten als Text gespeichert werden. Dies ist praktisch beim Anzeigen von Daten, hat jedoch eine Reihe von Nachteilen: Ineffektive Suche: Wenn kein Index vorhanden ist, nehmen die Daten viel Platz ein und es gibt keine Zeitzone. Um dies zu vermeiden, können Sie die Daten als Unix-Zeit speichern , d. H. Anzahl der Sekunden seit Mitternacht 01.01.1970.



select strftime('%Y-%M-%d %H:%m', 'now'); --> UTC 
select strftime('%Y-%M-%d %H:%m', 'now', 'localtime'); -->  
select strftime('%s', 'now'); --  Unix- 
select strftime('%s', 'now', '+2 day'); -->  unix-   
--  unix-     - 21-11-2020 15:25:14
select strftime('%d-%m-%Y %H:%M:%S', 1605961514, 'unixepoch', 'localtime')


Json

Seit Version 3.9.0 können Sie in SQLite mit json arbeiten (entweder ist ein Flag zur SQLITE_ENABLE_JSON1Kompilierungszeit oder eine geladene Erweiterung erforderlich ). Json - Daten werden als Text gespeichert. Das Ergebnis der Funktionen ist auch Text.



select json_array(1, 2, 3); --> [1,2,3] ()
select json_array_length(json_array(1, 2, 3)); --> 3
select json_array_length('[1,2,3]'); --> 3
select json_object('a', json_array(2, 5), 'b', 10); --> {"a":[2,5],"b":10} ()
select json_extract('{"a":[2,5],"b":10}', '$.a[0]');  --> 2
select json_insert('{"a":[2,5]}', '$.c', 10); --> {"a":[2,5],"c":10} ()
select value from json_each(json_array(2, 5)); --> 2  2, 5
select json_group_array(value) from json_each(json_array(2, 5)); --> [2,5] ()


Volltextsuche

Wie bei json muss bei der Volltextsuche SQLITE_ENABLE_FTS5beim Kompilieren oder Laden einer Erweiterung ein Flag gesetzt werden . Um mit der Suche zu arbeiten, wird zunächst eine virtuelle Tabelle mit indizierten Feldern erstellt und anschließend die Daten mit der üblichen Tabelle dort geladen insert. Es ist zu beachten, dass die Erweiterung für ihren Betrieb zusätzliche Tabellen erstellt und die erstellte virtuelle Tabelle ihre Daten verwendet.



create virtual table emails using fts5(sender, body);
SELECT * FROM emails WHERE emails = 'fts5'; -- sender  body  fts5


Erweiterungen

SQLite-Funktionen können über ladbare Module hinzugefügt werden. Einige von ihnen wurden bereits oben erwähnt - json1 und die fts .



Erweiterungen können verwendet werden, um benutzerdefinierte Funktionen (nicht nur skalar, wie zum Beispiel crc32, sondern auch aggregierend oder sogar mit Fenstern ) und virtuelle Tabellen hinzuzufügen . Virtuelle Tabellen sind Tabellen, die in der Datenbank vorhanden sind, deren Daten jedoch von der Erweiterung verarbeitet werden , während einige von ihnen je nach Implementierung erstellt werden müssen



create virtual table temp.tab1 using csv(filename='thefile.csv');
select * from tab1;


Andere, sogenannte Tabellenwerte , können sofort verwendet werden.



select value from generate_series(5, 100, 5);
...

Einige der virtuellen Tabellen sind hier aufgelistet .



Eine Erweiterung kann sowohl Funktionen als auch virtuelle Tabellen implementieren. Zum Beispiel enthält json1 13 Skalar- und 2 Aggregatfunktionen sowie zwei virtuelle Tabellen json_eachund json_tree. Um Ihre eigene Funktion zu schreiben, müssen Sie lediglich Grundkenntnisse in C haben und den Erweiterungscode aus dem SQLite-Repository analysieren . Das Implementieren eigener virtueller Tabellen ist etwas komplizierter (anscheinend gibt es deshalb nur wenige davon). Hier können wir das etwas veraltete Buch Using SQLite von Jay A. Kreibich , den Artikel von Michael Owens , die Vorlage aus dem Repository und den Code generate_series als tabellenwertige Funktionen empfehlen .



Darüber hinaus können Erweiterungen betriebssystemspezifische Elemente wie das Dateisystem implementieren, um Portabilität zu gewährleisten. Details finden Sie hier .



Verschiedenes



  • Verwenden Sie '(einfaches Anführungszeichen) für Zeichenfolgenkonstanten und "(doppeltes Anführungszeichen) für Spalten- und Tabellennamen.
  • Um Informationen zu Tabelle tab1 zu erhalten, können Sie verwenden



    --  main 
    select * from pragma_table_info('tab1');
    --  temp    (attach) 
    select * from pragma_table_info('tab1') where schema = 'temp'
  • SQLite hat ein eigenes offizielles Forum , in dem der Schöpfer von SQLite - Richard Hipp - teilnimmt und in dem Sie einen Fehlerbericht veröffentlichen können.

  • SQLite-Editoren: SQLite Studio , DB Browser für SQLite und ( ads !) Sqlite-gui (nur Windows).




All Articles