Diese Seite wurde von der Cloud Translation API übersetzt.
Switch to English

Sekundäre Indexe

In einer Cloud Spanner-Datenbank erstellt Cloud Spanner automatisch einen Index für den Primärschlüssel jeder Tabelle. Sie müssen beispielsweise nichts tun, um den Primärschlüssel von Singers zu indexieren, da er bereits automatisch indexiert wird.

Sie können auch sekundäre Indexe für andere Spalten erstellen. Wenn Sie einen sekundären Index für eine Spalte hinzufügen, können Sie Daten in dieser Spalte effizienter nachschlagen. Wenn Sie z. B. schnell eine Reihe von SingerId-Werten für einen bestimmten Bereich von LastName-Werten heraussuchen müssen, sollten Sie einen sekundären Index für LastName erstellen. Cloud Spanner braucht dann nicht die gesamte Tabelle zu scannen.

Wenn die Suche im obigen Beispiel innerhalb einer Lese-Schreib-Transaktion erfolgt, speichert die effizientere Suche auch keine Sperren für die gesamte Tabelle, wodurch gleichzeitige Einfügungen und Aktualisierungen für Zeilen außerhalb des LastName-Suchbereichs der Tabelle durchgeführt werden.

In jedem sekundären Index speichert Cloud Spanner die folgenden Daten:

  • Alle Schlüsselspalten aus der Basistabelle
  • Alle Spalten, die im Index enthalten sind
  • Alle in der optionalen STORING-Klausel der Indexdefinition angegebenen Spalten

Im Laufe der Zeit analysiert Cloud Spanner Ihre Tabellen, damit Ihre sekundären Indexe für die geeigneten Abfragen verwendet werden.

Sekundären Index erstellen

Am effizientesten ist die Erweiterung einer Tabelle um einen sekundären Index gleich bei deren Erstellung. Wenn Sie eine Tabelle und ihre Indexe gleichzeitig erstellen möchten, senden Sie die DDL-Anweisungen für die neue Tabelle und die neuen Indexe in einer einzigen Anfrage an Cloud Spanner.

Sie können in Cloud Spanner auch einen neuen sekundären Index zu einer vorhandenen Tabelle hinzufügen, während die Datenbank weiterhin Traffic bereitstellt. Wie bei allen anderen Schemaänderungen in Cloud Spanner erfordert das Erstellen eines Indexes in einer vorhandenen Datenbank weder, dass die Datenbank offline geschaltet wird, noch, dass ganze Spalten oder Tabellen gesperrt werden.

Immer wenn ein neuer Index zu einer vorhandenen Tabelle hinzugefügt wird, führt Cloud Spanner automatisch einen Backfill durch, d. h., der Index wird mit einer aktuellen Ansicht der zu indexierenden Daten gefüllt. Cloud Spanner verwaltet diesen Backfill-Prozess für Sie und verwendet während des Index-Backfills zusätzliche Ressourcen.

Die Indexerstellung kann mehrere Minuten bis hin zu vielen Stunden in Anspruch nehmen. Da die Indexerstellung eine Schemaaktualisierung ist, unterliegt sie denselben Leistungsbeschränkungen wie jede andere Schemaaktualisierung. Wie viel Zeit zum Erstellen eines sekundären Index benötigt wird, hängt von mehreren Faktoren ab:

  • Größe des Datasets
  • Anzahl der Knoten in der Instanz
  • Auslastung der Instanz

Im Fortschrittsabschnitt sehen Sie den Fortschritt für einen Index-Backfill-Prozess.

Die Verwendung der Spalte Commit-Zeitstempel als erster Teil des sekundären Index kann Hotspots erstellen und die Schreibleistung verringern.

Mit der Anweisung CREATE INDEX definieren Sie einen sekundären Index im Schema. Hier einige Beispiele:

So indexieren Sie alle Singers in der Datenbank nach ihrem Vor- und Nachnamen:

CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName)

So erstellen Sie einen Index aller Songs in der Datenbank nach dem Wert von SongName:

CREATE INDEX SongsBySongName ON Songs(SongName)

Wenn Sie nur die Titel eines bestimmten Interpreten indexieren möchten, verwenden Sie die Klausel INTERLEAVE IN, um den Index in der Tabelle Singers zu verschachteln:

CREATE INDEX SongsBySingerSongName ON Songs(SingerId, SongName),
    INTERLEAVE IN Singers

So indexieren Sie nur die Titel eines bestimmten Albums:

CREATE INDEX SongsBySingerAlbumSongName ON Songs(SingerId, AlbumId, SongName),
    INTERLEAVE IN Albums

So indexieren Sie nach SongName in absteigender Reihenfolge:

CREATE INDEX SongsBySingerAlbumSongNameDesc ON Songs(SingerId, AlbumId, SongName DESC),
    INTERLEAVE IN Albums

Die DESC-Anmerkung oben gilt nur für SongName. Wenn Sie nach anderen Indexschlüsseln in absteigender Reihenfolge indexieren möchten, versehen Sie sie ebenfalls mit DESC: SingerId DESC, AlbumId DESC.

Beachten Sie außerdem, dass PRIMARY_KEY ein reserviertes Wort ist und nicht als Name eines Index verwendet werden kann. Dies ist der Name, der dem Pseudo-Index zugewiesen wird, der beim Erstellen einer Tabelle mit PRIMARY KEY-Spezifikation erstellt wird.

Weitere Informationen und Best Practices für die Auswahl nicht verschränkter Indexe und verschränkter Indexe finden Sie unter Indexoptionen und Verschränkte Indexe für eine Spalte verwenden, deren Wert monoton erhöht oder reduziert wird..

Fortschritt des Index-Backfills anzeigen

