Indexierte Daten durchsuchen
Diese Seite enthält Beispiele für die Suche in BigQuery.
Wenn Sie Ihre Daten indexieren, kann BigQuery einige Abfragen optimieren, die die Funktion SEARCH
oder andere Funktionen und Operatoren wie z. B. =
, IN
, LIKE
oder STARTS_WITH
verwenden.
SQL-Abfragen geben korrekte Ergebnisse aus allen aufgenommenen Daten zurück, selbst wenn einige Daten noch nicht indexiert sind. Die Abfrageleistung kann jedoch mit einem Index erheblich verbessert werden. Einsparungen bei den verarbeiteten Byte und Slot-Millisekunden werden maximiert, wenn die Anzahl der Suchergebnisse einen relativ kleinen Teil der Gesamtzahl an Zeilen in Ihrer Tabelle ausmacht, da weniger Daten gescannt werden. Informationen dazu, ob ein Index für eine Abfrage verwendet wurde, finden Sie unter Nutzung des Suchindex.
Suchindex erstellen
Die folgende Tabelle mit dem Namen Logs
wird verwendet, um verschiedene Möglichkeiten zur Verwendung der Funktion SEARCH
zu zeigen. Diese Beispieltabelle ist ziemlich klein, aber in der Praxis erhöht sich die Leistungssteigerung durch SEARCH
mit zunehmender Größe der Tabelle.
CREATE TABLE my_dataset.Logs (Level STRING, Source STRING, Message STRING) AS ( SELECT 'INFO' as Level, '65.177.8.234' as Source, 'Entry Foo-Bar created' as Message UNION ALL SELECT 'WARNING', '132.249.240.10', 'Entry Foo-Bar already exists, created by 65.177.8.234' UNION ALL SELECT 'INFO', '94.60.64.181', 'Entry Foo-Bar deleted' UNION ALL SELECT 'SEVERE', '4.113.82.10', 'Entry Foo-Bar does not exist, deleted by 94.60.64.181' UNION ALL SELECT 'INFO', '181.94.60.64', 'Entry Foo-Baz created' );
Die Tabelle sieht so aus:
+---------+----------------+-------------------------------------------------------+ | Level | Source | Message | +---------+----------------+-------------------------------------------------------+ | INFO | 65.177.8.234 | Entry Foo-Bar created | | WARNING | 132.249.240.10 | Entry Foo-Bar already exists, created by 65.177.8.234 | | INFO | 94.60.64.181 | Entry Foo-Bar deleted | | SEVERE | 4.113.82.10 | Entry Foo-Bar does not exist, deleted by 94.60.64.181 | | INFO | 181.94.60.64 | Entry Foo-Baz created | +---------+----------------+-------------------------------------------------------+
Erstellen Sie mit dem Standardtextanalysator einen Suchindex für die Tabelle Logs
:
CREATE SEARCH INDEX my_index ON my_dataset.Logs(ALL COLUMNS);
Weitere Informationen zu Suchindexen finden Sie unter Suchindexe verwalten.
SEARCH
-Funktion verwenden
Die Funktion SEARCH
bietet eine tokenisierte Suche nach Daten.
SEARCH
ist für die Verwendung mit einem Index vorgesehen, um Abrufe zu optimieren.
Mit der Funktion SEARCH
können Sie in einer gesamten Tabelle suchen oder die Suche auf bestimmte Spalten beschränken.
Gesamte Tabelle durchsuchen
Die folgende Abfrage durchsucht alle Spalten der Tabelle Logs
nach dem Wert bar
und gibt die Zeilen zurück, die diesen Wert enthalten, unabhängig von der Groß-/Kleinschreibung. Da der Suchindex den Standard-Text-Analysator verwendet, müssen Sie ihn nicht in der Funktion SEARCH
angeben.
SELECT * FROM my_dataset.Logs WHERE SEARCH(Logs, 'bar');
+---------+----------------+-------------------------------------------------------+ | Level | Source | Message | +---------+----------------+-------------------------------------------------------+ | INFO | 65.177.8.234 | Entry Foo-Bar created | | WARNING | 132.249.240.10 | Entry Foo-Bar already exists, created by 65.177.8.234 | | INFO | 94.60.64.181 | Entry Foo-Bar deleted | | SEVERE | 4.113.82.10 | Entry Foo-Bar does not exist, deleted by 94.60.64.181 | +---------+----------------+-------------------------------------------------------+
Folgende Abfrage durchsucht alle Spalten der Tabelle Logs
nach dem Wert `94.60.64.181`
und gibt die Zeilen zurück, die diesen Wert enthalten. Die Backticks ermöglichen eine genaue Suche, weshalb die letzte Zeile der Tabelle Logs
, die 181.94.60.64
enthält, weggelassen wird.
SELECT * FROM my_dataset.Logs WHERE SEARCH(Logs, '`94.60.64.181`');
+---------+----------------+-------------------------------------------------------+ | Level | Source | Message | +---------+----------------+-------------------------------------------------------+ | INFO | 94.60.64.181 | Entry Foo-Bar deleted | | SEVERE | 4.113.82.10 | Entry Foo-Bar does not exist, deleted by 94.60.64.181 | +---------+----------------+-------------------------------------------------------+
In einer Teilmenge von Spalten suchen
Mit SEARCH
können Sie einfach eine Teilmenge der Spalten angeben, in denen nach Daten gesucht werden soll. Die folgende Abfrage durchsucht die Spalte Message
der Tabelle Logs
nach dem Wert 94.60.64.181
und gibt die Zeilen zurück, die diesen Wert enthalten.
SELECT * FROM my_dataset.Logs WHERE SEARCH(Message, '`94.60.64.181`');
+---------+----------------+-------------------------------------------------------+ | Level | Source | Message | +---------+----------------+-------------------------------------------------------+ | SEVERE | 4.113.82.10 | Entry Foo-Bar does not exist, deleted by 94.60.64.181 | +---------+----------------+-------------------------------------------------------+
Mit der folgenden Abfrage werden die Spalten Source
und Message
der Tabelle Logs
durchsucht. Sie gibt die Zeilen zurück, die den Wert 94.60.64.181
in einer der Spalten enthalten.
SELECT * FROM my_dataset.Logs WHERE SEARCH((Source, Message), '`94.60.64.181`');
+---------+----------------+-------------------------------------------------------+ | Level | Source | Message | +---------+----------------+-------------------------------------------------------+ | INFO | 94.60.64.181 | Entry Foo-Bar deleted | | SEVERE | 4.113.82.10 | Entry Foo-Bar does not exist, deleted by 94.60.64.181 | +---------+----------------+-------------------------------------------------------+
Spalten aus einer Suche ausschließen
Wenn eine Tabelle viele Spalten enthält und Sie die meisten davon durchsuchen möchten, ist es möglicherweise einfacher, nur die Spalten anzugeben, die von der Suche ausgeschlossen werden sollen. Mit der folgenden Abfrage wird in allen Spalten der Tabelle Logs
mit Ausnahme der Spalte Message
gesucht. Sie gibt die Zeilen aller Spalten außer Message
zurück, die den Wert 94.60.64.181
enthalten.
SELECT * FROM my_dataset.Logs WHERE SEARCH( (SELECT AS STRUCT Logs.* EXCEPT (Message)), '`94.60.64.181`');
+---------+----------------+---------------------------------------------------+ | Level | Source | Message | +---------+----------------+---------------------------------------------------+ | INFO | 94.60.64.181 | Entry Foo-Bar deleted | +---------+----------------+---------------------------------------------------+
Anderen Textanalysator verwenden
Im folgenden Beispiel wird eine Tabelle namens contact_info
mit einem Index erstellt, der die Textanalyse NO_OP_ANALYZER
verwendet:
CREATE TABLE my_dataset.contact_info (name STRING, email STRING) AS ( SELECT 'Kim Lee' AS name, 'kim.lee@example.com' AS email UNION ALL SELECT 'Kim' AS name, 'kim@example.com' AS email UNION ALL SELECT 'Sasha' AS name, 'sasha@example.com' AS email ); CREATE SEARCH INDEX noop_index ON my_dataset.contact_info(ALL COLUMNS) OPTIONS (analyzer = 'NO_OP_ANALYZER');
+---------+---------------------+ | name | email | +---------+---------------------+ | Kim Lee | kim.lee@example.com | | Kim | kim@example.com | | Sasha | sasha@example.com | +---------+---------------------+
Mit der folgenden Abfrage wird in der Spalte name
nach Kim
und in der Spalte email
nach kim
gesucht.
Da der Suchindex nicht den Standardtextanalysator verwendet, müssen Sie den Namen des Analysetools an die Funktion SEARCH
übergeben.
SELECT name, SEARCH(name, 'Kim', analyzer=>'NO_OP_ANALYZER') AS name_Kim, email, SEARCH(email, 'kim', analyzer=>'NO_OP_ANALYZER') AS email_kim FROM my_dataset.contact_info;
NO_OP_ANALYZER
ändert den Text nicht, sodass die Funktion SEARCH
nur TRUE
für genaue Übereinstimmungen zurückgibt:
+---------+----------+---------------------+-----------+ | name | name_Kim | email | email_kim | +---------+----------+---------------------+-----------+ | Kim Lee | FALSE | kim.lee@example.com | FALSE | | Kim | TRUE | kim@example.com | FALSE | | Sasha | FALSE | sasha@example.com | FALSE | +---------+----------+---------------------+-----------+
Optionen des Textanalysators konfigurieren
Die Textanalysatoren LOG_ANALYZER
und PATTERN_ANALYZER
können angepasst werden, indem den Konfigurationsoptionen ein JSON-formatierter String hinzugefügt wird. Sie können Textanalysatoren in der SEARCH
-Funktion, der CREATE
SEARCH INDEX
-DDL-Anweisung und der TEXT_ANALYZE
-Funktion konfigurieren.
Im folgenden Beispiel wird eine Tabelle namens complex_table
mit einem Index erstellt, der den Textanalysator LOG_ANALYZER
verwendet. Die Analysatoroptionen werden in einem JSON-formatierten String konfiguriert:
CREATE TABLE dataset.complex_table( a STRING, my_struct STRUCT<string_field STRING, int_field INT64>, b ARRAY<STRING> ); CREATE SEARCH INDEX my_index ON dataset.complex_table(a, my_struct, b) OPTIONS (analyzer = 'LOG_ANALYZER', analyzer_options = '''{ "token_filters": [ { "normalization": {"mode": "NONE"} } ] }''');
Die folgenden Tabellen zeigen Beispiele für Aufrufe der Funktion SEARCH
mit verschiedenen Textanalysatoren und deren Ergebnissen. Die erste Tabelle ruft die Funktion SEARCH
mit dem Standardtextanalysator LOG_ANALYZER
auf:
Funktionsaufruf | Gibt Folgendes zurück: | Grund |
---|---|---|
SEARCH('foobarexample', NULL) | FEHLER | Die search_terms sind `NULL`. |
SEARCH('foobarexample', '') | FEHLER | Die search_terms enthalten keine Tokens. |
SEARCH('foobar-example', 'foobar example') | TRUE | '-' und ' ' sind Trennzeichen. |
SEARCH('foobar-example', 'foobarexample') | FALSE | Die search_terms werden nicht aufgeteilt. |
SEARCH('foobar-example', 'foobar\\&example') | TRUE | Der doppelte umgekehrte Schrägstrich maskiert das kaufmännische Und-Zeichen, das ein Trennzeichen ist. |
SEARCH('foobar-example', R'foobar\&example') | TRUE | Der einzelne umgekehrte Schrägstrich maskiert das kaufmännische Und-Zeichen in einem Rohstring. |
SEARCH('foobar-example', '`foobar&example`') | FALSE | Die Backticks erfordern eine genaue Übereinstimmung für foobar&example. |
SEARCH('foobar&example', '`foobar&example`') | TRUE | Eine genaue Übereinstimmung wurde gefunden. |
SEARCH('foobar-example', 'example foobar') | TRUE | Die Reihenfolge der Begriffe spielt keine Rolle. |
SEARCH('foobar-example', 'foobar example') | TRUE | Tokens werden in Kleinbuchstaben geschrieben. |
SEARCH('foobar-example', '`foobar-example`') | TRUE | Eine genaue Übereinstimmung wurde gefunden. |
SEARCH('foobar-example', '`foobar`') | FALSE | Backticks behalten die Großschreibung bei. |
SEARCH('`foobar-example`', '`foobar-example`') | FALSE | Graviszeichen haben keine spezielle Bedeutung für data_to_search und |
SEARCH('foobar@example.com', '`example.com`') | TRUE | Nach dem Trennzeichen in data_to_search wird eine genaue Übereinstimmung gefunden. |
SEARCH('a foobar-example b', '`foobar-example`') | TRUE | Zwischen den Leerzeichen-Trennzeichen wird eine genaue Übereinstimmung gefunden. |
SEARCH(['foobar', 'example'], 'foobar example') | FALSE | Kein einzelner Arrayeintrag stimmt mit allen Suchbegriffen überein. |
SEARCH('foobar=', '`foobar\\=`') | FALSE | Die search_terms entsprechen foobar\=. |
SEARCH('foobar=', R'`foobar\=`') | FALSE | Dies entspricht dem vorherigen Beispiel. |
SEARCH('foobar=', 'foobar\\=') | TRUE | Das Gleichheitszeichen ist in den Daten und der Abfrage ein Trennzeichen. |
SEARCH('foobar=', R'foobar\=') | TRUE | Dies entspricht dem vorherigen Beispiel. |
SEARCH('foobar.example', '`foobar`') | TRUE | Eine genaue Übereinstimmung wurde gefunden. |
SEARCH('foobar.example', '`foobar.`') | FALSE | "foobar." wird aufgrund von Graviszeichen nicht analysiert; es ist nicht |
SEARCH('foobar..example', '`foobar.`') | TRUE | `foobar.` wird aufgrund von Graviszeichen nicht analysiert; ihm folgt |
Die folgende Tabelle zeigt Beispiele für Aufrufe der Funktion SEARCH
mit dem NO_OP_ANALYZER
-Textanalysator und Gründe für verschiedene Rückgabewerte:
Funktionsaufruf | Gibt Folgendes zurück: | Begründung |
---|---|---|
SEARCH('foobar', 'foobar', analyzer=>'NO_OP_ANALYZER') | TRUE | Eine genaue Übereinstimmung wurde gefunden. |
SEARCH('foobar', '`foobar`', analyzer=>'NO_OP_ANALYZER') | FALSE | Graviszeichen sind keine Sonderzeichen für NO_OP_ANALYZER. |
SEARCH('foobar', 'foobar', analyzer=>'NO_OP_ANALYZER') | FALSE | Die Großschreibung stimmt nicht überein. |
SEARCH('foobar example', 'foobar', analyzer=>'NO_OP_ANALYZER') | FALSE | Es gibt keine Trennzeichen für NO_OP_ANALYZER. |
SEARCH('', '', analyzer=>'NO_OP_ANALYZER') | TRUE | Es gibt keine Trennzeichen für NO_OP_ANALYZER. |
Andere Operatoren und Funktionen
Sie können Suchindexoptimierungen mit mehreren Operatoren, Funktionen und Prädikaten ausführen.
Mit Operatoren und Vergleichsfunktionen optimieren
BigQuery kann einige Abfragen optimieren, die den Gleichheitsoperator (=
), IN
-Operator, LIKE
-Operator oder die STARTS_WITH
-Funktion verwenden, um Stringliterale mit indexierten Daten zu vergleichen.
Mit Stringprädikaten optimieren
Die folgenden Prädikate können für die Optimierung des Suchindex verwendet werden:
column_name = 'string_literal'
'string_literal' = column_name
struct_column.nested_field = 'string_literal'
string_array_column[OFFSET(0)] = 'string_literal'
string_array_column[ORDINAL(1)] = 'string_literal'
column_name IN ('string_literal1', 'string_literal2', ...)
STARTS_WITH(column_name, 'prefix')
column_name LIKE 'prefix%'
Mit numerischen Prädikaten optimieren
Wenn Sie während der Vorschau Unterstützung benötigen, senden Sie eine E-Mail an bq-search-team@google.com.
Wenn der Suchindex mit numerischen Datentypen erstellt wurde, kann BigQuery einige Abfragen optimieren, die den Operator "Equal" (=
) oder IN
mit indexierten Daten verwenden. Die folgenden Prädikate können für die Optimierung des Suchindex verwendet werden:
INT64(json_column.int64_field) = 1
int64_column = 1
int64_array_column[OFFSET(0)] = 1
int64_column IN (1, 2)
struct_column.nested_int64_field = 1
struct_column.nested_timestamp_field = TIMESTAMP "2024-02-15 21:31:40"
timestamp_column = "2024-02-15 21:31:40"
timestamp_column IN ("2024-02-15 21:31:40", "2024-02-16 21:31:40")
Funktionen optimieren, die indexierte Daten generieren
BigQuery unterstützt die Optimierung des Suchindex, wenn bestimmte Funktionen auf indexierte Daten angewendet werden.
Wenn der Suchindex den Standard-Text-Analysator LOG_ANALYZER
verwendet, können Sie die Funktionen UPPER
oder LOWER
auf die Spalte anwenden, z. B. UPPER(column_name) = 'STRING_LITERAL'
.
Für JSON
-Skalarstringdaten, die aus einer indexierten JSON
-Spalte extrahiert wurden, können Sie die Funktion STRING
oder ihre sichere Version SAFE.STRING
anwenden.
Wenn der extrahierte Wert JSON
kein String ist, erzeugt die Funktion STRING
einen Fehler und die Funktion SAFE.STRING
gibt NULL
zurück.
Bei indexierten STRING
-Daten im JSON-Format (nicht JSON
-Daten) können Sie die folgenden Funktionen anwenden:
Angenommen, Sie haben die folgende indexierte Tabelle namens dataset.person_data
mit den Spalten JSON
und STRING
:
+----------------------------------------------------------------+-----------------------------------------+ | json_column | string_column | +----------------------------------------------------------------+-----------------------------------------+ | { "name" : "Ariel", "email" : "cloudysanfrancisco@gmail.com" } | { "name" : "Ariel", "job" : "doctor" } | +----------------------------------------------------------------+-----------------------------------------+
Für die folgenden Abfragen kann die Optimierung in Anspruch genommen werden:
SELECT * FROM dataset.person_data WHERE SAFE.STRING(json_column.email) = 'cloudysanfrancisco@gmail.com';
SELECT * FROM dataset.person_data WHERE JSON_VALUE(string_column, '$.job') IN ('doctor', 'lawyer', 'teacher');
Kombinationen dieser Funktionen werden ebenfalls optimiert, z. B. UPPER(JSON_VALUE(json_string_expression)) = 'FOO'
.
Nutzung des Suchindex
Um festzustellen, ob ein Suchindex für eine Abfrage verwendet wurde, sehen Sie sich die Jobinformationen der Abfrage in Abfrageergebnissen an. Die Nutzungsmodus für Indexe undNicht verwendete Gründe für Indexe Felder enthalten detaillierte Informationen zur Verwendung des Suchindex.
Informationen zur Verwendung des Suchindex finden Sie auch im Feld searchStatistics
in der API-Methode Jobs.Get. Das Feld indexUsageMode
in searchStatistics
gibt an, ob ein Suchindex mit den folgenden Werten verwendet wurde:
UNUSED
: Es wurde kein Suchindex verwendet.PARTIALLY_USED
: Ein Teil der Abfrage wurde Suchindexe verwendet und ein Teil nicht.FULLY_USED
: JedeSEARCH
-Funktion in der Abfrage hat einen Suchindex verwendet.
Wenn indexUsageMode
UNUSED
oder PARTIALLY_USED
ist, enthält das Feld indexUnusuedReasons
Informationen dazu, warum Suchindexe nicht in der Abfrage verwendet wurden.
Führen Sie den Befehl bq show
aus, um searchStatistics
für eine Abfrage aufzurufen.
bq show --format=prettyjson -j JOB_ID
Beispiel
Angenommen, Sie führen eine Abfrage aus, die die Funktion SEARCH
für Daten in einer Tabelle aufruft. In den Jobdetails der Abfrage finden Sie die Job-ID. Führen Sie dann den Befehl bq show
aus, um weitere Informationen zu erhalten:
bq show --format=prettyjson --j my_project:US.bquijob_123x456_789y123z456c
Die Ausgabe enthält zahlreiche Felder, einschließlich searchStatistics
, was in etwa so aussieht. In diesem Beispiel gibt indexUsageMode
an, dass der Index nicht verwendet wurde. Der Grund dafür ist, dass die Tabelle keinen Suchindex hat. Erstellen Sie einen Suchindex für die Tabelle, um dieses Problem zu lösen. Eine Liste aller Gründe, aus denen ein Suchindex möglicherweise nicht in einer Abfrage verwendet wird, finden Sie im indexUnusedReason
-Feld code
.
"searchStatistics": {
"indexUnusedReasons": [
{
"baseTable": {
"datasetId": "my_dataset",
"projectId": "my_project",
"tableId": "my_table"
},
"code": "INDEX_CONFIG_NOT_AVAILABLE",
"message": "There is no search index configuration for the base table `my_project:my_dataset.my_table`."
}
],
"indexUsageMode": "UNUSED"
},
Best Practices
In den folgenden Abschnitten werden Best Practices für die Suche beschrieben.
Selektiv suchen
Die Suche funktioniert am besten, wenn sie nur wenige Ergebnisse liefert. Machen Sie Suchanfragen daher so spezifisch wie möglich.
ORDER BY LIMIT-Optimierung
Abfragen, die SEARCH
, =
, IN
, LIKE
oder STARTS_WITH
für eine sehr große partitionierte Tabelle verwenden, können optimiert werden, wenn Sie eine ORDER BY
-Klausel für das partitionierte Feld und eine LIMIT
-Klausel verwenden.
Für Abfragen, die die Funktion SEARCH
nicht enthalten, können Sie die anderen Operatoren und Funktionen verwenden, um die Optimierung zu nutzen. Die Optimierung wird unabhängig davon angewendet, ob die Basistabelle indexiert ist oder nicht. Dies funktioniert gut, wenn Sie nach einem häufig verwendeten Begriff suchen.
Angenommen, die zuvor erstellte Tabelle Logs
ist nach einer zusätzlichen Spalte vom Typ DATE
mit dem Namen day
partitioniert. Die folgende Abfrage ist optimiert:
SELECT Level, Source, Message FROM my_dataset.Logs WHERE SEARCH(Message, "foo") ORDER BY day LIMIT 10;
Suche eingrenzen
Wenn Sie die Funktion SEARCH
verwenden, suchen Sie nur in den Spalten der Tabelle, bei denen Sie davon ausgehen, dass Ihre Suchbegriffe enthalten sein werden. Dies verbessert die Leistung und die Anzahl der Byte, die gescannt werden müssen, wird verringert.
Backticks verwenden
Wenn Sie die SEARCH
-Funktion mit dem LOG_ANALYZER
-Textanalysator verwenden, erzwingt das Einbinden Ihrer Suchanfrage in Graviszeichen eine genaue Übereinstimmung. Dies ist hilfreich, wenn bei Ihrer Suche zwischen Groß- und Kleinschreibung unterschieden wird oder Zeichen enthalten sind, die nicht als Begrenzungszeichen interpretiert werden sollten. Für die Suche nach der IP-Adresse 192.0.2.1
verwenden Sie beispielsweise `192.0.2.1`
. Ohne Graviszeichen gibt die Suche jede Zeile zurück, die die einzelnen Tokens 192
, 0
, 2
und 1
in beliebiger Reihenfolge enthält.