Bei einer Zeichenfolge, die mehrere Instanzen eines Trennzeichens enthalten kann, möchte ich alle Teilzeichenfolgen generieren, die nach diesem Zeichen beginnen.
Wenn ich beispielsweise einen String wie 'a.b.c.d.e'
(oder ein Array {a,b,c,d,e}
, nehme ich an) gebe, möchte ich ein Array wie folgt generieren:
{a.b.c.d.e, b.c.d.e, c.d.e, d.e, e}
Die beabsichtigte Verwendung dient als Auslöser zum Füllen einer Spalte, um das Abfragen von Domainnamen-Teilen zu vereinfachen (dh alle q.x.t.com
zur Abfrage zu finden t.com
), wenn in eine andere Spalte geschrieben wird.
Es scheint ein unangenehmer Weg zu sein, dies zu lösen (und es kann sehr gut sein), aber jetzt bin ich gespannt, wie eine solche Funktion in (Postgres ') SQL geschrieben werden könnte.
Da es sich um E-Mail-Domain-Namen handelt, ist es schwer zu sagen, wie viele Elemente maximal möglich sind, aber die überwiegende Mehrheit wäre sicherlich <5.
quelle
Antworten:
Ich glaube nicht, dass Sie hier eine separate Spalte benötigen. Dies ist ein XY-Problem. Sie versuchen nur, eine Suffix-Suche durchzuführen. Es gibt zwei Möglichkeiten, dies zu optimieren.
Verwandeln Sie die Suffix-Abfrage in eine Präfix-Abfrage
Sie tun dies im Grunde, indem Sie alles umkehren.
Erstellen Sie zuerst einen Index auf der Rückseite Ihrer Spalte:
Dann fragen Sie mit dem gleichen ab:
Sie können einen
UPPER
Anruf tätigen, wenn Sie die Groß- und Kleinschreibung nicht berücksichtigen möchten:Trigrammindizes
Die andere Option sind Trigrammindizes. Sie sollten dies auf jeden Fall verwenden, wenn Sie Infix-Abfragen benötigen (
LIKE 'something%something'
oderLIKE '%something%'
Abfragen eingeben).Aktivieren Sie zuerst die Trigrammindexerweiterung:
(Dies sollte mit PostgreSQL ohne zusätzliche Installation geliefert werden.)
Erstellen Sie dann einen Trigrammindex für Ihre Spalte:
Dann wählen Sie einfach:
Auch hier können Sie eine einwerfen
UPPER
, um die Groß- und Kleinschreibung nicht zu berücksichtigen, wenn Sie möchten:Ihre Frage wie geschrieben
Trigram-Indizes funktionieren tatsächlich mit einer etwas allgemeineren Form dessen, was Sie unter der Haube verlangen. Es zerlegt die Zeichenfolge in Stücke (Trigramme) und erstellt darauf basierend einen Index. Der Index kann dann verwendet werden, um viel schneller nach Übereinstimmungen zu suchen als bei einem sequentiellen Scan, jedoch nach Infix- sowie Suffix- und Präfixabfragen. Versuchen Sie immer zu vermeiden, das neu zu erfinden, was jemand anderes entwickelt hat, wenn Sie können.
Credits
Die beiden Lösungen stammen ziemlich wörtlich aus der Auswahl einer PostgreSQL-Textsuchmethode . Ich empfehle dringend, es zu lesen, um eine detaillierte Analyse der verfügbaren Textsuchoptionen in PotsgreSQL zu erhalten.
quelle
Ich denke das ist mein Favorit.
REIHEN
ARRAYS
quelle
REIHEN
ODER
ARRAYS
ODER
quelle
Frage gestellt
Testtabelle:
Rekursiver CTE in einer LATERAL-Unterabfrage
Das
CROSS JOIN LATERAL
(, LATERAL
kurz) ist sicher, da das Gesamtergebnis der Unterabfrage immer eine Zeile zurückgibt. Du erhältst ...str = ''
in der Basistabellestr IS NULL
in der BasistabelleEingepackt mit einem billigen Array-Konstruktor in der Unterabfrage, also keine Aggregation in der äußeren Abfrage.
Ein Vorzeigeobjekt für SQL-Funktionen, aber der rCTE-Overhead kann die Spitzenleistung beeinträchtigen.
Brute Force für eine triviale Anzahl von Elementen
Für Ihren Fall mit einer trivial kleinen Anzahl von Elementen kann ein einfacher Ansatz ohne Unterabfrage schneller sein:
Angenommen, maximal 5 Elemente, wie Sie kommentiert haben. Sie können leicht für mehr erweitern.
Wenn eine bestimmte Domäne weniger Elemente enthält, geben überschüssige
substring()
Ausdrücke NULL zurück und werden von entferntarray_remove()
.Tatsächlich kann der
right(str, strpos(str, '.')
mehrmals verschachtelte Ausdruck von oben ( ) schneller sein (obwohl er schwer zu lesen ist), da Funktionen für reguläre Ausdrücke teurer sind.Eine Abzweigung von @ Dudus Abfrage
@ Dudus intelligente Abfrage könnte verbessert werden mit
generate_subscripts()
:Wird auch verwendet
LEFT JOIN LATERAL ... ON true
, um mögliche Zeilen mit NULL-Werten beizubehalten.PL / pgSQL-Funktion
Ähnliche Logik wie beim rCTE. Wesentlich einfacher und schneller als das, was Sie haben:
Der
OUT
Parameter wird am Ende der Funktion automatisch zurückgegeben.Eine Initialisierung ist nicht erforderlich
result
, daNULL::text[] || text 'a' = '{a}'::text[]
.Dies funktioniert nur mit
'a'
der richtigen Eingabe.NULL::text[] || 'a'
(String-Literal) würde einen Fehler auslösen, da Postgres denarray || array
Operator auswählt .strpos()
Gibt zurück,0
wenn kein Punkt gefunden wurde.right()
Gibt also eine leere Zeichenfolge zurück und die Schleife endet.Dies ist wahrscheinlich die schnellste aller Lösungen hier.
Alle funktionieren in Postgres 9.3+
(mit Ausnahme der kurzen Array-Slice-Notation
arr[3:]
. Ich habe eine Obergrenze in die Geige eingefügt, damit sie in Seite 9.3 funktioniert :arr[3:999]
.)SQL Fiddle.
Anderer Ansatz zur Optimierung der Suche
Ich bin bei @ jpmc26 (und bei Ihnen ): Ein völlig anderer Ansatz ist vorzuziehen. Ich mag die Kombination von jpmc26
reverse()
und atext_pattern_ops
.Ein Trigrammindex wäre für Teil- oder Fuzzy-Übereinstimmungen überlegen. Da Sie jedoch nur an ganzen Wörtern interessiert sind , ist die Volltextsuche eine weitere Option. Ich erwarte eine wesentlich kleinere Indexgröße und damit eine bessere Performance.
pg_trgm sowie FTS unterstützen Abfragen, bei denen die Groß- und Kleinschreibung nicht berücksichtigt wird.
Hostnamen wie
q.x.t.com
odert.com
(Wörter mit Inline-Punkten) werden als Typ "Host" identifiziert und als ein Wort behandelt . Es gibt aber auch Präfix-Matching in FTS (was manchmal übersehen zu werden scheint). Das Handbuch:Mit der intelligenten Idee von @ jpmc26
reverse()
können wir Folgendes erreichen :Welches wird von einem Index unterstützt:
Beachten Sie die
'simple'
Konfiguration: Wir möchten nicht, dass der Stemming oder Thesaurus mit der Standardkonfiguration'english'
verwendet wird.Alternativ (mit einer größeren Anzahl möglicher Abfragen) könnten wir die neue Phrasensuchfunktion der Textsuche in Postgres 9.6 verwenden. Die Versionshinweise:
Abfrage:
Ersetzen Sie dot (
'.'
) durch space (' '
), um zu verhindern, dass der Parser 't.com' als Hostnamen klassifiziert, und verwenden Sie stattdessen jedes Wort als separates Lexem.Und dazu ein passender Index:
quelle
Ich habe mir etwas Semi-Workable ausgedacht, aber ich würde gerne Feedback zu diesem Ansatz erhalten. Ich habe sehr wenig PL / pgSQL geschrieben, daher habe ich das Gefühl, dass alles, was ich tue, ziemlich hackig ist und ich bin überrascht, wenn es funktioniert.
Trotzdem habe ich hier Folgendes erreicht:
Das funktioniert so:
quelle
Ich benutze die Fensterfunktion:
Ergebnis:
quelle
Eine Variante der Lösung von @Dudu Markovitz, die auch mit PostgreSQL-Versionen funktioniert, die [i:] (noch) nicht erkennen:
quelle