Schritte zum Aufrufen des Index-Backfill-Fortschritts

  1. Ein Index-Backfill-Prozess ist Teil eines lang andauernden schema-update-Vorgangs, da das Hinzufügen eines sekundären Index eine Schemaaktualisierung erfordert. Sie können den Fortschritt des Index-Backfills mithilfe der Vorgangs-ID anzeigen. Wenn Sie die Vorgangs-ID nicht haben, suchen Sie diese mit gcloud spanner operations list:

    gcloud spanner operations list --instance=INSTANCE --database=DATABASE
    

    Verwendungshinweise:

    • Wenn Sie die Liste der von diesem Befehl zurückgegebenen Vorgänge beschränken möchten, geben Sie das Flag --filter an. Verwenden Sie beispielsweise den folgenden Filter, um Schemaaktualisierungsvorgänge auszuführen.

      --filter="@TYPE:UpdateDatabaseDdlMetadata"
      

      Informationen zur Filtersyntax finden Sie unter gcloud topic filters. Informationen zum Filtern von Datenbankvorgängen finden Sie im Feld filter in ListDatabaseOperationsRequest.

    Hier ein Beispiel der Ausgabe:

    OPERATION_ID     STATEMENTS                                                                                          DONE   @TYPE
    _auto_op_123456  CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName)                                 False  UpdateDatabaseDdlMetadata
                     CREATE INDEX SongsBySingerAlbumSongName ON Songs(SingerId, AlbumId, SongName), INTERLEAVE IN Albums
    _auto_op_234567                                                                                                      True   CreateDatabaseMetadata
    
  2. Verwenden Sie gcloud spanner operations describe, um den Fortschritt eines oder mehrerer sekundärer Index-Backfill-Prozesse zu verfolgen:

    gcloud spanner operations describe _auto_op_123456 \
        --instance=INSTANCE \
        --database=DATABASE
    

    Hier ist ein Beispiel für die Ausgabe eines lange laufenden Vorgangs mit Schemaaktualisierung, der zwei Index-Backfill-Prozesse enthält:

    done: true
    metadata:
      '@type': type.googleapis.com/google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata
      commitTimestamps:
      - '2021-01-22T21:58:42.912540Z'
      database: projects/my-project/instances/test-instance/databases/example-db
      progress:
      - endTime: '2021-01-22T21:58:42.912540Z'
        progressPercent: 100
        startTime: '2021-01-22T21:58:11.053996Z'
      - progressPercent: 67
        startTime: '2021-01-22T21:58:11.053996Z'
      statements:
      - CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName)
      - CREATE INDEX SongsBySingerAlbumSongName ON Songs(SingerId, AlbumId, SongName), INTERLEAVE IN Albums
    name: projects/my-project/instances/test-instance/databases/example-db/operations/_auto_op_123456
    response:
      '@type': type.googleapis.com/google.protobuf.Empty
    

    Der Fortschritt jeder Index-Backfill-Anweisung befindet sich im Feld progress. Für jede Anweisung im Anweisungsarray gibt es ein entsprechendes Feld im Fortschrittsarray. Wenn die Felder startTime und endTime verfügbar sind, werden sie automatisch ausgefüllt. Die Fortschrittsarrayreihenfolge entspricht der Reihenfolge des Anweisungsarrays.

Szenarien beim Aufrufen des Index-Backfill-Fortschritts

Der Fortschritt eines Index-Backfills kann sich auf verschiedene Szenarien beziehen. Anweisungen zur Indexerstellung, die einen Index-Backfill erfordern, sind Teil der Schemaaktualisierung. Außerdem kann es mehrere Anweisungen geben, die Teil eines Schemaaktualisierungsvorgangs sind.

Das erste Szenario ist die einfachste Methode. Diese ist die erste Anweisung beim Erstellen des Index in der Schemaaktualisierung. Das Feld startTime für die Indexerstellungsanweisung wird mit der Startzeit des Schemas für die Schemaaktualisierung gefüllt, da es sich um die erste Anweisung handelt. Sie wird als erstes verarbeitet und ausgeführt. in der Reihenfolge der Ausführung. Das Feld progressPercent der Indexerstellungsanweisung wird ausgefüllt, wenn der Fortschritt des Index-Backfills über 0 % liegt. Das Feld endTime wird ausgefüllt, sobald die Anweisung übergeben wurde.

Das zweite Szenario ist, wenn die Indexerstellungsanweisung nicht die erste Anweisung im Schemaaktualisierungsvorgang ist. Bis auf die Reihenfolge der Ausführung werden keine Felder in Bezug auf die Indexerstellungsanweisung ausgefüllt. Sobald die vorherigen Anweisungen in Kraft getreten sind, wird für startTime das Feld "Fortschritt der Indexerstellungserstellung" das erste Attribut ausgefüllt. Anschließend wird das Feld progressPercent der Indexerstellungsanweisung ausgefüllt, wenn der Fortschritt des Index-Backfills über 0 % liegt. Das Feld endTime wird ausgefüllt, sobald die Anweisung übergeben wurde.

Indexerstellung abbrechen

Sie können über das Cloud SDK die Indexerstellung abbrechen. Zum Abrufen einer Liste von Schemaaktualisierungsvorgängen für eine Cloud Spanner-Datenbank verwenden Sie den Befehl gcloud spanner operations list und fügen die Option --filter ein:

gcloud spanner operations list \
    --instance=INSTANCE \
    --database=DATABASE \
    --filter="@TYPE:UpdateDatabaseDdlMetadata"

Suchen Sie nach der OPERATION_ID des Vorgangs, den Sie abbrechen möchten, und brechen Sie ihn mit dem Befehl gcloud spanner operations cancel ab:

gcloud spanner operations cancel OPERATION_ID \
    --instance=INSTANCE \
    --database=DATABASE

Vorhandene Indexe ansehen

Um Informationen zu vorhandenen Indexen in einer Datenbank aufzurufen, können Sie die Google Cloud Console oder das gcloud-Befehlszeilentool verwenden:

Console

  1. Rufen Sie in der Cloud Console die Seite Cloud Spanner-Instanzen auf.

    Zur Seite "VM-Instanzen"

  2. Klicken Sie auf den Namen der Instanz, die Sie aufrufen möchten.

  3. Klicken Sie im linken Bereich auf die Datenbank, die Sie sich ansehen möchten, und klicken Sie dann auf die Tabelle, die Sie aufrufen möchten.

  4. Klicken Sie auf den Tab Indexe. Die Cloud Console zeigt eine Liste von Indexen an.

  5. Optional: Klicken Sie auf den Namen eines Index, um Details zu diesem zu erhalten, z. B. die Spalten, die er enthält.

gcloud

Führen Sie den Befehl gcloud spanner databases ddl describe aus:

    gcloud spanner databases ddl describe DATABASE \
        --instance=INSTANCE

Das gcloud-Tool gibt die DDL-Anweisungen (Data Definition Language – Datendefinitionssprache) zum Erstellen der Tabellen und Indexe der Datenbank aus. Die CREATE INDEX-Anweisungen beschreiben die vorhandenen Indexe. Beispiel:

    --- |-
  CREATE TABLE Singers (
    SingerId INT64 NOT NULL,
    FirstName STRING(1024),
    LastName STRING(1024),
    SingerInfo BYTES(MAX),
  ) PRIMARY KEY(SingerId)
---
  CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName)

Abfrage mit einem bestimmten Index

In den folgenden Abschnitten wird erläutert, wie Sie einen Index in einer SQL-Anweisung und mit der Leseschnittstelle für Cloud Spanner angeben. In den Beispielen in diesen Abschnitten wird davon ausgegangen, dass Sie der Tabelle Albums eine Spalte MarketingBudget hinzugefügt und einen Index mit dem Namen AlbumsByAlbumTitle erstellt haben:

CREATE TABLE Albums (
  SingerId         INT64 NOT NULL,
  AlbumId          INT64 NOT NULL,
  AlbumTitle       STRING(MAX),
  MarketingBudget  INT64,
) PRIMARY KEY (SingerId, AlbumId),
  INTERLEAVE IN PARENT Singers ON DELETE CASCADE;

CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle);

Index in einer SQL-Anweisung angeben

Wenn Sie eine Cloud Spanner-Tabelle mit SQL abfragen, verwendet Cloud Spanner automatisch alle Indexe, die die Effizienz der Abfrage wahrscheinlich erhöhen. Daher müssen Sie für SQL-Abfragen in der Regel keinen Index angeben.

In einigen Fällen wählt Cloud Spanner jedoch unter Umständen einen Index aus, mit dem die Latenz der Abfrage zunimmt. Wenn Sie die Schritte zur Fehlerbehebung bei Leistungsabfällen ausgeführt haben und bestätigt haben, dass es sinnvoll ist, die Abfrage mit einem anderen Index zu versuchen, können Sie den Index als Teil Ihrer Abfrage angeben:

Um einen Index in einer SQL-Anweisung anzugeben, stellen Sie mit FORCE_INDEX eine Indexanweisung bereit. Indexanweisungen verwenden die folgende Syntax:

FROM MyTable@{FORCE_INDEX=MyTableIndex}

Mit einer Indexanweisung können Sie Cloud Spanner auch dazu veranlassen, in der Basistabelle zu suchen, statt einen Index zu verwenden:

FROM MyTable@{FORCE_INDEX=_BASE_TABLE}

Das folgende Beispiel zeigt eine SQL-Abfrage, die einen Index angibt:

SELECT AlbumId, AlbumTitle, MarketingBudget
    FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle}
    WHERE AlbumTitle >= "Aardvark" AND AlbumTitle < "Goo";

Eine Indexanweisung kann den Abfrageprozessor von Cloud Spanner zwingen, zusätzliche Spalten zu lesen, die für die Abfrage erforderlich, aber nicht im Index gespeichert sind. Der Abfrageprozessor ruft diese Spalten ab, indem er den Index und die Basistabelle zusammenfügt. Um diesen zusätzlichen Join zu vermeiden, speichern Sie die zusätzlichen Spalten mit einer STORING-Klausel im Index.

Im oben gezeigten Beispiel etwa wird die Spalte MarketingBudget nicht im Index gespeichert, aber die SQL-Abfrage wählt diese Spalte aus. Deshalb muss Cloud Spanner die Spalte MarketingBudget aus der Basistabelle abrufen und mit Daten aus dem Index verknüpfen, ehe er die Abfrageergebnisse zurückgeben kann.

Cloud Spanner gibt einen Fehler aus, wenn die Indexanweisung eines der folgenden Probleme aufweist:

Die folgenden Beispiele zeigen, wie Abfragen geschrieben und ausgeführt werden, die die Werte von AlbumId, AlbumTitle und MarketingBudget mithilfe des Index AlbumsByAlbumTitle abrufen:

C++

void QueryUsingIndex(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;

  spanner::SqlStatement select(
      "SELECT AlbumId, AlbumTitle, MarketingBudget"
      " FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle}"
      " WHERE AlbumTitle >= @start_title AND AlbumTitle < @end_title",
      {{"start_title", spanner::Value("Aardvark")},
       {"end_title", spanner::Value("Goo")}});
  using RowType =
      std::tuple<std::int64_t, std::string, absl::optional<std::int64_t>>;
  auto rows = client.ExecuteQuery(std::move(select));
  for (auto const& row : spanner::StreamOf<RowType>(rows)) {
    if (!row) throw std::runtime_error(row.status().message());
    std::cout << "AlbumId: " << std::get<0>(*row) << "\t";
    std::cout << "AlbumTitle: " << std::get<1>(*row) << "\t";
    auto marketing_budget = std::get<2>(*row);
    if (marketing_budget) {
      std::cout << "MarketingBudget: " << *marketing_budget << "\n";
    } else {
      std::cout << "MarketingBudget: NULL\n";
    }
  }
  std::cout << "Read completed for [spanner_query_data_with_index]\n";
}

C#

string connectionString =
$"Data Source=projects/{projectId}/instances/{instanceId}"
+ $"/databases/{databaseId}";
// Create connection to Cloud Spanner.
using (var connection = new SpannerConnection(connectionString))
{
    var cmd = connection.CreateSelectCommand(
        "SELECT AlbumId, AlbumTitle, MarketingBudget FROM Albums@ "
        + "{FORCE_INDEX=AlbumsByAlbumTitle} "
        + $"WHERE AlbumTitle >= @startTitle "
        + $"AND AlbumTitle < @endTitle",
        new SpannerParameterCollection {
            {"startTitle", SpannerDbType.String},
            {"endTitle", SpannerDbType.String} });
    cmd.Parameters["startTitle"].Value = startTitle;
    cmd.Parameters["endTitle"].Value = endTitle;
    using (var reader = await cmd.ExecuteReaderAsync())
    {
        while (await reader.ReadAsync())
        {
            var marketingBudget = reader.IsDBNull(
                reader.GetOrdinal("MarketingBudget")) ?
                "" :
                reader.GetFieldValue<string>("MarketingBudget");
            Console.WriteLine("AlbumId : "
            + reader.GetFieldValue<string>("AlbumId")
            + " AlbumTitle : "
            + reader.GetFieldValue<string>("AlbumTitle")
            + " MarketingBudget : "
            + marketingBudget);
        }
    }
}

Go


import (
	"context"
	"fmt"
	"io"
	"strconv"

	"cloud.google.com/go/spanner"
	"google.golang.org/api/iterator"
)

func queryUsingIndex(w io.Writer, db string) error {
	ctx := context.Background()
	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return err
	}
	defer client.Close()

	stmt := spanner.Statement{
		SQL: `SELECT AlbumId, AlbumTitle, MarketingBudget
			FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle}
			WHERE AlbumTitle >= @start_title AND AlbumTitle < @end_title`,
		Params: map[string]interface{}{
			"start_title": "Aardvark",
			"end_title":   "Goo",
		},
	}
	iter := client.Single().Query(ctx, stmt)
	defer iter.Stop()
	for {
		row, err := iter.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return err
		}
		var albumID int64
		var marketingBudget spanner.NullInt64
		var albumTitle string
		if err := row.ColumnByName("AlbumId", &albumID); err != nil {
			return err
		}
		if err := row.ColumnByName("AlbumTitle", &albumTitle); err != nil {
			return err
		}
		if err := row.ColumnByName("MarketingBudget", &marketingBudget); err != nil {
			return err
		}
		budget := "NULL"
		if marketingBudget.Valid {
			budget = strconv.FormatInt(marketingBudget.Int64, 10)
		}
		fmt.Fprintf(w, "%d %s %s\n", albumID, albumTitle, budget)
	}
	return nil
}

Java

static void queryUsingIndex(DatabaseClient dbClient) {
  Statement statement =
      Statement
          // We use FORCE_INDEX hint to specify which index to use. For more details see
          // https://cloud.google.com/spanner/docs/query-syntax#from-clause
          .newBuilder(
              "SELECT AlbumId, AlbumTitle, MarketingBudget "
                  + "FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle} "
                  + "WHERE AlbumTitle >= @StartTitle AND AlbumTitle < @EndTitle")
          // We use @BoundParameters to help speed up frequently executed queries.
          //  For more details see https://cloud.google.com/spanner/docs/sql-best-practices
          .bind("StartTitle")
          .to("Aardvark")
          .bind("EndTitle")
          .to("Goo")
          .build();
  try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) {
    while (resultSet.next()) {
      System.out.printf(
          "%d %s %s\n",
          resultSet.getLong("AlbumId"),
          resultSet.getString("AlbumTitle"),
          resultSet.isNull("MarketingBudget") ? "NULL" : resultSet.getLong("MarketingBudget"));
    }
  }
}

Node.js

// Imports the Google Cloud client library
const {Spanner} = require('@google-cloud/spanner');

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';
// const startTitle = 'Ardvark';
// const endTitle = 'Goo';

// Creates a client
const spanner = new Spanner({
  projectId: projectId,
});

// Gets a reference to a Cloud Spanner instance and database
const instance = spanner.instance(instanceId);
const database = instance.database(databaseId);

const query = {
  sql: `SELECT AlbumId, AlbumTitle, MarketingBudget
        FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle}
        WHERE AlbumTitle >= @startTitle AND AlbumTitle <= @endTitle`,
  params: {
    startTitle: startTitle,
    endTitle: endTitle,
  },
};

// Queries rows from the Albums table
try {
  const [rows] = await database.run(query);

  rows.forEach(row => {
    const json = row.toJSON();
    const marketingBudget = json.MarketingBudget
      ? json.MarketingBudget
      : null; // This value is nullable
    console.log(
      `AlbumId: ${json.AlbumId}, AlbumTitle: ${json.AlbumTitle}, MarketingBudget: ${marketingBudget}`
    );
  });
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the database when finished.
  database.close();
}

PHP

use Google\Cloud\Spanner\SpannerClient;

/**
 * Queries sample data from the database using SQL and an index.
 *
 * The index must exist before running this sample. You can add the index
 * by running the `add_index` sample or by running this DDL statement against
 * your database:
 *
 *     CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)
 *
 * Example:
 * ```
 * query_data_with_index($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 * @param string $startTitle The start of the title index.
 * @param string $endTitle   The end of the title index.
 */
function query_data_with_index(
    $instanceId,
    $databaseId,
    $startTitle = 'Aardvark',
    $endTitle = 'Goo'
) {
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $parameters = [
        'startTitle' => $startTitle,
        'endTitle' => $endTitle
    ];

    $results = $database->execute(
        'SELECT AlbumId, AlbumTitle, MarketingBudget ' .
        'FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle} ' .
        'WHERE AlbumTitle >= @startTitle AND AlbumTitle < @endTitle',
        ['parameters' => $parameters]
    );

    foreach ($results as $row) {
        printf('AlbumId: %s, AlbumTitle: %s, MarketingBudget: %d' . PHP_EOL,
            $row['AlbumId'], $row['AlbumTitle'], $row['MarketingBudget']);
    }
}

Python

def query_data_with_index(
    instance_id, database_id, start_title="Aardvark", end_title="Goo"
):
    """Queries sample data from the database using SQL and an index.

    The index must exist before running this sample. You can add the index
    by running the `add_index` sample or by running this DDL statement against
    your database:

        CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)

    This sample also uses the `MarketingBudget` column. You can add the column
    by running the `add_column` sample or by running this DDL statement against
    your database:

        ALTER TABLE Albums ADD COLUMN MarketingBudget INT64

    """
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    params = {"start_title": start_title, "end_title": end_title}
    param_types = {
        "start_title": spanner.param_types.STRING,
        "end_title": spanner.param_types.STRING,
    }

    with database.snapshot() as snapshot:
        results = snapshot.execute_sql(
            "SELECT AlbumId, AlbumTitle, MarketingBudget "
            "FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle} "
            "WHERE AlbumTitle >= @start_title AND AlbumTitle < @end_title",
            params=params,
            param_types=param_types,
        )

        for row in results:
            print(u"AlbumId: {}, AlbumTitle: {}, " "MarketingBudget: {}".format(*row))

Ruby

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"
# start_title = "An album title to start with such as 'Ardvark'"
# end_title   = "An album title to end with such as 'Goo'"

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id

sql_query = "SELECT AlbumId, AlbumTitle, MarketingBudget
             FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle}
             WHERE AlbumTitle >= @start_title AND AlbumTitle < @end_title"

params      = { start_title: start_title, end_title: end_title }
param_types = { start_title: :STRING,     end_title: :STRING }

client.execute(sql_query, params: params, types: param_types).rows.each do |row|
  puts "#{row[:AlbumId]} #{row[:AlbumTitle]} #{row[:MarketingBudget]}"
end

Index in der Leseschnittstelle angeben

Wenn Sie die Leseschnittstelle zu Cloud Spanner verwenden und Cloud Spanner einen Index verwenden soll, müssen Sie den Index angeben. Die Leseschnittstelle wählt den Index nicht automatisch aus.

Darüber hinaus muss Ihr Index alle Daten enthalten, die in den Abfrageergebnissen angezeigt werden, mit Ausnahme der Spalten, die Teil des Primärschlüssels sind. Diese Einschränkung besteht, weil die Leseschnittstelle keine Joins zwischen dem Index und der Basistabelle unterstützt. Wenn Sie weitere Spalten mit in die Abfrageergebnisse einbeziehen möchten, haben Sie mehrere Möglichkeiten:

  • Speichern Sie die zusätzlichen Spalten mit einer STORING-Klausel im Index.
  • Machen Sie Ihre Abfrage, ohne die zusätzlichen Spalten einzubeziehen, und senden Sie dann mithilfe der Primärschlüssel eine weitere Abfrage, die die zusätzlichen Spalten liest.

Cloud Spanner gibt Werte aus dem Index in aufsteigender Reihenfolge nach Indexschlüssel zurück. Führen Sie die folgenden Schritte aus, um Werte in absteigender Reihenfolge abzurufen:

  • Kommentieren Sie den Indexschlüssel mit DESC. Beispiel:

    CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle DESC);
    

    Die Anmerkung DESC gilt für einen einzelnen Indexschlüssel. Wenn der Index mehr als einen Schlüssel enthält und die Abfrageergebnisse auf Basis aller Schlüssel in absteigender Reihenfolge angezeigt werden sollen, fügen Sie für jeden Schlüssel eine DESC-Anmerkung ein.

  • Wenn der Lesevorgang einen Schlüsselbereich angibt, muss der Schlüsselbereich ebenfalls in absteigender Reihenfolge angegeben werden. Mit anderen Worten: Der Wert des Startschlüssels muss größer sein als der Wert des Endschlüssels.

Das folgende Beispiel veranschaulicht, wie die Werte von AlbumId und AlbumTitle mit dem Index AlbumsByAlbumTitle abgerufen werden:

C++

void ReadDataWithIndex(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;

  spanner::ReadOptions read_options;
  read_options.index_name = "AlbumsByAlbumTitle";
  auto rows = client.Read("Albums", google::cloud::spanner::KeySet::All(),
                          {"AlbumId", "AlbumTitle"}, read_options);
  using RowType = std::tuple<std::int64_t, std::string>;
  for (auto const& row : spanner::StreamOf<RowType>(rows)) {
    if (!row) throw std::runtime_error(row.status().message());
    std::cout << "AlbumId: " << std::get<0>(*row) << "\t";
    std::cout << "AlbumTitle: " << std::get<1>(*row) << "\n";
  }
  std::cout << "Read completed for [spanner_read_data_with_index]\n";
}

C#


using Google.Cloud.Spanner.Data;
using System.Collections.Generic;
using System.Threading.Tasks;

public class QueryDataWithIndexAsyncSample
{
    public class Album
    {
        public int AlbumId { get; set; }
        public string AlbumTitle { get; set; }
        public long MarketingBudget { get; set; }
    }

    public async Task<List<Album>> QueryDataWithIndexAsync(string projectId, string instanceId, string databaseId,
        string startTitle, string endTitle)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";
        using var connection = new SpannerConnection(connectionString);
        using var cmd = connection.CreateSelectCommand(
            "SELECT AlbumId, AlbumTitle, MarketingBudget FROM Albums@ "
            + "{FORCE_INDEX=AlbumsByAlbumTitle} "
            + $"WHERE AlbumTitle >= @startTitle "
            + $"AND AlbumTitle < @endTitle",
            new SpannerParameterCollection
            {
                { "startTitle", SpannerDbType.String, startTitle },
                { "endTitle", SpannerDbType.String, endTitle }
            });

        var albums = new List<Album>();
        using var reader = await cmd.ExecuteReaderAsync();
        while (await reader.ReadAsync())
        {
            albums.Add(new Album
            {
                AlbumId = reader.GetFieldValue<int>("AlbumId"),
                AlbumTitle = reader.GetFieldValue<string>("AlbumTitle"),
                MarketingBudget = reader.IsDBNull(reader.GetOrdinal("MarketingBudget")) ? 0 : reader.GetFieldValue<long>("MarketingBudget")
            });
        }
        return albums;
    }
}

Go


import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/spanner"
	"google.golang.org/api/iterator"
)

func readUsingIndex(w io.Writer, db string) error {
	ctx := context.Background()
	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return err
	}
	defer client.Close()

	iter := client.Single().ReadUsingIndex(ctx, "Albums", "AlbumsByAlbumTitle", spanner.AllKeys(),
		[]string{"AlbumId", "AlbumTitle"})
	defer iter.Stop()
	for {
		row, err := iter.Next()
		if err == iterator.Done {
			return nil
		}
		if err != nil {
			return err
		}
		var albumID int64
		var albumTitle string
		if err := row.Columns(&albumID, &albumTitle); err != nil {
			return err
		}
		fmt.Fprintf(w, "%d %s\n", albumID, albumTitle)
	}
}

Java

static void readUsingIndex(DatabaseClient dbClient) {
  try (ResultSet resultSet =
      dbClient
          .singleUse()
          .readUsingIndex(
              "Albums",
              "AlbumsByAlbumTitle",
              KeySet.all(),
              Arrays.asList("AlbumId", "AlbumTitle"))) {
    while (resultSet.next()) {
      System.out.printf("%d %s\n", resultSet.getLong(0), resultSet.getString(1));
    }
  }
}

Node.js

// Imports the Google Cloud client library
const {Spanner} = require('@google-cloud/spanner');

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';

// Creates a client
const spanner = new Spanner({
  projectId: projectId,
});

// Gets a reference to a Cloud Spanner instance and database
const instance = spanner.instance(instanceId);
const database = instance.database(databaseId);

const albumsTable = database.table('Albums');

const query = {
  columns: ['AlbumId', 'AlbumTitle'],
  keySet: {
    all: true,
  },
  index: 'AlbumsByAlbumTitle',
};

// Reads the Albums table using an index
try {
  const [rows] = await albumsTable.read(query);

  rows.forEach(row => {
    const json = row.toJSON();
    console.log(`AlbumId: ${json.AlbumId}, AlbumTitle: ${json.AlbumTitle}`);
  });
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the database when finished.
  database.close();
}

PHP

use Google\Cloud\Spanner\SpannerClient;

/**
 * Reads sample data from the database using an index.
 *
 * The index must exist before running this sample. You can add the index
 * by running the `add_index` sample or by running this DDL statement against
 * your database:
 *
 *     CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)
 *
 * Example:
 * ```
 * read_data_with_index($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function read_data_with_index($instanceId, $databaseId)
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $keySet = $spanner->keySet(['all' => true]);
    $results = $database->read(
        'Albums',
        $keySet,
        ['AlbumId', 'AlbumTitle'],
        ['index' => 'AlbumsByAlbumTitle']
    );

    foreach ($results->rows() as $row) {
        printf('AlbumId: %s, AlbumTitle: %s' . PHP_EOL,
            $row['AlbumId'], $row['AlbumTitle']);
    }
}

Python

def read_data_with_index(instance_id, database_id):
    """Reads sample data from the database using an index.

    The index must exist before running this sample. You can add the index
    by running the `add_index` sample or by running this DDL statement against
    your database:

        CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)

    """
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    with database.snapshot() as snapshot:
        keyset = spanner.KeySet(all_=True)
        results = snapshot.read(
            table="Albums",
            columns=("AlbumId", "AlbumTitle"),
            keyset=keyset,
            index="AlbumsByAlbumTitle",
        )

        for row in results:
            print("AlbumId: {}, AlbumTitle: {}".format(*row))

Ruby

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id

result = client.read "Albums", [:AlbumId, :AlbumTitle],
                     index: "AlbumsByAlbumTitle"

result.rows.each do |row|
  puts "#{row[:AlbumId]} #{row[:AlbumTitle]}"
end

STORING-Klausel

Optional können Sie mit der Klausel STORING eine Kopie einer Spalte im Index speichern. Dieser Indextyp bietet Vorteile für Abfragen und Leseaufrufe unter Verwendung des Index, allerdings beansprucht das zusätzliche Speicherkapazität:

  • SQL-Abfragen, die den Index verwenden und in der Klausel STORING gespeicherte Spalten auswählen, benötigen keine zusätzliche Verknüpfung mit der Basistabelle.
  • Leseaufrufe, die den Index verwenden, können in der Klausel STORING gespeicherte Spalten lesen.

Beispiel: Sie haben eine alternative Version von AlbumsByAlbumTitle erstellt, die eine Kopie der Spalte MarketingBudget im Index speichert. (Beachten Sie die STORING-Klausel in Fettschrift.)

CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget);

Beim alten AlbumsByAlbumTitle-Index muss Cloud Spanner den Index mit der Basistabelle verknüpfen und dann die Spalte aus der Basistabelle abrufen. Beim neuen Index AlbumsByAlbumTitle2 liest Cloud Spanner die Spalte direkt aus dem Index, was effizienter ist.

Wenn Sie statt SQL die Leseschnittstelle verwenden, können Sie mit dem neuen Index AlbumsByAlbumTitle2 auch direkt die Spalte MarketingBudget lesen:

C++

void ReadDataWithStoringIndex(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;

  spanner::ReadOptions read_options;
  read_options.index_name = "AlbumsByAlbumTitle2";
  auto rows =
      client.Read("Albums", google::cloud::spanner::KeySet::All(),
                  {"AlbumId", "AlbumTitle", "MarketingBudget"}, read_options);
  using RowType =
      std::tuple<std::int64_t, std::string, absl::optional<std::int64_t>>;
  for (auto const& row : spanner::StreamOf<RowType>(rows)) {
    if (!row) throw std::runtime_error(row.status().message());
    std::cout << "AlbumId: " << std::get<0>(*row) << "\t";
    std::cout << "AlbumTitle: " << std::get<1>(*row) << "\t";
    auto marketing_budget = std::get<2>(*row);
    if (marketing_budget) {
      std::cout << "MarketingBudget: " << *marketing_budget << "\n";
    } else {
      std::cout << "MarketingBudget: NULL\n";
    }
  }
  std::cout << "Read completed for [spanner_read_data_with_storing_index]\n";
}

C#


using Google.Cloud.Spanner.Data;
using System.Collections.Generic;
using System.Threading.Tasks;

public class QueryDataWithStoringIndexAsyncSample
{
    public class Album
    {
        public int AlbumId { get; set; }
        public string AlbumTitle { get; set; }
        public long? MarketingBudget { get; set; }
    }

    public async Task<List<Album>> QueryDataWithStoringIndexAsync(string projectId, string instanceId, string databaseId)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";

        using var connection = new SpannerConnection(connectionString);
        var cmd = connection.CreateSelectCommand(
            "SELECT AlbumId, AlbumTitle, MarketingBudget FROM Albums@ "
            + "{FORCE_INDEX=AlbumsByAlbumTitle2}");

        var albums = new List<Album>();
        using var reader = await cmd.ExecuteReaderAsync();
        while (await reader.ReadAsync())
        {
            albums.Add(new Album
            {
                AlbumId = reader.GetFieldValue<int>("AlbumId"),
                AlbumTitle = reader.GetFieldValue<string>("AlbumTitle"),
                MarketingBudget = reader.IsDBNull(reader.GetOrdinal("MarketingBudget")) ? 0 : reader.GetFieldValue<long>("MarketingBudget")
            });
        }
        return albums;
    }
}

Go


import (
	"context"
	"fmt"
	"io"
	"strconv"

	"cloud.google.com/go/spanner"
	"google.golang.org/api/iterator"
)

func readStoringIndex(w io.Writer, db string) error {
	ctx := context.Background()
	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return err
	}
	defer client.Close()

	iter := client.Single().ReadUsingIndex(ctx, "Albums", "AlbumsByAlbumTitle2", spanner.AllKeys(),
		[]string{"AlbumId", "AlbumTitle", "MarketingBudget"})
	defer iter.Stop()
	for {
		row, err := iter.Next()
		if err == iterator.Done {
			return nil
		}
		if err != nil {
			return err
		}
		var albumID int64
		var marketingBudget spanner.NullInt64
		var albumTitle string
		if err := row.Columns(&albumID, &albumTitle, &marketingBudget); err != nil {
			return err
		}
		budget := "NULL"
		if marketingBudget.Valid {
			budget = strconv.FormatInt(marketingBudget.Int64, 10)
		}
		fmt.Fprintf(w, "%d %s %s\n", albumID, albumTitle, budget)
	}
}

Java

static void readStoringIndex(DatabaseClient dbClient) {
  // We can read MarketingBudget also from the index since it stores a copy of MarketingBudget.
  try (ResultSet resultSet =
      dbClient
          .singleUse()
          .readUsingIndex(
              "Albums",
              "AlbumsByAlbumTitle2",
              KeySet.all(),
              Arrays.asList("AlbumId", "AlbumTitle", "MarketingBudget"))) {
    while (resultSet.next()) {
      System.out.printf(
          "%d %s %s\n",
          resultSet.getLong(0),
          resultSet.getString(1),
          resultSet.isNull("MarketingBudget") ? "NULL" : resultSet.getLong("MarketingBudget"));
    }
  }
}

Node.js

// "Storing" indexes store copies of the columns they index
// This speeds up queries, but takes more space compared to normal indexes
// See the link below for more information:
// https://cloud.google.com/spanner/docs/secondary-indexes#storing_clause

// Imports the Google Cloud client library
const {Spanner} = require('@google-cloud/spanner');

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';

// Creates a client
const spanner = new Spanner({
  projectId: projectId,
});

// Gets a reference to a Cloud Spanner instance and database
const instance = spanner.instance(instanceId);
const database = instance.database(databaseId);

const albumsTable = database.table('Albums');

const query = {
  columns: ['AlbumId', 'AlbumTitle', 'MarketingBudget'],
  keySet: {
    all: true,
  },
  index: 'AlbumsByAlbumTitle2',
};

// Reads the Albums table using a storing index
try {
  const [rows] = await albumsTable.read(query);

  rows.forEach(row => {
    const json = row.toJSON();
    let rowString = `AlbumId: ${json.AlbumId}`;
    rowString += `, AlbumTitle: ${json.AlbumTitle}`;
    if (json.MarketingBudget) {
      rowString += `, MarketingBudget: ${json.MarketingBudget}`;
    }
    console.log(rowString);
  });
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the database when finished.
  database.close();
}

PHP

use Google\Cloud\Spanner\SpannerClient;

/**
 * Reads sample data from the database using an index with a storing
 * clause.
 *
 * The index must exist before running this sample. You can add the index
 * by running the `add_storing_index` sample or by running this DDL statement
 * against your database:
 *
 *     CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle)
 *     STORING (MarketingBudget)
 *
 * Example:
 * ```
 * read_data_with_storing_index($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function read_data_with_storing_index($instanceId, $databaseId)
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $keySet = $spanner->keySet(['all' => true]);
    $results = $database->read(
        'Albums',
        $keySet,
        ['AlbumId', 'AlbumTitle', 'MarketingBudget'],
        ['index' => 'AlbumsByAlbumTitle2']
    );

    foreach ($results->rows() as $row) {
        printf('AlbumId: %s, AlbumTitle: %s, MarketingBudget: %d' . PHP_EOL,
            $row['AlbumId'], $row['AlbumTitle'], $row['MarketingBudget']);
    }
}

Python

def read_data_with_storing_index(instance_id, database_id):
    """Reads sample data from the database using an index with a storing
    clause.

    The index must exist before running this sample. You can add the index
    by running the `add_soring_index` sample or by running this DDL statement
    against your database:

        CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle)
        STORING (MarketingBudget)

    """
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    with database.snapshot() as snapshot:
        keyset = spanner.KeySet(all_=True)
        results = snapshot.read(
            table="Albums",
            columns=("AlbumId", "AlbumTitle", "MarketingBudget"),
            keyset=keyset,
            index="AlbumsByAlbumTitle2",
        )

        for row in results:
            print(u"AlbumId: {}, AlbumTitle: {}, " "MarketingBudget: {}".format(*row))

Ruby

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id

result = client.read "Albums", [:AlbumId, :AlbumTitle, :MarketingBudget],
                     index: "AlbumsByAlbumTitle2"

result.rows.each do |row|
  puts "#{row[:AlbumId]} #{row[:AlbumTitle]} #{row[:MarketingBudget]}"
end

Indexierung von NULL-Werten

NULL-Werte werden von Cloud Spanner standardmäßig indexiert. Denken Sie beispielsweise an die Definition des Index SingersByFirstLastName bei der Tabelle Singers zurück:

CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName);

Alle Zeilen von Singers werden auch dann indexiert, wenn FirstName oder LastName oder beide NULL sind.

Ein Diagramm zeigt Zeilen, die in einem nach NULL-Werten gefilterten Index ausgelassen wurden.

Wenn NULL-Werte indexiert werden, können Sie auf Daten, die NULL-Werte enthalten, effiziente SQL-Abfragen und Leseaufrufe durchführen. Verwenden Sie beispielsweise diese SQL-Abfrageanweisung, um alle Singers mit einem NULL FirstName zu finden:

SELECT s.SingerId, s.FirstName, s.LastName
    FROM Singers@{FORCE_INDEX=SingersByFirstLastName} AS s
    WHERE s.FirstName IS NULL;

Sortierreihenfolge für NULL-Werte

Cloud Spanner sortiert NULL als kleinsten Wert für den jeweiligen Typ ein. Bei einer Spalte in aufsteigender (ASC) Reihenfolge werden NULL-Werte als Erstes einsortiert. Bei einer Spalte in absteigender (DESC) Reihenfolge werden NULL-Werte als Letztes einsortiert.

Indexierung von NULL-Werten deaktivieren

Um die Indexierung von Null-Werten zu deaktivieren, fügen Sie das Schlüsselwort NULL_FILTERED zur Indexdefinition hinzu. NULL_FILTERED-Indexe sind besonders nützlich, wenn es um die Indexierung von dünnbesetzten Spalten geht, bei denen die meisten Zeilen einen NULL-Wert enthalten. In diesen Fällen kann der NULL_FILTERED-Index erheblich kleiner und effizienter zu pflegen sein als ein normaler Index, der NULL-Werte enthält.

Hier eine alternative Definition von SingersByFirstLastName, die keine NULL-Werte indexiert:

CREATE NULL_FILTERED INDEX SingersByFirstLastNameNoNulls
    ON Singers(FirstName, LastName);

Das Keyword NULL_FILTERED gilt für alle Indexschlüsselspalten. Eine NULL-Filterung kann nicht pro Spalte angegeben werden.

Wenn Sie einen Index NULL_FILTERED machen, kann Cloud Spanner ihn für einige Abfragen nicht verwenden. Cloud Spanner verwendet den Index beispielsweise nicht für diese Abfrage, da der Index keine Singers-Zeilen enthält, für die LastName NULL ist. Daher würde die Verwendung des Index verhindern, dass die Abfrage die richtigen Zeilen zurückgibt:

FROM Singers@{FORCE_INDEX=SingersByFirstLastNameNoNulls}
    WHERE FirstName = "John";

Damit Cloud Spanner den Index verwenden kann, müssen Sie die Abfrage so umschreiben, dass sie dieselben Zeilen ausschließt, die ebenfalls aus dem Index ausgeschlossen sind:

SELECT FirstName, LastName
    FROM Singers@{FORCE_INDEX=SingersByFirstLastNameNoNulls}
    WHERE FirstName = "John" AND LastName IS NOT NULL;

Eindeutige Indexe

Indexe können als UNIQUE deklariert werden. UNIQUE-Indexe fügen den indexierten Daten eine Einschränkung hinzu, die doppelte Einträge für einen bestimmten Indexschlüssel verbietet. Diese Einschränkung wird von Cloud Spanner zum Zeitpunkt des Transaktions-Commit erzwungen. Genauer gesagt kann eine Transaktion, die dazu führen würde, dass mehrere Indexeinträge für denselben Schlüssel existieren, nicht ausgeführt werden.

Wenn eine Tabelle anfänglich keine UNIQUE-Daten enthält, schlägt der Versuch fehl, einen UNIQUE-Index zu erstellen.

Ein Hinweis zu UNIQUE NULL_FILTERED-Indexen

Ein UNIQUE NULL_FILTERED-Index erzwingt keine Eindeutigkeit des Indexschlüssels, wenn mindestens eine der Schlüsselkomponenten des Index NULL ist.

Angenommen, Sie haben die folgende Tabelle und den folgenden Index erstellt:

CREATE TABLE ExampleTable (
  Key1 INT64 NOT NULL,
  Key2 INT64,
  Key3 INT64,
  Col1 INT64,
) PRIMARY KEY (Key1, Key2, Key3);

CREATE UNIQUE NULL_FILTERED INDEX ExampleIndex ON ExampleTable (Key1, Key2, Col1);

Die folgenden zwei Zeilen in ExampleTable weisen die gleichen Werte für die Sekundärindexschlüssel Key1, Key2 und Col1 auf:

1, NULL, 1, 1
1, NULL, 2, 1

Da Key2 NULL und der Index NULL_FILTERED ist, werden die Zeilen nicht im Index ExampleIndex vorhanden sein. Da sie nicht in den Index eingefügt werden, wird der Index sie nicht für den Verstoß gegen die Eindeutigkeit von (Key1, Key2, Col1) abweisen.

Wenn der Index die Eindeutigkeit von Werten des Tupels (Key1, Key2, Col1) erzwingen soll, müssen Sie Key2 in der Tabellendefinition mit NOT NULL kommentieren oder den Index ohne NULL_FILTERED erstellen.

Index löschen

Mit der Anweisung DROP INDEX entfernen Sie einen sekundären Index aus Ihrem Schema.

So löschen Sie den Index mit dem Namen SingersByFirstLastName:

DROP INDEX SingersByFirstLastName;

Nächste Schritte