

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

# Verwenden globaler sekundärer Indizes in DynamoDB
<a name="GSI"></a>

Einige Anwendungen müssen u. U. viele Arten von Abfragen ausführen und verwenden dabei eine Vielzahl von verschiedenen Attributen als Abfragekriterien. Um diese Anforderungen zu unterstützen, können Sie eine oder mehrere *globale sekundäre Indizes* erstellen und `Query`-Anforderungen für diese Indizes in Amazon DynamoDB generieren.

**Topics**
+ [Schritt 6: Verwenden eines globalen sekundären Indexes](#GSI.scenario)
+ [Attributprojektionen](#GSI.Projections)
+ [Schlüsselschema mit mehreren Attributen](#GSI.MultiAttributeKeys)
+ [Lesen von Daten aus einem globalen sekundären Index](#GSI.Reading)
+ [Datensynchronisierung zwischen Tabellen und globalen sekundären Indizes](#GSI.Writes)
+ [Tabellenklassen mit globalen sekundären Indizes](#GSI.tableclasses)
+ [Überlegungen im Hinblick auf die bereitgestellte Durchsatzkapazität für globale sekundäre Indizes](#GSI.ThroughputConsiderations)
+ [Speicherüberlegungen für globale sekundäre Indizes](#GSI.StorageConsiderations)
+ [Muster entwerfen](GSI.DesignPatterns.md)
+ [Verwalten globaler sekundärer Indizes in DynamoDB](GSI.OnlineOps.md)
+ [Erkennen und Korrigieren von Indexschlüsselverstößen in DynamoDB](GSI.OnlineOps.ViolationDetection.md)
+ [Arbeiten mit globalen sekundären Indizes: Java](GSIJavaDocumentAPI.md)
+ [Arbeiten mit globalen sekundären Indizes: .NET](GSILowLevelDotNet.md)
+ [Arbeiten mit globalen sekundären Indizes in DynamoDB mithilfe der AWS CLI](GCICli.md)

## Schritt 6: Verwenden eines globalen sekundären Indexes
<a name="GSI.scenario"></a>

Betrachten wir ein Beispiel zur Veranschaulichung. Eine Tabelle mit dem Namen `GameScores` erfasst die Benutzer und Punktzahlen für eine mobile Gaming-Anwendung. Jedes Element in `GameScores` wird anhand eines Partitionsschlüssels (`UserId`) und eines Sortierschlüssels (`GameTitle`) identifiziert. Das folgende Diagramm zeigt, wie die Elemente in der Tabelle organisiert wären. (Es werden nicht alle Attribute angezeigt.)

![\[GameScores Tabelle mit einer Liste von Benutzer-ID, Titel, Punktzahl, Datum und Gewinnen/Niederlagen.\]](http://docs.aws.amazon.com/de_de/amazondynamodb/latest/developerguide/images/GSI_01.png)


Angenommen, Sie möchten eine Bestenlisten-Anwendung für die Anzeige der höchsten Punktzahlen für jedes Spiel entwickeln. Eine Abfrage, die die Schlüsselattribute (`UserId` und `GameTitle`) angibt, wäre äußerst effizient. Wenn die Anwendung Daten aus `GameScores` nur basierend auf `GameTitle` abrufen muss, muss sie eine `Scan`-Operation verwenden. Wenn immer mehr Elemente der Tabelle hinzugefügt werden, werden die Scans aller Daten langsam und ineffizient. Dadurch wird die Beantwortung von folgenden Fragen erschwert:
+ Was ist die höchste Punktzahl, die für das Spiel Meteor Blaster jemals erfasst wurde?
+ Welche Benutzer hatte die höchste Punktzahl für Galaxy Invaders?
+ Wie war das höchste Verhältnis zwischen gewonnen und verlorenen Spielen?

Zum Beschleunigen von Abfragen auf der Basis von Nicht-Schlüsselattributen können Sie einen globalen sekundären Index erstellen. Ein globaler sekundärer Index enthält eine Auswahl von Attributen aus der Basistabelle. Diese sind jedoch nach einem Primärschlüssel organisiert, der sich von dem Schlüssel der Tabelle unterscheidet. Der Indexschlüssel benötigt keinen der Schlüsselattribute aus der Tabelle. Er muss nicht einmal über dasselbe Schlüsselschema verfügen wie eine Tabelle.

Sie können beispielsweise einen globalen sekundären Index mit dem Namen `GameTitleIndex` mit einem Partitionsschlüssel `GameTitle` und einem Sortierschlüssel `TopScore` erstellen. Da die Primärschlüsselattribute der Basistabelle immer in einen Index projiziert werden, ist das Attribut `UserId` ebenfalls vorhanden. Das folgende Diagramm veranschaulicht, wie der Index `GameTitleIndex` aussehen würde.

![\[GameTitleIndex Tabelle mit einer Liste von Titeln, Ergebnissen und Benutzer-IDs.\]](http://docs.aws.amazon.com/de_de/amazondynamodb/latest/developerguide/images/GSI_02.png)


Jetzt können Sie `GameTitleIndex` abfragen und die Punktzahlen für Meteor Blasters einfach erhalten. Die Ergebnisse werden nach den Sortierschlüsselwerten `TopScore` sortiert. Wenn Sie den Parameter `ScanIndexForward` auf False festlegen, werden die Ergebnisse in der absteigender Reihenfolge mit der höchsten Punktzahl zuerst zurückgegeben.

Jeder globale sekundäre Index muss über einen Partitionsschlüssel verfügen und kann einen optionalen Sortierschlüssel besitzen. Das Indexschlüsselschema kann sich vom Basistabellenschema unterscheiden. Sie können eine Tabelle mit einem einfachen Primärschlüssel (Partitionsschlüssel) vorliegen haben und einen globalen sekundären Index mit einem zusammengesetzten Primärschlüssel (Partitions- und Sortierschlüssel) erstellen oder umgekehrt. Die Indexschlüsselattribute können aus beliebigen Top-Level-Attributen vom Typ `String`, `Number` oder `Binary` der Basistabelle bestehen. Andere Skalar-, Dokument- und Satztypen sind nicht zulässig.

Sie können andere Basistabellenattribute in den Index projizieren, wenn Sie möchten. Wenn Sie den Index abfragen, kann DynamoDB diese projizierten Attribute effizient abrufen. Globale sekundäre Index-Abfragen können jedoch keine Attribute aus der Basistabelle abrufen. Wenn Sie beispielsweise `GameTitleIndex` wie im Diagramm oben veranschaulicht abgefragt haben, kann die Abfrage nicht auf Nicht-Schlüsselattribute außer `TopScore` zugreifen (die Schlüsselattribute `GameTitle` und `UserId` werden allerdings automatisch projiziert).

In einer DynamoDB-Tabelle, muss jeder Schlüsselwert eindeutig sein. Die Schlüsselwerte in einem globalen sekundären Index müssen jedoch nicht eindeutig sein. Beispiel: Angenommen, ein Spiel mit dem Namen Comet Quest ist besonders schwierig. Viele neue Benutzer probieren es aus, schaffen es aber nicht, eine Punktzahl über Null zu erreichen. Im Folgenden finden Sie einige der Daten, die dies veranschaulichen.


****  

| UserId | GameTitle | TopScore | 
| --- | --- | --- | 
| 123 | Comet Quest | 0 | 
| 201 | Comet Quest | 0 | 
| 301 | Comet Quest | 0 | 

Wenn diese Daten der Tabelle `GameScores` hinzugefügt werden, verteilt DynamoDB sie auf `GameTitleIndex`. Wenn wir dann den Index mit Comet Quest für `GameTitle` und mit 0 für `TopScore` abfragen, werden die folgenden Daten zurückgegeben.

![\[Tabelle mit einer Liste mit Titeln, Höchstpunktzahlen und Benutzer-IDs.\]](http://docs.aws.amazon.com/de_de/amazondynamodb/latest/developerguide/images/GSI_05.png)


Es werden nur die Elemente mit den angegebenen Schlüsselwerten in der Antwort angezeigt. Innerhalb dieser Datengruppe werden die Elemente nicht in einer bestimmten Reihenfolge angezeigt. 

Ein globaler sekundärer Index verfolgt nur Datenelemente, bei denen die Schlüsselattribute tatsächlich vorhanden sind. Angenommen, Sie haben der Tabelle `GameScores` ein neues Element hinzugefügt, jedoch nur die erforderlichen Primärschlüsselattribute bereitgestellt.


****  

| UserId | GameTitle | 
| --- | --- | 
| 400 | Comet Quest | 

Da Sie das Attribut `TopScore` nicht angegeben haben, verteilt DynamoDB dieses Element nicht über `GameTitleIndex`. Wenn Sie `GameScores` für alle Comet Quest-Elemente abfragen, erhalten Sie die folgenden vier Elemente:

![\[Tabelle mit einer Liste mit vier Titeln, Höchstpunktzahlen und Benutzer-IDs.\]](http://docs.aws.amazon.com/de_de/amazondynamodb/latest/developerguide/images/GSI_04.png)


Eine ähnliche Abfrage für `GameTitleIndex` gibt drei Elemente statt vier zurück. Der Grund hierfür ist, dass das Element mit dem nicht vorhandenen `TopScore` nicht an den Index verteilt wird.

![\[Tabelle mit einer Liste mit drei Titeln, Höchstpunktzahlen und Benutzer-IDs.\]](http://docs.aws.amazon.com/de_de/amazondynamodb/latest/developerguide/images/GSI_05.png)


## Attributprojektionen
<a name="GSI.Projections"></a>

Eine *Projektion* ist der Satz von Attributen, die aus einer Tabelle in einen sekundären Index kopiert werden. Der Partitionsschlüssel und der Sortierschlüssel der Tabelle werden immer in den Index projiziert. Sie können andere Attribute projizieren, um die Abfrageanforderungen Ihrer Anwendung zu unterstützen. Wenn Sie einen Index abfragen, kann Amazon DynamoDB auf jedes Attribut in der Projektion zugreifen, als ob sich diese Attribute in einer eigenen Tabelle befinden.

Wenn Sie einen sekundären Index erstellen, müssen Sie die Attribute angeben, die in den Index projiziert werden. DynamoDB bietet hierfür drei verschiedene Optionen:
+ *KEYS\$1ONLY* – Jeder Eintrag im Index besteht nur aus dem Tabellenpartitionsschlüssel und Sortierschlüsselwerten, sowie den Indexschlüsselwerten. Die Option `KEYS_ONLY` führt zu dem kleinstmöglichen sekundären Index.
+ *INCLUDE* – Zusätzlich zu den in `KEYS_ONLY` beschriebenen Attributen, enthält der sekundäre Index andere Nicht-Schlüsselattribute, die Sie angeben.
+ *ALL* – Der sekundäre Index enthält alle Attribute der Quelltabelle. Da alle Tabellendaten im Index dupliziert werden, wird ein `ALL`-Projektion führt zu dem größtmöglichen sekundären Index.

Im vorherigen Diagramm verfügt `GameTitleIndex` nur über ein projiziertes Attribut: `UserId`. Obwohl eine Anwendung die `UserId` der besten Ergebnisse für jedes Spiel mit `GameTitle` und `TopScore` effizient in Abfragen bestimmen kann, ist es nicht möglich, effizient die höchste Punktzahl für ein bestimmtes Spiel oder das höchste Verhältnis gewonnener und verlorener Spiele bei den besten Ergebnissen zu bestimmen. Dazu müsste die Anwendung eine zusätzliche Abfrage in der Basistabelle durchführen, um die Siege und Niederlagen für jeden der besten Torschützen abzurufen. Abfragen für diese Daten lassen sich effizienter unterstützen, indem diese Attribute aus der Basistabelle in den globalen sekundären Index projiziert werden, wie in folgendem Diagramm dargestellt. 

![\[Darstellung der Projektion von Nicht-Schlüsselattributen in einem globalen sekundären Index zur Unterstützung effizienter Abfragen.\]](http://docs.aws.amazon.com/de_de/amazondynamodb/latest/developerguide/images/GSI_06.png)


Da die Nicht-Schlüsselattribute `Wins` und `Losses` in den Index projiziert werden, kann eine Anwendung das Verhältnis zwischen Siegen und Niederlagen für jedes Spiel oder für eine beliebige Kombination aus Spiel und Benutzer-ID bestimmen.

Wenn Sie die Attribute zum Projizieren in einen globalen sekundären Index auswählen, müssen Sie die Differenz der Kosten des bereitgestellten Durchsatzes und der Speicherkosten berücksichtigen:
+ Wenn Sie nur auf wenige Attribute mit möglichst niedriger Latenz zugreifen müssen, erwägen Sie, nur diese Attribute in einen globalen sekundären Index zu projizieren. Je kleiner der Index, desto geringer die Speicher- und somit die Schreibkosten.
+ Greift Ihre Anwendung häufig auf einige Nicht-Schlüsselattribute zu, sollten Sie erwägen, diese Attribute in einen globalen sekundären Index zu projizieren. Die zusätzlichen Speicherkosten für den globalen sekundären Index wiegen die Kosten für die Durchführung häufiger Tabellen-Scans auf.
+ Wenn Sie auf die meisten Nicht-Schlüsselattribute in regelmäßigen Abständen zugreifen müssen, können Sie diese Attribute – oder sogar die gesamte Basistabelle – in einen globalen sekundären Index projizieren. Dadurch erhalten Sie maximale Flexibilität. Ihre Speicherkosten würden allerdings steigen oder sich sogar verdoppeln.
+ Wenn Ihre Anwendung eine Tabelle selten abfragen, jedoch viele Schreibvorgänge oder Updates für die Daten in der Tabelle durchführen muss, erwägen Sie das Projizieren von `KEYS_ONLY`. Der globale sekundäre Index wäre nur klein, stünde aber weiterhin bei Bedarf für Abfrageaktivitäten zur Verfügung. 

## Schlüsselschema mit mehreren Attributen
<a name="GSI.MultiAttributeKeys"></a>

Globale Sekundärindizes unterstützen Schlüssel mit mehreren Attributen, sodass Sie Partitionsschlüssel zusammenstellen und Schlüssel aus mehreren Attributen sortieren können. Mit Schlüsseln mit mehreren Attributen können Sie einen Partitionsschlüssel aus bis zu vier Attributen und einen Sortierschlüssel aus bis zu vier Attributen erstellen, sodass insgesamt bis zu acht Attribute pro Schlüsselschema vorhanden sind.

Schlüssel mit mehreren Attributen vereinfachen Ihr Datenmodell, indem sie das manuelle Verketten von Attributen zu synthetischen Schlüsseln überflüssig machen. Anstatt zusammengesetzte Zeichenketten zu erstellen`TOURNAMENT#WINTER2024#REGION#NA-EAST`, können Sie die natürlichen Attribute aus Ihrem Domänenmodell direkt verwenden. DynamoDB verarbeitet die zusammengesetzte Schlüssellogik automatisch, indem es mehrere Partitionsschlüsselattribute für die Datenverteilung zusammenfasst und die hierarchische Sortierreihenfolge für mehrere Sortierschlüsselattribute beibehält.

Stellen Sie sich beispielsweise ein Spieleturniersystem vor, bei dem Sie Spiele nach Turnieren und Regionen organisieren möchten. Bei Schlüsseln mit mehreren Attributen können Sie Ihren Partitionsschlüssel als zwei separate Attribute definieren: `tournamentId` und`region`. In ähnlicher Weise können Sie Ihren Sortierschlüssel mit mehreren Attributen wie`round`, und definieren`bracket`, `matchId` um eine natürliche Hierarchie zu erstellen. Dieser Ansatz sorgt dafür, dass Ihre Daten eingegeben und Ihr Code sauber bleibt, ohne dass Zeichenketten manipuliert oder analysiert werden müssen.

Wenn Sie einen globalen sekundären Index mit Schlüsseln mit mehreren Attributen abfragen, müssen Sie alle Partitionsschlüsselattribute unter Verwendung von Gleichheitsbedingungen angeben. Bei Sortierschlüsselattributen können Sie sie left-to-right in der Reihenfolge abfragen, in der sie im Schlüsselschema definiert sind. Das bedeutet, dass Sie das erste Sortierschlüsselattribut allein, die ersten beiden Attribute zusammen oder alle Attribute zusammen abfragen können, aber Sie können keine Attribute in der Mitte überspringen. Ungleichheitsbedingungen wie`>`, `<``BETWEEN`, oder `begins_with()` müssen die letzte Bedingung in Ihrer Abfrage sein.

Schlüssel mit mehreren Attributen eignen sich besonders gut, wenn globale Sekundärindizes für bestehende Tabellen erstellt werden. Sie können Attribute verwenden, die bereits in Ihrer Tabelle vorhanden sind, ohne synthetische Schlüssel in Ihren Daten aufzufüllen. Auf diese Weise können Sie Ihrer Anwendung ganz einfach neue Abfragemuster hinzufügen, indem Sie Indizes erstellen, die Ihre Daten mithilfe verschiedener Attributkombinationen neu organisieren.

Jedes Attribut in einem Schlüssel mit mehreren Attributen kann seinen eigenen Datentyp haben: `String` (S), `Number` (N) oder `Binary` (B). Beachten Sie bei der Auswahl von Datentypen, dass `Number` Attribute numerisch sortiert werden, ohne dass Nullen aufgefüllt werden müssen, während Attribute lexikografisch sortiert werden. `String` Wenn Sie beispielsweise einen `Number` Typ für ein Bewertungsattribut verwenden, werden die Werte 5, 50, 500 und 1000 in natürlicher numerischer Reihenfolge sortiert. Dieselben Werte wie `String` Typ würden nach „1000", „5", „50", „500" sortiert, sofern Sie sie nicht mit führenden Nullen auffüllen.

Ordnen Sie beim Entwerfen von Schlüsseln mit mehreren Attributen die Attribute von den allgemeinsten bis hin zu den spezifischsten. Kombinieren Sie bei Partitionsschlüsseln Attribute, die immer zusammen abgefragt werden und für eine gute Datenverteilung sorgen. Platzieren Sie bei Sortierschlüsseln häufig abgefragte Attribute an erster Stelle in der Hierarchie, um die Flexibilität bei der Abfrage zu maximieren. Diese Reihenfolge ermöglicht es Ihnen, Abfragen auf jeder Granularitätsebene durchzuführen, die Ihren Zugriffsmustern entspricht.

Implementierungsbeispiele finden Sie unter. [Schlüssel mit mehreren Attributen](GSI.DesignPattern.MultiAttributeKeys.md)

## Lesen von Daten aus einem globalen sekundären Index
<a name="GSI.Reading"></a>

Sie können Elemente aus einem globalen sekundären Index mithilfe von `Query` und `Scan` verwenden. Die `GetItem` und `BatchGetItem`-Operationen können für einen globalen sekundären Index nicht verwendet werden.

### Abfragen eines globalen sekundären Index
<a name="GSI.Querying"></a>

Sie können die `Query`-Operation für den Zugriff auf ein oder mehrere Elemente in einem globalen sekundären Index verwenden. Die Abfrage muss den Namen der Basistabelle und den Namen des Index, den Sie verwenden möchten, die Attribute, die in den Abfrageergebnissen zurückgegeben werden sollen, und etwaige Abfragebedingungen, die Sie anwenden möchten, angeben. DynamoDB kann Ergebnisse in auf- oder absteigender Reihenfolge liefern.

Sehen Sie sich die folgenden Daten an, die von einer `Query` zurückgegeben wurden, die Spieledaten für eine Bestenlisten-Anwendung abfragt.

```
{
    "TableName": "GameScores",
    "IndexName": "GameTitleIndex",
    "KeyConditionExpression": "GameTitle = :v_title",
    "ExpressionAttributeValues": {
        ":v_title": {"S": "Meteor Blasters"}
    },
    "ProjectionExpression": "UserId, TopScore",
    "ScanIndexForward": false
}
```

Vorgänge in dieser Abfrage:
+ DynamoDB greift zu und verwendet den *GameTitle*Partitionsschlüssel *GameTitleIndex*, um die Indexelemente für Meteor Blasters zu finden. Alle Indexelemente mit diesem Schlüssel werden nebeneinander gespeichert, um ein schnelles Abrufen zu ermöglichen.
+ In diesem Spiel verwendet DynamoDB den Index, um auf alle Benutzer IDs - und Top-Scores für dieses Spiel zuzugreifen.
+ Die Ergebnisse werden in absteigender Reihenfolge sortiert zurückgegeben, da der Parameter `ScanIndexForward` auf False festgelegt ist.

### Scannen eines globalen sekundären Index
<a name="GSI.Scanning"></a>

Sie können die `Scan`-Operation zum Abrufen aller Daten aus einem globalen sekundären Index verwenden. Geben Sie dazu den Namen der Basistabelle sowie den Indexnamen in der Abfrage an. Mit einer `Scan`-Operation liest DynamoDB alle Daten im Index und gibt sie an die Anwendung zurück. Sie können auch anfordern, dass nur einige der Daten zurückgegeben und die verbleibenden Daten verworfen werden. Verwenden Sie dazu den Parameter `FilterExpression` der `Scan`-Operation. Weitere Informationen finden Sie unter [Filterausdrücke für Scan](Scan.md#Scan.FilterExpression).

## Datensynchronisierung zwischen Tabellen und globalen sekundären Indizes
<a name="GSI.Writes"></a>

DynamoDB synchronisiert automatisch jeden globalen sekundären Index mit der Basistabelle. Wenn eine Anwendung Elemente in eine Tabelle schreibt oder daraus löscht, werden globale sekundäre Indizes in dieser Tabelle mit einem Eventually-Consistent-Modell asynchron aktualisiert. Anwendungen schreiben niemals direkt in einen Index. Allerdings ist es wichtig, zu wissen, welche Auswirkungen es hat, wie DynamoDB diese Indizes verwaltet.

 Globale sekundäre Indizes erben den read/write Kapazitätsmodus aus der Basistabelle. Weitere Informationen finden Sie unter [Überlegungen beim Umstellen der Kapazitätsmodi in DynamoDB](bp-switching-capacity-modes.md). 

Wenn Sie einen globalen sekundären Index erstellen, geben Sie ein oder mehrere Indexschlüsselattribute und deren Datentypen an. Das bedeutet, dass wenn Sie ein Element in die Basistabelle schreiben, die Datentypen für diese Attribute den Datentypen des Indexschlüsselschemas entsprechen müssen. Im Fall von `GameTitleIndex` wird der Partitionsschlüssel `GameTitle` im Index als Datentyp `String` definiert. Der Sortierschlüssel `TopScore` im Index ist vom Typ `Number`. Wenn Sie versuchen, der Tabelle `GameScores` ein Element hinzuzufügen und einen anderen Datentyp entweder für `GameTitle` oder `TopScore` anzugeben, gibt DynamoDB eine `ValidationException` aufgrund der fehlenden Übereinstimmung des Datentyps zurück.

Wenn Sie Elemente in eine Tabelle schreiben oder daraus löschen, werden die globalen sekundären Indizes in dieser Tabelle in Eventually Consistent-Form aktualisiert. Änderungen an den Tabellendaten werden unter normalen Bedingungen im Bruchteil einer Sekunde auf die globalen sekundären Indizes verteilt. In einigen seltenen Fehlerszenarien können längere Verzögerungen bei der Verteilung auftreten. Ihre Anwendungen sollten daher Situationen abhelfen können, in denen eine Abfrage für einen globalen sekundären Index Ergebnisse zurückgibt, die nicht auf dem neuesten Stand sind.

Wenn Sie ein Element in eine Tabelle schreiben, müssen Sie keine Attribute für globale sekundäre Index-Sortierschlüssel angeben. Bei `GameTitleIndex` müssen Sie z. B. keinen Wert für das Attribut `TopScore` angeben, um ein neues Element in die Tabelle `GameScores` zu schreiben. In diesem Fall schreibt DynamoDB keine Daten in den Index für dieses bestimmte Element.

Eine Tabelle mit vielen globalen sekundären Indizes verursacht höhere Kosten für Schreibaktivitäten als Tabellen mit weniger Indizes. Weitere Informationen finden Sie unter [Überlegungen im Hinblick auf die bereitgestellte Durchsatzkapazität für globale sekundäre Indizes](#GSI.ThroughputConsiderations).

## Tabellenklassen mit globalen sekundären Indizes
<a name="GSI.tableclasses"></a>

Ein globaler sekundärer Index verwendet immer dieselbe Tabellenklasse wie seine Basistabelle. Jedes Mal, wenn ein neuer globaler sekundärer Index für eine Tabelle hinzugefügt wird, verwendet der neue Index dieselbe Tabellenklasse wie seine Basistabelle. Wenn die Tabellenklasse einer Tabelle aktualisiert wird, werden auch alle zugehörigen globalen sekundären Indizes aktualisiert.

## Überlegungen im Hinblick auf die bereitgestellte Durchsatzkapazität für globale sekundäre Indizes
<a name="GSI.ThroughputConsiderations"></a>

Wenn Sie einen globalen sekundären Index für eine Tabelle mit dem Modus bereitgestellter Kapazität erstellen, müssen Sie Lese- und Schreibkapazitätseinheiten für den erwarteten Workload dieses Indexes angeben. Die Einstellungen für den bereitgestellten Durchsatz eines globalen sekundären Indizes sind getrennt von denen der Basistabelle. Eine `Query`-Operation auf einem globalen sekundären Index verbraucht Lesekapazitätseinheiten des Index und nicht der Basistabelle. Wenn Sie Elemente in eine Tabelle schreiben, sie aktualisieren oder aus der Tabelle löschen, werden die globalen sekundären Indizes in dieser Tabelle in Eventually Consistent-Form aktualisiert. Diese Indexaktualisierungen verbrauchen Kapazitätseinheiten des Indexes und nicht der Basistabelle.

Wenn Sie beispielsweise eine `Query`-Operation für einen ausführen und die bereitgestellte Lesekapazität überschreiten, wird die Anforderung gedrosselt. Wenn Sie viele Schreibaktivitäten für die Tabelle ausführen, ein globaler sekundärer Index dieser Tabelle jedoch über nicht genügend Schreibkapazität verfügt, dann wird die Schreibaktivität der Tabelle gedrosselt.

**Wichtig**  
 Damit es zu keiner Ablehnung kommt, sollte die bereitgestellte Kapazität für Schreibvorgänge bei einem globalen sekundären Index gleich oder größer als die Kapazität für Schreibvorgänge der Basistabelle sein, da neue Aktualisierungen in die Basistabelle und den globalen sekundären Index geschrieben werden. 

Verwenden Sie zum Anzeigen der Einstellungen des bereitgestellten Durchsatzes für einen globalen sekundären Index die `DescribeTable`-Operation. Es werden detaillierte Informationen zu globalen sekundären Indizes für die Tabelle zurückgegeben.

### Lesekapazitätseinheiten
<a name="GSI.ThroughputConsiderations.Reads"></a>

Globale sekundäre Indizes unterstützen Eventually Consistent-Lesevorgänge, die jeweils eine halbe Lesekapazitätseinheit verbrauchen. Das bedeutet, dass eine einzelne globale sekundäre Index-Abfrage bis zu 2 × 4 KB = 8 KB pro Lesekapazitätseinheit abrufen kann.

Für globale sekundäre Index-Abfragen berechnet DynamoDB die bereitgestellten Lesevorgänge auf die gleiche Weise wie für Abfragen der Tabellen. Der einzige Unterschied besteht darin, dass die Berechnung auf der Größe der Indexeinträge und nicht auf der Größe des Elements in der Basistabelle beruht. Die Anzahl der Lesekapazitätseinheiten ist die Summe aller projizierten Attributgrößen sämtlicher zurückgegebener Elemente. Das Ergebnis wird dann auf den nächsten 4 KB-Grenzwert aufgerundet. Weitere Informationen darüber, wie DynamoDB die bereitgestellte Durchsatznutzung berechnet, finden Sie unter [DynamoDB – Modus mit bereitgestellter Kapazität](provisioned-capacity-mode.md).

Die maximale Größe der von einer `Query`-Operation zurückgegebenen Ergebnisse beträgt 1MB. Diese umfasst die Größen aller Attributnamen und Werte sämtlicher zurückgegebenen Elemente.

Nehmen wir als Beispiel einen globalen sekundären Index bei dem jedes Element 2 000 Byte Daten enthält. Angenommen, Sie führen eine `Query`-Operation für diesen Index aus, bei der `KeyConditionExpression` 8 Elemente zurückgegeben werden. Die Gesamtgröße der übereinstimmenden Elemente beträgt: 2 000 Byte x 8 Elemente = 16 000 Byte. Das Ergebnis wird dann auf den nächsten 4-KB-Grenzwert aufgerundet. Da globale sekundäre Index-Abfragen Eventually Consistent ausgeführt werden, belaufen sich die Gesamtkosten auf 0,5  (16 KB/ 4 KB) oder 2 Lesekapazitätseinheiten.

### Schreibkapazitätseinheiten
<a name="GSI.ThroughputConsiderations.Writes"></a>

Wenn ein Element in einer Tabelle hinzugefügt, aktualisiert oder gelöscht wird und ein globaler sekundärer Index davon betroffen ist, dann belegt der globale sekundäre Index bereitgestellte Schreibkapazitätseinheiten für die Operation. Die Gesamtkosten für den bereitgestellten Durchsatz ergeben sich aus der Summe der Schreibkapazitätseinheiten, die durch Schreiben in die Basistabelle verbraucht wurden, und der durch Aktualisieren der globalen sekundären Indizes belegten Einheiten. Hinweis: Wenn ein Schreibvorgang in eine Tabelle keine globale sekundäre Index-Aktualisierung erfordert, wird keine Schreibkapazität vom Index verbraucht.

Damit ein Tabellenschreibvorgang erfolgreich ausgeführt wird, muss der für die Tabelle und alle zugehörigen globalen sekundären Indizes bereitgestellte Durchsatz genügend Kapazität aufweisen. Andernfalls wird der Schreibvorgang in die Tabelle gedrosselt. 

**Wichtig**  
Bei der Erstellung eines globalen sekundären Index (GSI) können Schreibvorgänge in die Basistabelle gedrosselt werden, wenn die GSI-Aktivität, die sich aus Schreibvorgängen in die Basistabelle ergibt, die bereitgestellte Schreibkapazität der GSI überschreitet. Diese Drosselung wirkt sich auf alle Schreibvorgänge aus, vom Indizierungsprozess bis hin zur möglichen Unterbrechung Ihrer Produktions-Workloads. Weitere Informationen finden Sie unter [Beheben von Drosselungsereignissen in Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TroubleshootingThrottling.html).

Die Kosten für das Schreiben eines Elements in einen globalen sekundären Index hängen von mehreren Faktoren ab:
+ Wenn Sie ein neues Element in die Tabelle schreiben, die ein indiziertes Attribut definiert, oder ein vorhandenes Element zum Definieren eines zuvor nicht definierten indizierten Attributs aktualisieren, ist ein Schreibvorgang erforderlich, um das Element in den Index einzufügen.
+ Wenn eine Aktualisierung der Tabelle den Wert eines indizierten Schlüsselattributs (von A in B) ändert, sind zwei Schreibvorgänge erforderlich, und zwar einer zum Löschen des vorherigen Elements aus dem Index und einer zum Schreiben des neuen Elements in den Index.  
+ Wenn ein Element im Index vorhanden war, ein Schreibvorgang in der Tabelle jedoch dazu führte, dass das indizierte Attribut gelöscht wurde, ist ein Schreibvorgang erforderlich, um die alte Elementprojektion im Index zu löschen.
+ Wenn ein Element nicht im Index vorhanden ist, bevor oder nachdem das Element aktualisiert wird, fallen keine zusätzlichen Kosten für das Schreiben in den Index an.
+ Wenn durch eine Aktualisierung der Tabelle nur der Wert von projizierten Attributen im Indexschlüsselschema, nicht aber der Wert von indizierten Schlüsselattributen geändert wird, ist ein Schreibvorgang erforderlich, um die Werte der projizierten Attribute im Index zu aktualisieren.

Alle diese Faktoren setzen voraus, dass die Größe der einzelnen Elemente im Index kleiner oder gleich der 1-KB- Elementgröße für das Berechnen der Schreibkapazitätseinheiten ist. Größere Indexeinträge erfordern zusätzliche Schreibkapazitätseinheiten. Sie können Ihre Kosten für Schreibvorgänge minimieren, indem Sie überlegen, welche Attribute Ihre Abfragen zurückgeben müssen, und nur diese Attribute in den Index projizieren.

## Speicherüberlegungen für globale sekundäre Indizes
<a name="GSI.StorageConsiderations"></a>

Wenn eine Anwendung ein Element in eine Tabelle schreibt, kopiert DynamoDB automatisch die richtige Teilmenge der Attribute in den globalen sekundären Index, in dem diese Attribute angezeigt werden sollen. Ihrem AWS Konto werden sowohl die Speicherung des Elements in der Basistabelle als auch die Speicherung von Attributen in allen globalen Sekundärindizes dieser Tabelle in Rechnung gestellt.

Der Speicherplatz, der von einem Indexelement belegt wird, ergibt sich aus der Summe von folgenden Werten:
+ Die Größe in Byte des Primärschlüssels der Basistabelle (Partitionsschlüssel und Sortierschlüssel)
+ Die Größe in Byte des Indexschlüsselattributs
+ Die Größe in Byte der projizierten Attribute (sofern vorhanden)
+ 100 Bytes des Overheads pro Indexelement

Zur Schätzung der Speicheranforderungen für einen globalen sekundären Index können Sie die durchschnittliche Größe eines Elements im Index schätzen und diesen Wert mit der Anzahl der Elemente in der Basistabelle multiplizieren, die die globalen sekundären Index-Schlüsselattribute besitzen.

Wenn eine Tabelle ein Element enthält, für das ein oder mehrere bestimmte Attribute nicht definiert sind, dieses Attribut jedoch als Indexpartitionsschlüssel oder Sortierschlüssel definiert ist, schreibt DynamoDB keine Daten für dieses Element in den Index.

# Muster entwerfen
<a name="GSI.DesignPatterns"></a>

Entwurfsmuster bieten bewährte Lösungen für häufig auftretende Herausforderungen bei der Arbeit mit globalen Sekundärindizes. Diese Muster helfen Ihnen bei der Entwicklung effizienter, skalierbarer Anwendungen, indem sie Ihnen zeigen, wie Sie Ihre Indizes für bestimmte Anwendungsfälle strukturieren.

Jedes Muster enthält einen vollständigen Implementierungsleitfaden mit Codebeispielen, bewährten Methoden und realen Anwendungsfällen, die Ihnen helfen, das Muster auf Ihre eigenen Anwendungen anzuwenden.

**Topics**
+ [Schlüssel mit mehreren Attributen](GSI.DesignPattern.MultiAttributeKeys.md)

# Schlüsselmuster mit mehreren Attributen
<a name="GSI.DesignPattern.MultiAttributeKeys"></a>

## Übersicht
<a name="GSI.DesignPattern.MultiAttributeKeys.Overview"></a>

Mit Schlüsseln mit mehreren Attributen können Sie eine GSI-Partition (Global Secondary Index) erstellen und Schlüssel sortieren, die jeweils aus bis zu vier Attributen bestehen. Dadurch wird der clientseitige Code reduziert und es ist einfacher, Daten zunächst zu modellieren und später neue Zugriffsmuster hinzuzufügen.

Stellen Sie sich ein gängiges Szenario vor: Um einen globalen Index zu erstellen, der Elemente anhand mehrerer hierarchischer Attribute abfragt, müssten Sie üblicherweise synthetische Schlüssel erstellen, indem Sie Werte verketten. Um beispielsweise in einer Gaming-App Turnierspiele nach Turnier, Region und Runde abzufragen, könnten Sie einen synthetischen GSI-Partitionsschlüssel wie TOURNAMENT\$1 WINTER2 024 \$1REGION \$1NA -EAST und einen synthetischen Sortierschlüssel wie ROUND \$1SEMIFINALS \$1BRACKET \$1UPPER erstellen. Dieser Ansatz funktioniert, erfordert jedoch die Verkettung von Zeichenketten beim Schreiben von Daten, das Parsen beim Lesen und das Auffüllen synthetischer Schlüssel für alle vorhandenen Elemente, wenn Sie den GSI zu einer vorhandenen Tabelle hinzufügen. Dadurch wird der Code unübersichtlicher und es ist schwierig, die Typsicherheit einzelner Schlüsselkomponenten aufrechtzuerhalten.

Schlüssel mit mehreren Attributen lösen dieses Problem für. GSIs Sie definieren Ihren GSI-Partitionsschlüssel mithilfe mehrerer vorhandener Attribute wie TournamentID und Region. DynamoDB verarbeitet die Logik der zusammengesetzten Schlüssel automatisch und hasht sie für die Datenverteilung zusammen. Sie schreiben Elemente mit natürlichen Attributen aus Ihrem Domänenmodell, und die GSI indexiert sie automatisch. Keine Verkettung, kein Parsen, kein Hinterfüllen. Ihr Code bleibt sauber, Ihre Daten bleiben eingegeben und Ihre Abfragen bleiben einfach. Dieser Ansatz ist besonders nützlich, wenn Sie über hierarchische Daten mit natürlichen Attributgruppierungen verfügen (wie Turnier → Region → Runde oder Organisation → Abteilung → Team).

## Beispiel für eine Anwendung
<a name="GSI.DesignPattern.MultiAttributeKeys.ApplicationExample"></a>

In diesem Leitfaden wird der Aufbau eines Systems zur Nachverfolgung von Turnierspielen für eine Esports-Plattform beschrieben. Die Plattform muss Spiele effizient über mehrere Dimensionen hinweg abfragen können: nach Turnier und Region für die Gruppenverwaltung, nach Spielern für den Spielverlauf und nach Datum für die Planung.

## Datenmodell
<a name="GSI.DesignPattern.MultiAttributeKeys.DataModel"></a>

In dieser exemplarischen Vorgehensweise unterstützt das System zur Nachverfolgung von Turnierspielen drei primäre Zugriffsmuster, für die jeweils eine andere Schlüsselstruktur erforderlich ist:

**Zugriffsmuster 1:** Suchen Sie ein bestimmtes Spiel anhand seiner eindeutigen ID
+ **Lösung:** Basistabelle mit einem `matchId` Partitionsschlüssel

**Zugriffsmuster 2:** Alle Spiele für ein bestimmtes Turnier und eine bestimmte Region abfragen und optional nach Runde, Bracket oder Spiel filtern
+ **Lösung:** Globaler sekundärer Index mit Partitionsschlüssel (`tournamentId`\$1`region`) mit mehreren Attributen und Sortierschlüssel mit mehreren Attributen (`round`\$1 \$1`bracket`) `matchId`
+ **Beispielabfragen:** „Alle WINTER2 024 Spiele in der Region NA-OST“ oder „Alle HALBFINALSPIELE in OBERER Klammer für 024/NA-EAST“ WINTER2

**Zugriffsmuster 3:** Fragen Sie den Spielverlauf eines Spielers ab und filtern Sie optional nach Zeitraum oder Turnierrunde
+ **Lösung:** Globaler sekundärer Index mit einem einzigen Partitionsschlüssel (`player1Id`) und einem Sortierschlüssel mit mehreren Attributen (`matchDate`\$1`round`)
+ **Beispielabfragen:** „Alle Spiele für Spieler 101“ oder „Spiele von Spieler 101 im Januar 2024“

Der entscheidende Unterschied zwischen traditionellen Ansätzen und Methoden mit mehreren Attributen wird deutlich, wenn man sich die Artikelstruktur ansieht:

**Traditioneller Ansatz für globale Sekundärindizes (verkettete Schlüssel):**

```
// Manual concatenation required for GSI keys
const item = {
    matchId: 'match-001',                                          // Base table PK
    tournamentId: 'WINTER2024',
    region: 'NA-EAST',
    round: 'SEMIFINALS',
    bracket: 'UPPER',
    player1Id: '101',
    // Synthetic keys needed for GSI
    GSI_PK: `TOURNAMENT#${tournamentId}#REGION#${region}`,       // Must concatenate
    GSI_SK: `${round}#${bracket}#${matchId}`,                    // Must concatenate
    // ... other attributes
};
```

**Globaler Sekundärindex-Ansatz mit mehreren Attributen (systemeigene Schlüssel):**

```
// Use existing attributes directly - no concatenation needed
const item = {
    matchId: 'match-001',                                          // Base table PK
    tournamentId: 'WINTER2024',
    region: 'NA-EAST',
    round: 'SEMIFINALS',
    bracket: 'UPPER',
    player1Id: '101',
    matchDate: '2024-01-18',
    // No synthetic keys needed - GSI uses existing attributes directly
    // ... other attributes
};
```

Bei Schlüsseln mit mehreren Attributen schreiben Sie Elemente einmal mit natürlichen Domänenattributen. DynamoDB indexiert sie automatisch über mehrere, GSIs ohne dass synthetische verkettete Schlüssel erforderlich sind.

**Schema der Basistabelle:**
+ Partitionsschlüssel: `matchId` (1 Attribut)

**Globales sekundäres Indexschema (TournamentRegionIndex mit Schlüsseln mit mehreren Attributen):**
+ Partitionsschlüssel:`tournamentId`, `region` (2 Attribute)
+ Sortierschlüssel:`round`,`bracket`, `matchId` (3 Attribute)

**Globales sekundäres Indexschema (PlayerMatchHistoryIndex mit Schlüsseln mit mehreren Attributen):**
+ Partitionsschlüssel: `player1Id` (1 Attribut)
+ Sortierschlüssel:`matchDate`, `round` (2 Attribute)

### Basistabelle: TournamentMatches
<a name="GSI.DesignPattern.MultiAttributeKeys.BaseTable"></a>


| MatchID (PK) | Turnier-ID | Region | round | Halterung | Spieler1-ID | Spieler2-ID | Datum des Spiels | Gewinner | Bewertung | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| Match-001 | WINTER2024 | IM OSTEN | ENDSPIELE | MEISTERSCHAFT | 101 | 103 | 20.01.2024-01 | 101 | 3-1 | 
| Spiel-002 | WINTER2024 | IM OSTEN | HALBFINALE | UPPER | 101 | 105 | 2024-01-18 | 101 | 3-2 | 
| Spiel-003 | WINTER2024 | IM OSTEN | HALBFINALE | UPPER | 103 | 107 | 2024-01-18 | 103 | 3-0 | 
| Spiel-004 | WINTER2024 | IM OSTEN | VIERTELFINALE | UPPER | 101 | 109 | 2024-01-15 | 101 | 3-1 | 
| Spiel-005 | WINTER2024 | IM WESTEN | ENDSPIELE | MEISTERSCHAFT | 102 | 104 | 20.01.2024-01 | 102 | 3-2 | 
| Spiel-006 | WINTER2024 | IM WESTEN | HALBFINALE | UPPER | 102 | 106 | 2024-01-18 | 102 | 3-1 | 
| Spiel 007 | SPRING2024 | IM OSTEN | VIERTELFINALE | UPPER | 101 | 108 | 2024-03-15 | 101 | 3-0 | 
| Spiel-008 | SPRING2024 | IM OSTEN | VIERTELFINALE | LOWER | 103 | 110 | 2024-03-15 | 103 | 3-2 | 

### GSI: TournamentRegionIndex (Schlüssel mit mehreren Attributen)
<a name="GSI.DesignPattern.MultiAttributeKeys.TournamentRegionIndexTable"></a>


| Turnier-ID (PK) | Region (PK) | rund (SK) | Klammer (SK) | Spiel-ID (SK) | Spieler1-ID | Spieler2-ID | Datum des Spiels | Gewinner | Bewertung | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| WINTER2024 | IM OSTEN | ENDSPIELE | MEISTERSCHAFT | Spiel-001 | 101 | 103 | 20.01.2024 | 101 | 3-1 | 
| WINTER2024 | IM OSTEN | VIERTELFINALE | UPPER | Spiel-004 | 101 | 109 | 15.01.2024 | 101 | 3-1 | 
| WINTER2024 | IM OSTEN | HALBFINALE | UPPER | Spiel-002 | 101 | 105 | 18.01.2024 | 101 | 3-2 | 
| WINTER2024 | IM OSTEN | HALBFINALE | UPPER | Spiel-003 | 103 | 107 | 18.01.2024 | 103 | 3-0 | 
| WINTER2024 | IM WESTEN | ENDSPIELE | MEISTERSCHAFT | Spiel-005 | 102 | 104 | 20.01.2024 | 102 | 3-2 | 
| WINTER2024 | IM WESTEN | HALBFINALE | UPPER | Spiel-006 | 102 | 106 | 18.01.2024 | 102 | 3-1 | 
| SPRING2024 | IM OSTEN | VIERTELFINALE | LOWER | Spiel-008 | 103 | 110 | 15.03.2024 | 103 | 3-2 | 
| SPRING2024 | IM OSTEN | VIERTELFINALE | UPPER | Spiel-007 | 101 | 108 | 15.03.2024 | 101 | 3-0 | 

### GSI: PlayerMatchHistoryIndex (Schlüssel mit mehreren Attributen)
<a name="GSI.DesignPattern.MultiAttributeKeys.PlayerMatchHistoryIndexTable"></a>


| Spieler1-ID (PK) | Spieldatum (SK) | Runde (SK) | ID des Turniers | Region | Halterung | ID abgleichen | Spieler2-ID | Gewinner | Bewertung | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| 101 | 2024-01-15 | VIERTELFINALE | WINTER2024 | IM OSTEN | UPPER | Spiel-004 | 109 | 101 | 3-1 | 
| 101 | 18.01.2024 | HALBFINALE | WINTER2024 | IM OSTEN | UPPER | Spiel-002 | 105 | 101 | 3-2 | 
| 101 | 20.01.2024 | ENDSPIELE | WINTER2024 | IM OSTEN | MEISTERSCHAFT | Spiel-001 | 103 | 101 | 3-1 | 
| 101 | 15.03.2024 | VIERTELFINALE | SPRING2024 | IM OSTEN | UPPER | Spiel 007 | 108 | 101 | 3-0 | 
| 102 | 18.01.2024 | HALBFINALE | WINTER2024 | IM WESTEN | UPPER | Spiel-006 | 106 | 102 | 3-1 | 
| 102 | 20.01.2024 | ENDSPIELE | WINTER2024 | IM WESTEN | MEISTERSCHAFT | Spiel-005 | 104 | 102 | 3-2 | 
| 103 | 18.01.2024 | HALBFINALE | WINTER2024 | IM OSTEN | UPPER | Spiel-003 | 107 | 103 | 3-0 | 
| 103 | 2024-03-15 | VIERTELFINALE | SPRING2024 | IM OSTEN | LOWER | Spiel-008 | 110 | 103 | 3-2 | 

## Voraussetzungen
<a name="GSI.DesignPattern.MultiAttributeKeys.Prerequisites"></a>

Stellen Sie vor Beginn sicher, dass Sie über Folgendes verfügen:

### Konto und Berechtigungen
<a name="GSI.DesignPattern.MultiAttributeKeys.Prerequisites.AWSAccount"></a>
+ Ein aktives AWS Konto ([erstellen Sie hier bei Bedarf eines](https://aws.amazon.com/free/))
+ IAM-Berechtigungen für DynamoDB-Operationen:
  + `dynamodb:CreateTable`
  + `dynamodb:DeleteTable`
  + `dynamodb:DescribeTable`
  + `dynamodb:PutItem`
  + `dynamodb:Query`
  + `dynamodb:BatchWriteItem`

**Anmerkung**  
**Sicherheitshinweis:** Erstellen Sie für Produktionszwecke eine benutzerdefinierte IAM-Richtlinie mit nur den Berechtigungen, die Sie benötigen. Für dieses Tutorial können Sie die AWS verwaltete Richtlinie `AmazonDynamoDBFullAccessV2` verwenden.

### Entwicklungsumgebung
<a name="GSI.DesignPattern.MultiAttributeKeys.Prerequisites.DevEnvironment"></a>
+ Node.js ist auf Ihrem Computer installiert
+ AWS Anmeldeinformationen, die mit einer der folgenden Methoden konfiguriert wurden:

**Option 1: AWS CLI**

```
aws configure
```

**Option 2: Umgebungsvariablen**

```
export AWS_ACCESS_KEY_ID=your_access_key_here
export AWS_SECRET_ACCESS_KEY=your_secret_key_here
export AWS_DEFAULT_REGION=us-east-1
```

### Installieren erforderlicher Pakete
<a name="GSI.DesignPattern.MultiAttributeKeys.Prerequisites.InstallPackages"></a>

```
npm install @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
```

## Implementierung
<a name="GSI.DesignPattern.MultiAttributeKeys.Implementation"></a>

### Schritt 1: Erstellen Sie eine Tabelle mit Schlüsseln GSIs mit mehreren Attributen
<a name="GSI.DesignPattern.MultiAttributeKeys.CreateTable"></a>

Erstellen Sie eine Tabelle mit einer einfachen Basisschlüsselstruktur GSIs , die Schlüssel mit mehreren Attributen verwendet.

#### Codebeispiel
<a name="w2aac19c13c45c23b9c11b3b5b1"></a>

```
import { DynamoDBClient, CreateTableCommand } from "@aws-sdk/client-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });

const response = await client.send(new CreateTableCommand({
    TableName: 'TournamentMatches',
    
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'matchId', KeyType: 'HASH' }              // Simple PK
    ],
    
    AttributeDefinitions: [
        { AttributeName: 'matchId', AttributeType: 'S' },
        { AttributeName: 'tournamentId', AttributeType: 'S' },
        { AttributeName: 'region', AttributeType: 'S' },
        { AttributeName: 'round', AttributeType: 'S' },
        { AttributeName: 'bracket', AttributeType: 'S' },
        { AttributeName: 'player1Id', AttributeType: 'S' },
        { AttributeName: 'matchDate', AttributeType: 'S' }
    ],
    
    // GSIs with multi-attribute keys
    GlobalSecondaryIndexes: [
        {
            IndexName: 'TournamentRegionIndex',
            KeySchema: [
                { AttributeName: 'tournamentId', KeyType: 'HASH' },    // GSI PK attribute 1
                { AttributeName: 'region', KeyType: 'HASH' },          // GSI PK attribute 2
                { AttributeName: 'round', KeyType: 'RANGE' },          // GSI SK attribute 1
                { AttributeName: 'bracket', KeyType: 'RANGE' },        // GSI SK attribute 2
                { AttributeName: 'matchId', KeyType: 'RANGE' }         // GSI SK attribute 3
            ],
            Projection: { ProjectionType: 'ALL' }
        },
        {
            IndexName: 'PlayerMatchHistoryIndex',
            KeySchema: [
                { AttributeName: 'player1Id', KeyType: 'HASH' },       // GSI PK
                { AttributeName: 'matchDate', KeyType: 'RANGE' },      // GSI SK attribute 1
                { AttributeName: 'round', KeyType: 'RANGE' }           // GSI SK attribute 2
            ],
            Projection: { ProjectionType: 'ALL' }
        }
    ],
    
    BillingMode: 'PAY_PER_REQUEST'
}));

console.log("Table with multi-attribute GSI keys created successfully");
```

**Wichtige Designentscheidungen:**

**Basistabelle:** Die Basistabelle verwendet einen einfachen `matchId` Partitionsschlüssel für direkte Suchabfragen, wodurch die Struktur der Basistabelle übersichtlich bleibt und gleichzeitig die komplexen Abfragemuster GSIs bereitgestellt werden.

**TournamentRegionIndex Globaler Sekundärindex:** Der `TournamentRegionIndex` globale Sekundärindex verwendet `tournamentId` \$1 `region` als Partitionsschlüssel mit mehreren Attributen, wodurch eine Isolierung der Turnierregion erreicht wird, bei der die Daten durch den Hash beider Attribute zusammen verteilt werden, was effiziente Abfragen innerhalb eines bestimmten Turnierregionskontextes ermöglicht. Der Sortierschlüssel mit mehreren Attributen (`round`\$1 `bracket` \$1`matchId`) ermöglicht eine hierarchische Sortierung, die Abfragen auf jeder Hierarchieebene mit natürlicher Reihenfolge von allgemein (rund) bis spezifisch (Match-ID) unterstützt.

**PlayerMatchHistoryIndex Globaler Sekundärindex:** Der `PlayerMatchHistoryIndex` globale sekundäre Index reorganisiert Daten nach Spielern, die `player1Id` als Partitionsschlüssel verwendet werden, und ermöglicht so turnierübergreifende Abfragen für einen bestimmten Spieler. Der Sortierschlüssel mit mehreren Attributen (`matchDate`\$1`round`) bietet eine chronologische Reihenfolge mit der Möglichkeit, nach Datumsbereichen oder bestimmten Turnierrunden zu filtern.

### Schritt 2: Fügen Sie Daten mit systemeigenen Attributen ein
<a name="GSI.DesignPattern.MultiAttributeKeys.InsertData"></a>

Fügen Sie Turnierspieldaten mit natürlichen Attributen hinzu. Die GSI indexiert diese Attribute automatisch, ohne dass synthetische Schlüssel erforderlich sind.

#### Codebeispiel
<a name="w2aac19c13c45c23b9c11b5b5b1"></a>

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, PutCommand } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

// Tournament match data - no synthetic keys needed for GSIs
const matches = [
    // Winter 2024 Tournament, NA-EAST region
    {
        matchId: 'match-001',
        tournamentId: 'WINTER2024',
        region: 'NA-EAST',
        round: 'FINALS',
        bracket: 'CHAMPIONSHIP',
        player1Id: '101',
        player2Id: '103',
        matchDate: '2024-01-20',
        winner: '101',
        score: '3-1'
    },
    {
        matchId: 'match-002',
        tournamentId: 'WINTER2024',
        region: 'NA-EAST',
        round: 'SEMIFINALS',
        bracket: 'UPPER',
        player1Id: '101',
        player2Id: '105',
        matchDate: '2024-01-18',
        winner: '101',
        score: '3-2'
    },
    {
        matchId: 'match-003',
        tournamentId: 'WINTER2024',
        region: 'NA-EAST',
        round: 'SEMIFINALS',
        bracket: 'UPPER',
        player1Id: '103',
        player2Id: '107',
        matchDate: '2024-01-18',
        winner: '103',
        score: '3-0'
    },
    {
        matchId: 'match-004',
        tournamentId: 'WINTER2024',
        region: 'NA-EAST',
        round: 'QUARTERFINALS',
        bracket: 'UPPER',
        player1Id: '101',
        player2Id: '109',
        matchDate: '2024-01-15',
        winner: '101',
        score: '3-1'
    },
    
    // Winter 2024 Tournament, NA-WEST region
    {
        matchId: 'match-005',
        tournamentId: 'WINTER2024',
        region: 'NA-WEST',
        round: 'FINALS',
        bracket: 'CHAMPIONSHIP',
        player1Id: '102',
        player2Id: '104',
        matchDate: '2024-01-20',
        winner: '102',
        score: '3-2'
    },
    {
        matchId: 'match-006',
        tournamentId: 'WINTER2024',
        region: 'NA-WEST',
        round: 'SEMIFINALS',
        bracket: 'UPPER',
        player1Id: '102',
        player2Id: '106',
        matchDate: '2024-01-18',
        winner: '102',
        score: '3-1'
    },
    
    // Spring 2024 Tournament, NA-EAST region
    {
        matchId: 'match-007',
        tournamentId: 'SPRING2024',
        region: 'NA-EAST',
        round: 'QUARTERFINALS',
        bracket: 'UPPER',
        player1Id: '101',
        player2Id: '108',
        matchDate: '2024-03-15',
        winner: '101',
        score: '3-0'
    },
    {
        matchId: 'match-008',
        tournamentId: 'SPRING2024',
        region: 'NA-EAST',
        round: 'QUARTERFINALS',
        bracket: 'LOWER',
        player1Id: '103',
        player2Id: '110',
        matchDate: '2024-03-15',
        winner: '103',
        score: '3-2'
    }
];

// Insert all matches
for (const match of matches) {
    await docClient.send(new PutCommand({
        TableName: 'TournamentMatches',
        Item: match
    }));
    
    console.log(`Added: ${match.matchId} - ${match.tournamentId}/${match.region} - ${match.round} ${match.bracket}`);
}

console.log(`\nInserted ${matches.length} tournament matches`);
console.log("No synthetic keys created - GSIs use native attributes automatically");
```

**Datenstruktur erklärt:**

**Natürliche Verwendung von Attributen:** Jedes Attribut steht für ein echtes Turnierkonzept, bei dem keine Verkettung oder Analyse von Zeichenketten erforderlich ist, sodass eine direkte Zuordnung zum Domänenmodell möglich ist.

**Automatische Indizierung des globalen sekundären Indexes:** Die Indexierung von Elementen GSIs erfolgt automatisch unter Verwendung der vorhandenen Attribute (`tournamentId`,,,`region`, `matchId` für TournamentRegionIndex und `round``bracket`, `round` für PlayerMatchHistoryIndex), ohne dass `player1Id` synthetische `matchDate` verkettete Schlüssel erforderlich sind.

**Kein Backfilling erforderlich:** Wenn Sie einer vorhandenen Tabelle einen neuen globalen sekundären Index mit Schlüsseln mit mehreren Attributen hinzufügen, indexiert DynamoDB automatisch alle vorhandenen Elemente anhand ihrer natürlichen Attribute — es ist nicht erforderlich, Elemente mit synthetischen Schlüsseln zu aktualisieren.

### Schritt 3: Fragen Sie den globalen sekundären Index mit allen Partitionsschlüsselattributen ab TournamentRegionIndex
<a name="GSI.DesignPattern.MultiAttributeKeys.QueryAllPartitionKeys"></a>

In diesem Beispiel wird der TournamentRegionIndex globale sekundäre Index abgefragt, der über einen Partitionsschlüssel mit mehreren Attributen (`tournamentId`\$1`region`) verfügt. Alle Partitionsschlüsselattribute müssen in Abfragen mit Gleichheitsbedingungen angegeben werden. Es ist nicht möglich, Abfragen nur `tournamentId` allein oder Ungleichheitsoperatoren für Partitionsschlüsselattribute zu verwenden.

#### Codebeispiel
<a name="w2aac19c13c45c23b9c11b7b5b1"></a>

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

// Query GSI: All matches for WINTER2024 tournament in NA-EAST region
const response = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region',
    ExpressionAttributeNames: {
        '#region': 'region',  // 'region' is a reserved keyword
        '#tournament': 'tournament'
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST'
    }
}));

console.log(`Found ${response.Items.length} matches for WINTER2024/NA-EAST:\n`);
response.Items.forEach(match => {
    console.log(`  ${match.round} | ${match.bracket} | ${match.matchId}`);
    console.log(`    Players: ${match.player1Id} vs ${match.player2Id}`);
    console.log(`    Winner: ${match.winner}, Score: ${match.score}\n`);
});
```

**Erwartete Ausgabe:**

```
Found 4 matches for WINTER2024/NA-EAST:

  FINALS | CHAMPIONSHIP | match-001
    Players: 101 vs 103
    Winner: 101, Score: 3-1

  QUARTERFINALS | UPPER | match-004
    Players: 101 vs 109
    Winner: 101, Score: 3-1

  SEMIFINALS | UPPER | match-002
    Players: 101 vs 105
    Winner: 101, Score: 3-2

  SEMIFINALS | UPPER | match-003
    Players: 103 vs 107
    Winner: 103, Score: 3-0
```

**Ungültige Abfragen:**

```
// Missing region attribute
KeyConditionExpression: 'tournamentId = :tournament'

// Using inequality on partition key attribute
KeyConditionExpression: 'tournamentId = :tournament AND #region > :region'
```

**Leistung:** Partitionsschlüssel mit mehreren Attributen werden zusammen gehasht und bieten so dieselbe O (1) -Suchleistung wie Schlüssel mit einem Attribut.

### Schritt 4: Sortierschlüssel für den globalen sekundären Index abfragen left-to-right
<a name="GSI.DesignPattern.MultiAttributeKeys.QuerySortKeysLeftToRight"></a>

Sortierschlüsselattribute müssen left-to-right in der Reihenfolge abgefragt werden, in der sie im globalen sekundären Index definiert sind. Dieses Beispiel zeigt die Abfrage von TournamentRegionIndex auf verschiedenen Hierarchieebenen: das Filtern nur nach`round`, nach `round` \$1 `bracket` oder nach allen drei Sortierschlüsselattributen. Sie können keine Attribute in der Mitte überspringen. Sie können beispielsweise keine Abfragen nach und nach `round` dem Überspringen durchführen. `matchId` `bracket`

#### Codebeispiel
<a name="w2aac19c13c45c23b9c11b9b5b1"></a>

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

// Query 1: Filter by first sort key attribute (round)
console.log("Query 1: All SEMIFINALS matches");
const query1 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round = :round',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':round': 'SEMIFINALS'
    }
}));
console.log(`  Found ${query1.Items.length} matches\n`);

// Query 2: Filter by first two sort key attributes (round + bracket)
console.log("Query 2: SEMIFINALS UPPER bracket matches");
const query2 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round = :round AND bracket = :bracket',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':round': 'SEMIFINALS',
        ':bracket': 'UPPER'
    }
}));
console.log(`  Found ${query2.Items.length} matches\n`);

// Query 3: Filter by all three sort key attributes (round + bracket + matchId)
console.log("Query 3: Specific match in SEMIFINALS UPPER bracket");
const query3 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round = :round AND bracket = :bracket AND matchId = :matchId',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':round': 'SEMIFINALS',
        ':bracket': 'UPPER',
        ':matchId': 'match-002'
    }
}));
console.log(`  Found ${query3.Items.length} matches\n`);

// Query 4: INVALID - skipping round
console.log("Query 4: Attempting to skip first sort key attribute (WILL FAIL)");
try {
    const query4 = await docClient.send(new QueryCommand({
        TableName: 'TournamentMatches',
        IndexName: 'TournamentRegionIndex',
        KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND bracket = :bracket',
        ExpressionAttributeNames: {
            '#region': 'region'  // 'region' is a reserved keyword
        },
        ExpressionAttributeValues: {
            ':tournament': 'WINTER2024',
            ':region': 'NA-EAST',
            ':bracket': 'UPPER'
        }
    }));
} catch (error) {
    console.log(`  Error: ${error.message}`);
    console.log(`  Cannot skip sort key attributes - must query left-to-right\n`);
}
```

**Erwartete Ausgabe:**

```
Query 1: All SEMIFINALS matches
  Found 2 matches

Query 2: SEMIFINALS UPPER bracket matches
  Found 2 matches

Query 3: Specific match in SEMIFINALS UPPER bracket
  Found 1 matches

Query 4: Attempting to skip first sort key attribute (WILL FAIL)
  Error: Query key condition not supported
  Cannot skip sort key attributes - must query left-to-right
```

**Left-to-right Abfrageregeln:** Sie müssen Attribute in der Reihenfolge von links nach rechts abfragen, ohne irgendwelche zu überspringen.

**Gültige Muster:**
+ Nur erstes Attribut: `round = 'SEMIFINALS'`
+ Die ersten beiden Attribute: `round = 'SEMIFINALS' AND bracket = 'UPPER'`
+ Alle drei Attribute: `round = 'SEMIFINALS' AND bracket = 'UPPER' AND matchId = 'match-002'`

**Ungültige Muster:**
+ Das erste Attribut wird übersprungen: `bracket = 'UPPER'` (überspringt die Runde)
+ Die Abfrage ist nicht in der richtigen Reihenfolge: `matchId = 'match-002' AND round = 'SEMIFINALS'`
+ Lücken hinterlassen: `round = 'SEMIFINALS' AND matchId = 'match-002'` (überspringt die Klammer)

**Anmerkung**  
**Design-Tipp:** Sortieren Sie die Schlüsselattribute von den allgemeinsten bis hin zu den spezifischsten, um die Flexibilität bei der Abfrage zu maximieren.

### Schritt 5: Verwenden Sie Ungleichheitsbedingungen für Sortierschlüssel im globalen sekundären Index
<a name="GSI.DesignPattern.MultiAttributeKeys.InequalityConditions"></a>

Ungleichheitsbedingungen müssen die letzte Bedingung in Ihrer Abfrage sein. Dieses Beispiel zeigt die Verwendung von Vergleichsoperatoren (`>=`,`BETWEEN`) und Präfixabgleich (`begins_with()`) für Sortierschlüsselattribute. Sobald Sie einen Ungleichheitsoperator verwendet haben, können Sie danach keine weiteren Sortierschlüsselbedingungen hinzufügen — die Ungleichheit muss die letzte Bedingung in Ihrem Schlüsselbedingungsausdruck sein.

#### Codebeispiel
<a name="w2aac19c13c45c23b9c11c11b5b1"></a>

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

// Query 1: Round comparison (inequality on first sort key attribute)
console.log("Query 1: Matches from QUARTERFINALS onwards");
const query1 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round >= :round',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':round': 'QUARTERFINALS'
    }
}));
console.log(`  Found ${query1.Items.length} matches\n`);

// Query 2: Round range with BETWEEN
console.log("Query 2: Matches between QUARTERFINALS and SEMIFINALS");
const query2 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round BETWEEN :start AND :end',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':start': 'QUARTERFINALS',
        ':end': 'SEMIFINALS'
    }
}));
console.log(`  Found ${query2.Items.length} matches\n`);

// Query 3: Prefix matching with begins_with (treated as inequality)
console.log("Query 3: Matches in brackets starting with 'U'");
const query3 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round = :round AND begins_with(bracket, :prefix)',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':round': 'SEMIFINALS',
        ':prefix': 'U'
    }
}));
console.log(`  Found ${query3.Items.length} matches\n`);

// Query 4: INVALID - condition after inequality
console.log("Query 4: Attempting condition after inequality (WILL FAIL)");
try {
    const query4 = await docClient.send(new QueryCommand({
        TableName: 'TournamentMatches',
        IndexName: 'TournamentRegionIndex',
        KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round > :round AND bracket = :bracket',
        ExpressionAttributeNames: {
            '#region': 'region'  // 'region' is a reserved keyword
        },
        ExpressionAttributeValues: {
            ':tournament': 'WINTER2024',
            ':region': 'NA-EAST',
            ':round': 'QUARTERFINALS',
            ':bracket': 'UPPER'
        }
    }));
} catch (error) {
    console.log(`  Error: ${error.message}`);
    console.log(`  Cannot add conditions after inequality - it must be last\n`);
}
```

**Regeln für Ungleichheitsoperatoren:** Sie können Vergleichsoperatoren (`>`, `>=``<`,`<=`) `BETWEEN` für Bereichsabfragen und `begins_with()` für den Präfixabgleich verwenden. Die Ungleichheit muss die letzte Bedingung in Ihrer Abfrage sein.

**Gültige Muster:**
+ Gleichheitsbedingungen, gefolgt von Ungleichheit: `round = 'SEMIFINALS' AND bracket = 'UPPER' AND matchId > 'match-001'`
+ Ungleichheit beim ersten Merkmal: `round BETWEEN 'QUARTERFINALS' AND 'SEMIFINALS'`
+ Präfixübereinstimmung als letzte Bedingung: `round = 'SEMIFINALS' AND begins_with(bracket, 'U')`

**Ungültige Muster:**
+ Hinzufügen von Bedingungen nach einer Ungleichung: `round > 'QUARTERFINALS' AND bracket = 'UPPER'`
+ Verwendung mehrerer Ungleichungen: `round > 'QUARTERFINALS' AND bracket > 'L'`

**Wichtig**  
`begins_with()`wird als Ungleichheitsbedingung behandelt, sodass ihr keine weiteren Sortierschlüsselbedingungen folgen können.

### Schritt 6: Fragen Sie den PlayerMatchHistoryIndex globalen sekundären Index mit einem Sortierschlüssel mit mehreren Attributen ab
<a name="GSI.DesignPattern.MultiAttributeKeys.QueryPlayerHistory"></a>

In diesem Beispiel wird abgefragt PlayerMatchHistoryIndex , welcher einen einzelnen Partitionsschlüssel (`player1Id`) und einen Sortierschlüssel mit mehreren Attributen (`matchDate`\$1`round`) hat. Dies ermöglicht eine turnierübergreifende Analyse, indem alle Spiele eines bestimmten Spielers abgefragt werden, ohne das Turnier IDs zu kennen. In der Basistabelle wären für jede Kombination aus Turnier und Region separate Abfragen erforderlich.

#### Codebeispiel
<a name="w2aac19c13c45c23b9c11c13b5b1"></a>

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

// Query 1: All matches for Player 101 across all tournaments
console.log("Query 1: All matches for Player 101");
const query1 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'PlayerMatchHistoryIndex',
    KeyConditionExpression: 'player1Id = :player',
    ExpressionAttributeValues: {
        ':player': '101'
    }
}));

console.log(`  Found ${query1.Items.length} matches for Player 101:`);
query1.Items.forEach(match => {
    console.log(`    ${match.tournamentId}/${match.region} - ${match.matchDate} - ${match.round}`);
});
console.log();

// Query 2: Player 101 matches on specific date
console.log("Query 2: Player 101 matches on 2024-01-18");
const query2 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'PlayerMatchHistoryIndex',
    KeyConditionExpression: 'player1Id = :player AND matchDate = :date',
    ExpressionAttributeValues: {
        ':player': '101',
        ':date': '2024-01-18'
    }
}));

console.log(`  Found ${query2.Items.length} matches\n`);

// Query 3: Player 101 SEMIFINALS matches on specific date
console.log("Query 3: Player 101 SEMIFINALS matches on 2024-01-18");
const query3 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'PlayerMatchHistoryIndex',
    KeyConditionExpression: 'player1Id = :player AND matchDate = :date AND round = :round',
    ExpressionAttributeValues: {
        ':player': '101',
        ':date': '2024-01-18',
        ':round': 'SEMIFINALS'
    }
}));

console.log(`  Found ${query3.Items.length} matches\n`);

// Query 4: Player 101 matches in date range
console.log("Query 4: Player 101 matches in January 2024");
const query4 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'PlayerMatchHistoryIndex',
    KeyConditionExpression: 'player1Id = :player AND matchDate BETWEEN :start AND :end',
    ExpressionAttributeValues: {
        ':player': '101',
        ':start': '2024-01-01',
        ':end': '2024-01-31'
    }
}));

console.log(`  Found ${query4.Items.length} matches\n`);
```

## Variationen des Musters
<a name="GSI.DesignPattern.MultiAttributeKeys.PatternVariations"></a>

### Zeitreihendaten mit Schlüsseln mit mehreren Attributen
<a name="GSI.DesignPattern.MultiAttributeKeys.TimeSeries"></a>

Optimieren Sie für Zeitreihenabfragen mit hierarchischen Zeitattributen

#### Codebeispiel
<a name="w2aac19c13c45c23b9c13b3b5b1"></a>

```
{
    TableName: 'IoTReadings',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'readingId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'readingId', AttributeType: 'S' },
        { AttributeName: 'deviceId', AttributeType: 'S' },
        { AttributeName: 'locationId', AttributeType: 'S' },
        { AttributeName: 'year', AttributeType: 'S' },
        { AttributeName: 'month', AttributeType: 'S' },
        { AttributeName: 'day', AttributeType: 'S' },
        { AttributeName: 'timestamp', AttributeType: 'S' }
    ],
    // GSI with multi-attribute keys for time-series queries
    GlobalSecondaryIndexes: [{
        IndexName: 'DeviceLocationTimeIndex',
        KeySchema: [
            { AttributeName: 'deviceId', KeyType: 'HASH' },
            { AttributeName: 'locationId', KeyType: 'HASH' },
            { AttributeName: 'year', KeyType: 'RANGE' },
            { AttributeName: 'month', KeyType: 'RANGE' },
            { AttributeName: 'day', KeyType: 'RANGE' },
            { AttributeName: 'timestamp', KeyType: 'RANGE' }
        ],
        Projection: { ProjectionType: 'ALL' }
    }],
    BillingMode: 'PAY_PER_REQUEST'
}

// Query patterns enabled via GSI:
// - All readings for device in location
// - Readings for specific year
// - Readings for specific month in year
// - Readings for specific day
// - Readings in time range
```

**Vorteile: Die** natürliche Zeithierarchie (Jahr → Monat → Tag → Zeitstempel) ermöglicht effiziente Abfragen mit beliebiger Granularität, ohne dass das Datum analysiert oder manipuliert werden muss. Der globale Sekundärindex indexiert automatisch alle Messwerte anhand ihrer natürlichen Zeitattribute.

### E-Commerce-Bestellungen mit Schlüsseln mit mehreren Attributen
<a name="GSI.DesignPattern.MultiAttributeKeys.ECommerce"></a>

Verfolgen Sie Bestellungen mit mehreren Dimensionen

#### Codebeispiel
<a name="w2aac19c13c45c23b9c13b5b5b1"></a>

```
{
    TableName: 'Orders',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'orderId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'orderId', AttributeType: 'S' },
        { AttributeName: 'sellerId', AttributeType: 'S' },
        { AttributeName: 'region', AttributeType: 'S' },
        { AttributeName: 'orderDate', AttributeType: 'S' },
        { AttributeName: 'category', AttributeType: 'S' },
        { AttributeName: 'customerId', AttributeType: 'S' },
        { AttributeName: 'orderStatus', AttributeType: 'S' }
    ],
    GlobalSecondaryIndexes: [
        {
            IndexName: 'SellerRegionIndex',
            KeySchema: [
                { AttributeName: 'sellerId', KeyType: 'HASH' },
                { AttributeName: 'region', KeyType: 'HASH' },
                { AttributeName: 'orderDate', KeyType: 'RANGE' },
                { AttributeName: 'category', KeyType: 'RANGE' },
                { AttributeName: 'orderId', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        },
        {
            IndexName: 'CustomerOrdersIndex',
            KeySchema: [
                { AttributeName: 'customerId', KeyType: 'HASH' },
                { AttributeName: 'orderDate', KeyType: 'RANGE' },
                { AttributeName: 'orderStatus', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        }
    ],
    BillingMode: 'PAY_PER_REQUEST'
}

// SellerRegionIndex GSI queries:
// - Orders by seller and region
// - Orders by seller, region, and date
// - Orders by seller, region, date, and category

// CustomerOrdersIndex GSI queries:
// - Customer's orders
// - Customer's orders by date
// - Customer's orders by date and status
```

### Hierarchische Organisationsdaten
<a name="GSI.DesignPattern.MultiAttributeKeys.Hierarchical"></a>

Modellieren Sie Organisationshierarchien

#### Codebeispiel
<a name="w2aac19c13c45c23b9c13b7b5b1"></a>

```
{
    TableName: 'Employees',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'employeeId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'employeeId', AttributeType: 'S' },
        { AttributeName: 'companyId', AttributeType: 'S' },
        { AttributeName: 'divisionId', AttributeType: 'S' },
        { AttributeName: 'departmentId', AttributeType: 'S' },
        { AttributeName: 'teamId', AttributeType: 'S' },
        { AttributeName: 'skillCategory', AttributeType: 'S' },
        { AttributeName: 'skillLevel', AttributeType: 'S' },
        { AttributeName: 'yearsExperience', AttributeType: 'N' }
    ],
    GlobalSecondaryIndexes: [
        {
            IndexName: 'OrganizationIndex',
            KeySchema: [
                { AttributeName: 'companyId', KeyType: 'HASH' },
                { AttributeName: 'divisionId', KeyType: 'HASH' },
                { AttributeName: 'departmentId', KeyType: 'RANGE' },
                { AttributeName: 'teamId', KeyType: 'RANGE' },
                { AttributeName: 'employeeId', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        },
        {
            IndexName: 'SkillsIndex',
            KeySchema: [
                { AttributeName: 'skillCategory', KeyType: 'HASH' },
                { AttributeName: 'skillLevel', KeyType: 'RANGE' },
                { AttributeName: 'yearsExperience', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'INCLUDE', NonKeyAttributes: ['employeeId', 'name'] }
        }
    ],
    BillingMode: 'PAY_PER_REQUEST'
}

// OrganizationIndex GSI query patterns:
// - All employees in company/division
// - Employees in specific department
// - Employees in specific team

// SkillsIndex GSI query patterns:
// - Employees by skill and experience level
```

### Wenige Schlüssel mit mehreren Attributen
<a name="GSI.DesignPattern.MultiAttributeKeys.Sparse"></a>

Kombinieren Sie Schlüssel mit mehreren Attributen, um einen globalen Index mit geringer Dichte zu erstellen

#### Codebeispiel
<a name="w2aac19c13c45c23b9c13b9b5b1"></a>

```
{
    TableName: 'Products',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'productId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'productId', AttributeType: 'S' },
        { AttributeName: 'categoryId', AttributeType: 'S' },
        { AttributeName: 'subcategoryId', AttributeType: 'S' },
        { AttributeName: 'averageRating', AttributeType: 'N' },
        { AttributeName: 'reviewCount', AttributeType: 'N' }
    ],
    GlobalSecondaryIndexes: [
        {
            IndexName: 'CategoryIndex',
            KeySchema: [
                { AttributeName: 'categoryId', KeyType: 'HASH' },
                { AttributeName: 'subcategoryId', KeyType: 'HASH' },
                { AttributeName: 'productId', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        },
        {
            IndexName: 'ReviewedProductsIndex',
            KeySchema: [
                { AttributeName: 'categoryId', KeyType: 'HASH' },
                { AttributeName: 'averageRating', KeyType: 'RANGE' },  // Optional attribute
                { AttributeName: 'reviewCount', KeyType: 'RANGE' }     // Optional attribute
            ],
            Projection: { ProjectionType: 'ALL' }
        }
    ],
    BillingMode: 'PAY_PER_REQUEST'
}

// Only products with reviews appear in ReviewedProductsIndex GSI
// Automatic filtering without application logic
// Multi-attribute sort key enables rating and count queries
```

### SaaS für mehrere Mandanten
<a name="GSI.DesignPattern.MultiAttributeKeys.SaaS"></a>

Mehrinstanzenfähige SaaS-Plattform mit Kundenisolierung

#### Codebeispiel
<a name="w2aac19c13c45c23b9c13c11b5b1"></a>

```
// Table design
{
    TableName: 'SaasData',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'resourceId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'resourceId', AttributeType: 'S' },
        { AttributeName: 'tenantId', AttributeType: 'S' },
        { AttributeName: 'customerId', AttributeType: 'S' },
        { AttributeName: 'resourceType', AttributeType: 'S' }
    ],
    // GSI with multi-attribute keys for tenant-customer isolation
    GlobalSecondaryIndexes: [{
        IndexName: 'TenantCustomerIndex',
        KeySchema: [
            { AttributeName: 'tenantId', KeyType: 'HASH' },
            { AttributeName: 'customerId', KeyType: 'HASH' },
            { AttributeName: 'resourceType', KeyType: 'RANGE' },
            { AttributeName: 'resourceId', KeyType: 'RANGE' }
        ],
        Projection: { ProjectionType: 'ALL' }
    }],
    BillingMode: 'PAY_PER_REQUEST'
}

// Query GSI: All resources for tenant T001, customer C001
const resources = await docClient.send(new QueryCommand({
    TableName: 'SaasData',
    IndexName: 'TenantCustomerIndex',
    KeyConditionExpression: 'tenantId = :tenant AND customerId = :customer',
    ExpressionAttributeValues: {
        ':tenant': 'T001',
        ':customer': 'C001'
    }
}));

// Query GSI: Specific resource type for tenant/customer
const documents = await docClient.send(new QueryCommand({
    TableName: 'SaasData',
    IndexName: 'TenantCustomerIndex',
    KeyConditionExpression: 'tenantId = :tenant AND customerId = :customer AND resourceType = :type',
    ExpressionAttributeValues: {
        ':tenant': 'T001',
        ':customer': 'C001',
        ':type': 'document'
    }
}));
```

**Vorteile:** Effiziente Abfragen im Mandanten-Kunden-Kontext und natürliche Datenorganisation.

### Finanztransaktionen
<a name="GSI.DesignPattern.MultiAttributeKeys.Financial"></a>

Banksystem zur Verfolgung von Kontotransaktionen mithilfe von GSIs

#### Codebeispiel
<a name="w2aac19c13c45c23b9c13c13b5b1"></a>

```
// Table design
{
    TableName: 'BankTransactions',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'transactionId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'transactionId', AttributeType: 'S' },
        { AttributeName: 'accountId', AttributeType: 'S' },
        { AttributeName: 'year', AttributeType: 'S' },
        { AttributeName: 'month', AttributeType: 'S' },
        { AttributeName: 'day', AttributeType: 'S' },
        { AttributeName: 'transactionType', AttributeType: 'S' }
    ],
    GlobalSecondaryIndexes: [
        {
            IndexName: 'AccountTimeIndex',
            KeySchema: [
                { AttributeName: 'accountId', KeyType: 'HASH' },
                { AttributeName: 'year', KeyType: 'RANGE' },
                { AttributeName: 'month', KeyType: 'RANGE' },
                { AttributeName: 'day', KeyType: 'RANGE' },
                { AttributeName: 'transactionId', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        },
        {
            IndexName: 'TransactionTypeIndex',
            KeySchema: [
                { AttributeName: 'accountId', KeyType: 'HASH' },
                { AttributeName: 'transactionType', KeyType: 'RANGE' },
                { AttributeName: 'year', KeyType: 'RANGE' },
                { AttributeName: 'month', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        }
    ],
    BillingMode: 'PAY_PER_REQUEST'
}

// Query AccountTimeIndex GSI: All transactions for account in 2023
const yearTransactions = await docClient.send(new QueryCommand({
    TableName: 'BankTransactions',
    IndexName: 'AccountTimeIndex',
    KeyConditionExpression: 'accountId = :account AND #year = :year',
    ExpressionAttributeNames: { '#year': 'year' },
    ExpressionAttributeValues: {
        ':account': 'ACC-12345',
        ':year': '2023'
    }
}));

// Query AccountTimeIndex GSI: Transactions in specific month
const monthTransactions = await docClient.send(new QueryCommand({
    TableName: 'BankTransactions',
    IndexName: 'AccountTimeIndex',
    KeyConditionExpression: 'accountId = :account AND #year = :year AND #month = :month',
    ExpressionAttributeNames: { '#year': 'year', '#month': 'month' },
    ExpressionAttributeValues: {
        ':account': 'ACC-12345',
        ':year': '2023',
        ':month': '11'
    }
}));

// Query TransactionTypeIndex GSI: Deposits in 2023
const deposits = await docClient.send(new QueryCommand({
    TableName: 'BankTransactions',
    IndexName: 'TransactionTypeIndex',
    KeyConditionExpression: 'accountId = :account AND transactionType = :type AND #year = :year',
    ExpressionAttributeNames: { '#year': 'year' },
    ExpressionAttributeValues: {
        ':account': 'ACC-12345',
        ':type': 'deposit',
        ':year': '2023'
    }
}));
```

## Vollständiges Beispiel
<a name="GSI.DesignPattern.MultiAttributeKeys.CompleteExample"></a>

Das folgende Beispiel zeigt Schlüssel mit mehreren Attributen von der Einrichtung bis zur Bereinigung:

### Codebeispiel
<a name="w2aac19c13c45c23b9c15b5b1"></a>

```
import { 
    DynamoDBClient, 
    CreateTableCommand, 
    DeleteTableCommand, 
    waitUntilTableExists 
} from "@aws-sdk/client-dynamodb";
import { 
    DynamoDBDocumentClient, 
    PutCommand, 
    QueryCommand 
} from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

async function multiAttributeKeysDemo() {
    console.log("Starting Multi-Attribute GSI Keys Demo\n");
    
    // Step 1: Create table with GSIs using multi-attribute keys
    console.log("1. Creating table with multi-attribute GSI keys...");
    await client.send(new CreateTableCommand({
        TableName: 'TournamentMatches',
        KeySchema: [
            { AttributeName: 'matchId', KeyType: 'HASH' }
        ],
        AttributeDefinitions: [
            { AttributeName: 'matchId', AttributeType: 'S' },
            { AttributeName: 'tournamentId', AttributeType: 'S' },
            { AttributeName: 'region', AttributeType: 'S' },
            { AttributeName: 'round', AttributeType: 'S' },
            { AttributeName: 'bracket', AttributeType: 'S' },
            { AttributeName: 'player1Id', AttributeType: 'S' },
            { AttributeName: 'matchDate', AttributeType: 'S' }
        ],
        GlobalSecondaryIndexes: [
            {
                IndexName: 'TournamentRegionIndex',
                KeySchema: [
                    { AttributeName: 'tournamentId', KeyType: 'HASH' },
                    { AttributeName: 'region', KeyType: 'HASH' },
                    { AttributeName: 'round', KeyType: 'RANGE' },
                    { AttributeName: 'bracket', KeyType: 'RANGE' },
                    { AttributeName: 'matchId', KeyType: 'RANGE' }
                ],
                Projection: { ProjectionType: 'ALL' }
            },
            {
                IndexName: 'PlayerMatchHistoryIndex',
                KeySchema: [
                    { AttributeName: 'player1Id', KeyType: 'HASH' },
                    { AttributeName: 'matchDate', KeyType: 'RANGE' },
                    { AttributeName: 'round', KeyType: 'RANGE' }
                ],
                Projection: { ProjectionType: 'ALL' }
            }
        ],
        BillingMode: 'PAY_PER_REQUEST'
    }));
    
    await waitUntilTableExists({ client, maxWaitTime: 120 }, { TableName: 'TournamentMatches' });
    console.log("Table created\n");
    
    // Step 2: Insert tournament matches
    console.log("2. Inserting tournament matches...");
    const matches = [
        { matchId: 'match-001', tournamentId: 'WINTER2024', region: 'NA-EAST', round: 'FINALS', bracket: 'CHAMPIONSHIP', player1Id: '101', player2Id: '103', matchDate: '2024-01-20', winner: '101', score: '3-1' },
        { matchId: 'match-002', tournamentId: 'WINTER2024', region: 'NA-EAST', round: 'SEMIFINALS', bracket: 'UPPER', player1Id: '101', player2Id: '105', matchDate: '2024-01-18', winner: '101', score: '3-2' },
        { matchId: 'match-003', tournamentId: 'WINTER2024', region: 'NA-WEST', round: 'FINALS', bracket: 'CHAMPIONSHIP', player1Id: '102', player2Id: '104', matchDate: '2024-01-20', winner: '102', score: '3-2' },
        { matchId: 'match-004', tournamentId: 'SPRING2024', region: 'NA-EAST', round: 'QUARTERFINALS', bracket: 'UPPER', player1Id: '101', player2Id: '108', matchDate: '2024-03-15', winner: '101', score: '3-0' }
    ];
    
    for (const match of matches) {
        await docClient.send(new PutCommand({ TableName: 'TournamentMatches', Item: match }));
    }
    console.log(`Inserted ${matches.length} tournament matches\n`);
    
    // Step 3: Query GSI with multi-attribute partition key
    console.log("3. Query TournamentRegionIndex GSI: WINTER2024/NA-EAST matches");
    const gsiQuery1 = await docClient.send(new QueryCommand({
        TableName: 'TournamentMatches',
        IndexName: 'TournamentRegionIndex',
        KeyConditionExpression: 'tournamentId = :tournament AND #region = :region',
        ExpressionAttributeNames: { '#region': 'region' },
        ExpressionAttributeValues: { ':tournament': 'WINTER2024', ':region': 'NA-EAST' }
    }));
    
    console.log(`  Found ${gsiQuery1.Items.length} matches:`);
    gsiQuery1.Items.forEach(match => {
        console.log(`    ${match.round} - ${match.bracket} - ${match.winner} won`);
    });
    
    // Step 4: Query GSI with multi-attribute sort key
    console.log("\n4. Query PlayerMatchHistoryIndex GSI: All matches for Player 101");
    const gsiQuery2 = await docClient.send(new QueryCommand({
        TableName: 'TournamentMatches',
        IndexName: 'PlayerMatchHistoryIndex',
        KeyConditionExpression: 'player1Id = :player',
        ExpressionAttributeValues: { ':player': '101' }
    }));
    
    console.log(`  Found ${gsiQuery2.Items.length} matches for Player 101:`);
    gsiQuery2.Items.forEach(match => {
        console.log(`    ${match.tournamentId}/${match.region} - ${match.matchDate} - ${match.round}`);
    });
    
    console.log("\nDemo complete");
    console.log("No synthetic keys needed - GSIs use native attributes automatically");
}

async function cleanup() {
    console.log("Deleting table...");
    await client.send(new DeleteTableCommand({ TableName: 'TournamentMatches' }));
    console.log("Table deleted");
}

// Run demo
multiAttributeKeysDemo().catch(console.error);

// Uncomment to cleanup:
// cleanup().catch(console.error);
```

**Minimales Codegerüst**

### Codebeispiel
<a name="w2aac19c13c45c23b9c15b9b1"></a>

```
// 1. Create table with GSI using multi-attribute keys
await client.send(new CreateTableCommand({
    TableName: 'MyTable',
    KeySchema: [
        { AttributeName: 'id', KeyType: 'HASH' }        // Simple base table PK
    ],
    AttributeDefinitions: [
        { AttributeName: 'id', AttributeType: 'S' },
        { AttributeName: 'attr1', AttributeType: 'S' },
        { AttributeName: 'attr2', AttributeType: 'S' },
        { AttributeName: 'attr3', AttributeType: 'S' },
        { AttributeName: 'attr4', AttributeType: 'S' }
    ],
    GlobalSecondaryIndexes: [{
        IndexName: 'MyGSI',
        KeySchema: [
            { AttributeName: 'attr1', KeyType: 'HASH' },    // GSI PK attribute 1
            { AttributeName: 'attr2', KeyType: 'HASH' },    // GSI PK attribute 2
            { AttributeName: 'attr3', KeyType: 'RANGE' },   // GSI SK attribute 1
            { AttributeName: 'attr4', KeyType: 'RANGE' }    // GSI SK attribute 2
        ],
        Projection: { ProjectionType: 'ALL' }
    }],
    BillingMode: 'PAY_PER_REQUEST'
}));

// 2. Insert items with native attributes (no concatenation needed for GSI)
await docClient.send(new PutCommand({
    TableName: 'MyTable',
    Item: {
        id: 'item-001',
        attr1: 'value1',
        attr2: 'value2',
        attr3: 'value3',
        attr4: 'value4',
        // ... other attributes
    }
}));

// 3. Query GSI with all partition key attributes
await docClient.send(new QueryCommand({
    TableName: 'MyTable',
    IndexName: 'MyGSI',
    KeyConditionExpression: 'attr1 = :v1 AND attr2 = :v2',
    ExpressionAttributeValues: {
        ':v1': 'value1',
        ':v2': 'value2'
    }
}));

// 4. Query GSI with sort key attributes (left-to-right)
await docClient.send(new QueryCommand({
    TableName: 'MyTable',
    IndexName: 'MyGSI',
    KeyConditionExpression: 'attr1 = :v1 AND attr2 = :v2 AND attr3 = :v3',
    ExpressionAttributeValues: {
        ':v1': 'value1',
        ':v2': 'value2',
        ':v3': 'value3'
    }
}));

// Note: If any attribute name is a DynamoDB reserved keyword, use ExpressionAttributeNames:
// KeyConditionExpression: 'attr1 = :v1 AND #attr2 = :v2'
// ExpressionAttributeNames: { '#attr2': 'attr2' }
```

## Weitere Ressourcen
<a name="GSI.DesignPattern.MultiAttributeKeys.AdditionalResources"></a>
+ [Bewährte Methoden für DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/best-practices.html)
+ [Arbeiten mit Tabellen und Daten](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithTables.html)
+ [Globale Sekundärindizes](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html)
+ [Abfrage- und Scanvorgänge](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html)

# Verwalten globaler sekundärer Indizes in DynamoDB
<a name="GSI.OnlineOps"></a>

Dieser Abschnitt beschreibt, wie Sie globale sekundäre Indizes in Amazon DynamoDB erstellen, ändern und löschen.

**Topics**
+ [Erstellen einer Tabelle mit globalen sekundären Indizes](#GSI.Creating)
+ [Beschreiben globaler sekundären Indizes in einer Tabelle](#GSI.Describing)
+ [Hinzufügen eines globalen sekundären Index zu einer vorhandenen Tabelle](#GSI.OnlineOps.Creating)
+ [Löschen eines globalen sekundären Index](#GSI.OnlineOps.Deleting)
+ [Ändern eines globalen sekundären Indexes während der Erstellung](#GSI.OnlineOps.Creating.Modify)

## Erstellen einer Tabelle mit globalen sekundären Indizes
<a name="GSI.Creating"></a>

Um eine Tabelle mit einem oder mehreren globalen sekundären Indizes zu erstellen, verwenden Sie `CreateTable` mit dem Parameter `GlobalSecondaryIndexes`. Für maximale Abfrageflexibilität können Sie bis zu 20 globale sekundäre Indizes pro Tabelle (Standardkontingent) erstellen. 

Sie müssen ein Attribut angeben, das als Partitionsschlüssel des Index fungieren soll. Sie können optional ein weiteres Attribut für den Sortierschlüssel des Index festlegen. Diese beiden Schlüsselattribute müssen nicht mit den Schlüsselattributen in der Tabelle übereinstimmen. Beispielsweise sind in der *GameScores*Tabelle (siehe[Verwenden globaler sekundärer Indizes in DynamoDB](GSI.md)) `TopScore` weder Schlüsselattribute `TopScoreDateTime` noch Schlüsselattribute enthalten. Sie könnten einen globalen sekundären Index mit einem Partitionsschlüssel von `TopScore` und einem Sortierschlüssel von `TopScoreDateTime` erstellen. Sie könnten einen solchen Index verwenden, um festzustellen, ob eine Verbindung zwischen den Highscores und der Uhrzeit, an der ein Spiel stattfindet, besteht.

Jedes Schlüsselattribut eines Indexes muss ein Skalar des `String`, `Number` oder `Binary` sein. (Es kann kein Dokument oder Satz sein.) Sie können Attribute jeden Datentyps in einen globalen sekundären Index projizieren. Dazu zählen skalare Werte, Dokumente und Sätze. Eine vollständige Liste der Datentypen finden Sie unter [Datentypen](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes).

Wenn Sie den bereitgestellten Modus verwenden, müssen Sie die `ProvisionedThroughput`-Einstellungen für den Index, die aus `ReadCapacityUnits` und `WriteCapacityUnits` bestehen, bereitstellen. Diese Einstellungen des bereitgestellten Durchsatzes sind getrennt von denen der Tabelle, aber verhalten sich auf ähnliche Weise. Weitere Informationen finden Sie unter [Überlegungen im Hinblick auf die bereitgestellte Durchsatzkapazität für globale sekundäre Indizes](GSI.md#GSI.ThroughputConsiderations).

 Globale Sekundärindizes erben den read/write Kapazitätsmodus aus der Basistabelle. Weitere Informationen finden Sie unter [Überlegungen beim Umstellen der Kapazitätsmodi in DynamoDB](bp-switching-capacity-modes.md). 

**Anmerkung**  
 Beim Erstellen eines neuen GSI kann es wichtig sein zu prüfen, ob Ihre Wahl des Partitionsschlüssels eine ungleichmäßige oder eingeschränkte Verteilung von Daten oder Datenverkehr über die Partitionsschlüssel-Werte des neuen Index erzeugt. In diesem Fall werden möglicherweise Backfill- und Schreibvorgänge gleichzeitig auftreten und Schreibvorgänge in die Basistabelle drosseln. Der Service ergreift Maßnahmen, um das Potenzial für dieses Szenario zu minimieren, hat jedoch keinen Einblick in die Form von Kundendaten in Bezug auf den Indexpartitionsschlüssel, die gewählte Projektion oder die Spärlichkeit des Index-Primärschlüssels.  
Wenn Sie vermuten, dass Ihr neuer globaler Sekundärindex möglicherweise enge oder verzerrte Daten oder Datenverkehrsverteilung über Partitionsschlüsselwerte hinweg aufweisen könnte, sollten Sie Folgendes berücksichtigen, bevor Sie betrieblich wichtigen Tabellen neue Indizes hinzufügen.  
Es ist möglicherweise am sichersten, den Index zu einem Zeitpunkt hinzuzufügen, zu dem Ihre Anwendung den geringsten Datenverkehr steuert.
Erwägen Sie, CloudWatch Contributor Insights für Ihre Basistabelle und Indizes zu aktivieren. Dies gibt Ihnen wertvolle Einblicke in Ihre Verkehrsverteilung.
 Beobachten Sie `WriteThrottleEvents` den gesamten Prozess und `OnlineIndexPercentageProgress` CloudWatch geben Sie Kennzahlen an. `ThrottledRequests` Passen Sie die bereitgestellte Schreibkapazität nach Bedarf an, um das Backfill innerhalb einer angemessenen Zeit abzuschließen, ohne dass Ihr laufender Betrieb nennenswert beeinträchtigt wird. `OnlineIndexConsumedWriteCapacity`und `OnlineThrottleEvents` es wird erwartet, dass sie beim Auffüllen des Index 0 anzeigen.
Seien Sie bereit, die Indexerstellung abzubrechen, falls Sie aufgrund der Schreibdrosselung betriebliche Auswirkungen haben.

## Beschreiben globaler sekundären Indizes in einer Tabelle
<a name="GSI.Describing"></a>

Um den Status aller globalen sekundären Indizes in einer Tabelle anzuzeigen, verwenden Sie die `DescribeTable`-Operation. Der `GlobalSecondaryIndexes`-Teil der Antwort zeigt alle Indizes der Tabelle, zusammen mit deren jeweiligem aktuellen Status ( `IndexStatus`).

`IndexStatus` für einen globalen sekundären Index ist einer der folgenden:
+ `CREATING` – Der Index wird derzeit erstellt und ist noch nicht verfügbar.
+ `ACTIVE` — Der Index ist betriebsbereit und Anwendungen können in diesem `Query`-Operationen durchführen
+ `UPDATING` — Die Einstellungen des bereitgestellten Durchsatzes des Index werden geändert.
+ `DELETING` – Der Index wird derzeit gelöscht und kann nicht länger verwendet werden.

Wenn DynamoDB einen globalen sekundären Index fertiggestellt hat, ändert sich der Indexstatus von `CREATING` in `ACTIVE`.

## Hinzufügen eines globalen sekundären Index zu einer vorhandenen Tabelle
<a name="GSI.OnlineOps.Creating"></a>

Um einen globalen sekundären Index einer vorhandenen Tabelle hinzuzufügen, verwenden Sie die `UpdateTable`-Operation mit dem Parameter `GlobalSecondaryIndexUpdates`. Geben Sie die folgenden Informationen ein:
+ Ein Indexname. Der Name muss innerhalb der Indizes der Tabelle eindeutig sein.
+ Das Schlüsselschema des Index. Sie müssen ein Attribut für den Partitionsschlüssel des Index angeben. Sie können optional ein weiteres Attribut für den Sortierschlüssel des Index festlegen. Diese beiden Schlüsselattribute müssen nicht mit den Schlüsselattributen in der Tabelle übereinstimmen. Die Datentypen für jedes Schemaattribut müssen skalar sein: `String`, `Number` oder `Binary`.
+ Die Attribute, die von der Tabelle in den Index projiziert werden:
  + `KEYS_ONLY` — Jeder Eintrag im Index besteht nur aus dem Tabellenpartitionsschlüssel und Sortierschlüsselwerten, sowie den Indexschlüsselwerten. 
  + `INCLUDE` — Zusätzlich zu den in `KEYS_ONLY` beschriebenen Attributen, enthält der sekundäre Index andere Nicht-Schlüsselattribute, die Sie angeben.
  + `ALL` — Der Index enthält alle Attribute der Quelltabelle.
+ Die Einstellungen für den bereitgestellten Durchsatz für den Index bestehen aus `ReadCapacityUnits` und `WriteCapacityUnits`. Diese Einstellungen für den bereitgestellten Durchsatz sind getrennt von denen der Tabelle.

Sie können nur einen globalen sekundären Index pro `UpdateTable`-Operation verwenden.

### Phasen der Index-Erstellung
<a name="GSI.OnlineOps.Creating.Phases"></a>

Wenn Sie einen neuen globalen sekundären Index einer vorhandenen Tabelle hinzufügen, ist die Tabelle weiterhin verfügbar, während der Index erstellt wird. Jedoch ist der neue Index für Abfrageoperationen nicht verfügbar bis sein Status sich von `CREATING` in `ACTIVE` ändert.

**Anmerkung**  
Bei der Erstellung des globalen sekundären Index wird Application Auto Scaling nicht verwendet. Wenn die `MIN`-Kapazität für Application Auto Scaling erhöht wird, verkürzt sich die Erstellungszeit für den globalen sekundären Index nicht.

Im Hintergrund erstellt DynamoDB den Index in zwei Phasen:

**Ressourcenzuweisung**  
DynamoDB weist die erforderlichen Rechen- und Speicherressourcen zu, um den Index zu erstellen.  
Während der Ressourcenzuordnungsphase ist das `IndexStatus`-Attribut `CREATING` und das `Backfilling`-Attribut ist False. Um den Status einer Tabelle und all ihre sekundären Indizes abzurufen, verwenden Sie die `DescribeTable`-Operation.  
Während sich der Index in der Ressourcenzuweisungsphase befindet, können Sie weder den Index noch seine übergeordnete Tabelle löschen. Sie können auch den bereitgestellten Durchsatz des Index oder der Tabelle nicht ändern. Sie in der Tabelle keine anderen Indizes hinzufügen oder löschen. Sie können jedoch den bereitgestellten Durchsatz dieser anderen Indizes ändern.

**Abgleichen**  
Für jedes Element in der Tabelle bestimmt DynamoDB, welche Reihe von Attributen auf Basis seiner Projektion (`KEYS_ONLY`, `INCLUDE`, oder `ALL`) in den Index geschrieben werden. Es schreibt dann diese Attribute in den Index. Während der Abgleichphase verfolgt DynamoDB Elemente, die in der Tabelle hinzugefügt, gelöscht oder aktualisiert werden. Die Attribute dieser Elemente werden, soweit erforderlich, auch in dem Index hinzugefügt, gelöscht oder aktualisiert.  
Während der Abgleichphase ist das `IndexStatus`-Attribut auf `CREATING` gesetzt und das `Backfilling`-Attribut ist True. Um den Status einer Tabelle und all ihrer es abzurufen, verwenden Sie die `DescribeTable`-Operation.  
Während der Index abgleicht, können Sie seine übergeordnete Tabelle nicht löschen. Sie können jedoch weiterhin den den Index löschen oder den bereitgestellten Durchsatz der Tabelle und dessen sekundäre Indizes ändern.  
Während der Abgleichphase können einige Schreibvorgänge regelwidriger Elemente erfolgreich sein, während andere abgelehnt werden. Nach dem Abgleich werden alle Schreibvorgänge für Elemente, die gegen das neue Schlüsselschema des Index verstoßen, abgelehnt. Wir empfehlen, dass Sie das Tool Violation Detector ausführen, nachdem die Abgleichphase abgeschlossen ist, um alle Schlüsselverstöße, die gegebenenfalls aufgetreten sind, zu erkennen und zu beheben. Weitere Informationen finden Sie unter [Erkennen und Korrigieren von Indexschlüsselverstößen in DynamoDB](GSI.OnlineOps.ViolationDetection.md).

Während der laufenden Ressourcenzuordnung und Abgleichphasen ist der Index im Status `CREATING`. Während dieser Zeit führt DynamoDB Leseoperationen für die Tabelle durch. Lesevorgänge aus der Basistabelle zum Auffüllen des globalen sekundären Indexes werden Ihnen nicht in Rechnung gestellt.

Sobald die Erstellung des Index abgeschlossen ist, ändert sich sein Status in `ACTIVE`. Sie können den Index nicht `Query` oder `Scan` bis er `ACTIVE` ist.

**Anmerkung**  
In einigen Fällen kann DynamoDB aufgrund von Indexschlüsselverstößen keine Daten aus der Tabelle in den Index schreiben. Dies kann in folgenden Fällen passieren:  
Der Datentyp eines Attributwerts stimmt nicht mit dem Datentyp eines Indexschlüssel-Schema-Datentyps überein.
Die Größe eines Attributs überschreitet die maximale Länge für ein Indexschlüsselattribut.
Ein Indexschlüsselattribut hat eine leere Zeichenfolge oder einen leeren Binärattributwert.
Indexschlüsselverstöße beeinträchtigen die Erstellung des globalen sekundären Indexes nicht. Wenn der Index jedoch `ACTIVE` wird, sind die verstoßenden Schlüssel nicht im Index vorhanden.  
DynamoDB bietet ein eigenständiges Tool für das Erkennen und Lösen dieser Probleme. Weitere Informationen finden Sie unter [Erkennen und Korrigieren von Indexschlüsselverstößen in DynamoDB](GSI.OnlineOps.ViolationDetection.md).

### Hinzufügen eines globalen sekundären Index zu einer großen Tabelle
<a name="GSI.OnlineOps.Creating.LargeTable"></a>

Die erforderliche Zeit für das Erstellen eines globalen sekundären Indizes hängt von mehreren Faktoren ab, z. B.:
+ Die Tabellengröße
+ Die Anzahl der Elemente in der Tabelle, die sich für eine Aufnahme in den Index eignen
+ Die Reihe von Attributen, die in den Index projiziert werden
+ Schreibaktivität der Haupttabelle, während der Index erstellt wird

Wenn Sie einen globalen sekundären Index einer sehr große Tabelle hinzufügen, kann es sehr lang dauern, bis der Erstellungsvorgang abgeschlossen ist. Um den Fortschritt zu überwachen und festzustellen, ob der Index über ausreichende Schreibkapazität verfügt, ziehen Sie die folgenden CloudWatch Amazon-Metriken zu Rate:
+ `OnlineIndexPercentageProgress`

Weitere Informationen zu CloudWatch Metriken im Zusammenhang mit DynamoDB finden Sie unter. [DynamoDB-Metriken](metrics-dimensions.md#dynamodb-metrics)

**Wichtig**  
Möglicherweise müssen Sie sehr große Tabellen auf eine Zulassungsliste setzen, bevor Sie einen globalen sekundären Index erstellen oder aktualisieren können. Bitte wenden Sie sich an den AWS Support, um Ihre Tische auf die Zulassungsliste zu setzen.

Während ein Index abgeglichen wird, verwendet DynamoDB interne Systemkapazität um aus der Tabelle zu lesen. Dies soll die Auswirkung der Indexerstellung minimieren und vermeiden, dass der Tabelle nicht genügend Schreibkapazität zur Verfügung steht.

## Löschen eines globalen sekundären Index
<a name="GSI.OnlineOps.Deleting"></a>

Wenn Sie keinen globalen sekundären Index mehr benötigen, können Sie ihn mithilfe von `UpdateTable` verwenden.

Sie können nur einen globalen sekundären Index pro `UpdateTable`verwenden.

Während der globale sekundäre Index gelöscht wird, hat dies keine Auswirkung auf jegliche Lese- oder Schreibaktivität in der übergeordneten Tabelle. Während die Löschung ausgeführt wird, können Sie weiterhin den bereitgestellten Durchsatz anderer Indizes ändern

**Anmerkung**  
Beim Löschen einer Tabelle mithilfe der `DeleteTable`-Aktion, werden alle es in dieser Tabelle ebenfalls gelöscht.
Die Löschung des globalen sekundären Index wird Ihrem Konto nicht berechnet.

## Ändern eines globalen sekundären Indexes während der Erstellung
<a name="GSI.OnlineOps.Creating.Modify"></a>

Während ein Index erstellt wird, können Sie die `DescribeTable`-Operation verwenden, um zu bestimmen, in welcher Phase er sich befindet. Die Beschreibung für den Index enthält ein Boolesches Attribut, `Backfilling`, um anzugeben, ob DynamoDB derzeit den Index mit Elementen aus der Tabelle lädt. Wenn `Backfilling` True ist, dann ist die Phase der Ressourcenzuordnung abgeschlossen und der Index ist dabei, abzugleichen. 

Während des Abgleichens können Sie den erstellten Index löschen. Während der Phase können Sie in der Tabelle keine anderen Indizes hinzufügen oder löschen.

**Anmerkung**  
Bei Indizes, die als Teil einer `CreateTable`-Operation erstellt wurden, wird das `Backfilling`-Attribut in der `DescribeTable`-Ausgabe nicht angezeigt. Weitere Informationen finden Sie unter [Phasen der Index-Erstellung](#GSI.OnlineOps.Creating.Phases).

# Erkennen und Korrigieren von Indexschlüsselverstößen in DynamoDB
<a name="GSI.OnlineOps.ViolationDetection"></a>

Während der Abgleichphase der globalen sekundären Index-Erstellung untersucht Amazon DynamoDB jedes Element in der Tabelle, um zu ermitteln, ob es in den Index aufgenommen werden kann. Einige Elemente können möglicherweise nicht in den Index aufgenommen werden, da sie zu Indexschlüsselverstößen führen würden. In diesem Fall verbleiben die Elemente in der Tabelle, der Index verfügt dann jedoch über keinen entsprechenden Eintrag für dieses Element.

*Verstoß des Indexschlüssels* tritt in folgenden Situationen auf:
+ Die Datentypen zwischen einem Attributwert und dem Indexschlüsselschema stimmen nicht überein. Angenommen, eines der Elemente in der Tabelle `GameScores` verfügt über den Wert `TopScore` des Typs `String`. Wenn Sie dann einen globalen sekundären Index mit dem Partitionsschlüssel `TopScore` vom Typ `Number` hinzufügen, verstößt das Element aus der Tabelle gegen den Index.
+ Ein Attributwert aus der Tabelle überschreitet die maximale Länge für ein Indexschlüsselattribut. Die maximale Länge des Partitionsschlüssels beträgt 2048 bytes und die maximale Länge des Sortierschlüssels 1024 bytes. Wenn einer der entsprechenden Attributwerte in der Tabelle diese Grenzwerte überschreitet, würde das Element aus der Tabelle gegen den Index verstoßen.

**Anmerkung**  
Wenn ein Zeichenfolgen- oder Binär-Attributwert für ein Attribut festgelegt ist, das als Indexschlüssel verwendet wird, muss der Attributwert eine Länge größer als Null haben. Andernfalls würde das Element aus der Tabelle gegen den Indexschlüssel verstoßen.  
Dieses Tool kennzeichnet diesen Indexschlüsselverstoß derzeit nicht.

Wenn ein Indexschlüsselverstoß auftritt, wird die Auffüllphase ohne Unterbrechung fortgesetzt. Verstoßende Elemente sind jedoch nicht im Index enthalten. Nach Abschluss der Abgleichphase werden alle Schreibvorgänge für Elemente, die gegen das neue Schlüsselschema des Indexes verstoßen, abgelehnt.

Um die Attributwerte in einer Tabelle, die gegen einen Indexschlüssel verstoßen, zu identifizieren und zu korrigieren, verwenden Sie das Tool Violation Detector. Um Violation Detector auszuführen, erstellen Sie eine Konfigurationsdatei, die den Namen einer zu scannenden Tabelle sowie die Datentypen des Partitions- und Sortierschlüssels des globalen sekundären Indizes enthält und angibt, welche Aktionen unternommen werden, sollten Verstöße gegen den Indexschlüssel gefunden werden. Violation Detector kann in einem der beiden folgenden Modi ausgeführt werden:
+ **Erkennungsmodus** — Erkennt Indexschlüsselverstöße. Verwenden Sie den Erkennungsmodus, um die Elemente in der Tabelle zu ermitteln, die in einem globalen sekundären Index zu Schlüsselverstößen führen. (Sie können optional angeben, dass die den Verstoß verursachenden Tabellenelemente sofort gelöscht werden, nachdem sie gefunden wurden.) Die Ausgabe des Erkennungsmodus wird eine Datei geschrieben, die Sie für weitere Analysen verwenden können.
+ **Korrekturmodus** — Korrigiert Indexschlüsselverstöße. Im Korrekturmodus liest Violation Detector eine Eingabedatei im gleichen Format wie die Ausgabedatei des Erkennungsmodus. Der Korrekturmodus liest die Datensätze aus der Eingabedatei und löscht zu jedem Datensatz die entsprechenden Elemente in der Tabelle bzw. aktualisiert diese. (Wenn Sie beschließen, die Elemente zu aktualisieren, müssen Sie die Eingabedatei bearbeiten und für diese Aktualisierungen entsprechende Werte festlegen.)

## Herunterladen und Ausführen von Violation Detector
<a name="GSI.OnlineOps.ViolationDetection.Running"></a>

Violation Detector steht als ausführbares Java-Archiv (`.jar`-Datei) zur Verfügung und kann auf Windows-, MacOS- oder Linux-Computern ausgeführt werden. Der Violation Detector erfordert Java 1.7 (oder höher) und Apache Maven.
+ [Laden Sie den Verletzungsdetektor herunter von GitHub](https://github.com/awslabs/dynamodb-online-index-violation-detector)

Um Violation Detector mit Maven herunterzuladen und zu installieren, befolgen Sie die Anweisungen in der `README.md`-Datei.

Um Violation Detector zu starten, gehen Sie zu dem Verzeichnis, in dem Sie `ViolationDetector.java` erstellt haben, und geben Sie den folgenden Befehl ein:

```
java -jar ViolationDetector.jar [options]
```

Die Violation-Detector-Befehlszeile akzeptiert die folgenden Optionen:
+ `-h | --help` – Druckt eine Nutzungszusammenfassung und die Optionen für Violation Detector.
+ `-p | --configFilePath` `value` – Der vollständig qualifizierte Namen einer Violation Detector Konfigurationsdatei. Weitere Informationen finden Sie unter [Die Konfigurationsdatei für den Violation Detector](#GSI.OnlineOps.ViolationDetection.ConfigFile).
+ `-t | --detect`  `value`– Erkennt Indexschlüsselverstöße in der Tabelle und schreibt sie in die Ausgabedatei von Violation Detector. Wenn der Wert für diesen Parameter auf `keep` festgelegt ist, werden Elemente mit Schlüsselverstößen nicht geändert. Wenn der Wert auf `delete` festgelegt ist, werden Elemente mit Schlüsselverstößen aus der Tabelle gelöscht.
+ `-c | --correct` `value` — Liest Indexschlüsselverstöße aus einer Eingabedatei und nimmt entsprechende Korrekturen an den Elementen in der Tabelle vor. Wenn der Wert für diesen Parameter auf `update` festgelegt ist, werden Elemente mit Schlüsselverstößen mit neuen, konformen Werten aktualisiert. Wenn der Wert auf `delete` festgelegt ist, werden Elemente mit Schlüsselverstößen aus der Tabelle gelöscht.

## Die Konfigurationsdatei für den Violation Detector
<a name="GSI.OnlineOps.ViolationDetection.ConfigFile"></a>

Zur Laufzeit erfordert das Tool Violation Detector eine Konfigurationsdatei. Die Parameter in dieser Datei bestimmen, auf welche DynamoDB-Ressourcen Violation Detector zugreifen kann und wie viel bereitgestellten Durchsatz es verbrauchen darf. In der Tabelle unten werden diese Parameter beschrieben.


****  

| Parametername | Description | Erforderlich? | 
| --- | --- | --- | 
|  `awsCredentialsFile`  |  Der vollständig qualifizierte Namen einer Datei, die Ihre AWS -Anmeldeinformationen enthält. Die Datei mit den Anmeldeinformationen muss das folgende Format aufweisen: <pre>accessKey = access_key_id_goes_here<br />secretKey = secret_key_goes_here </pre>  |  Ja  | 
|  `dynamoDBRegion`  |  Die AWS Region, in der sich die Tabelle befindet. Beispiel: `us-west-2`.  |  Ja  | 
|  `tableName`  | Der Name der zu scannenden DynamoDB-Tabelle. |  Ja  | 
|  `gsiHashKeyName`  |  Der Name des Partitionsschlüssels des Index.  |  Ja  | 
|  `gsiHashKeyType`  |  Der Datentyp des Partitionsschlüssels des Index – `String`, `Number` oder `Binary`: `S \| N \| B`  |  Ja  | 
|  `gsiRangeKeyName`  |  Der Name des Sortierschlüssels des Index. Geben Sie diesen Parameter nicht an, wenn der Index nur über einen einfachen Primärschlüssel (Partitionsschlüssel) verfügt.  |  Nein  | 
|  `gsiRangeKeyType`  |  Der Datentyp des Sortierschlüssels–`String`,`Number`, oder`Binary`: `S \| N \| B`  Geben Sie diesen Parameter nicht an, wenn der Index nur über einen einfachen Primärschlüssel (Partitionsschlüssel) verfügt.  |  Nein  | 
|  `recordDetails`  |  Gibt an, ob alle Details der Indexschlüsselverstöße in die Ausgabedatei geschrieben werden sollen. Ist dieser Parameter auf `true` (Standardwert) gesetzt, werden alle Informationen zu den Verstoß-Elementen aufgezeichnet. Wenn er auf `false` festgelegt ist, wird nur die Anzahl der Verstöße erfasst.  |  Nein  | 
|  `recordGsiValueInViolationRecord`  |  Gibt an, ob die Werte der verursachenden Indexschlüssel in die Ausgabedatei geschrieben werden sollen. Ist dieser Parameter auf `true` (Standardwert) gesetzt, werden die Schlüsselwerte aufgezeichnet. Ist dieser Parameter auf `false` festgelegt, werden die Schlüsselwerte nicht aufgezeichnet.  |  Nein  | 
|  `detectionOutputPath`  |  Der vollständige Pfad der Violation-Detector-Ausgabedatei. Dieser Parameter unterstützt das Schreiben in ein lokales Verzeichnis oder in Amazon Simple Storage Service (Amazon S3). Im Folgenden sind einige Beispiele aufgeführt: `detectionOutputPath = ``//local/path/filename.csv` `detectionOutputPath = ``s3://bucket/filename.csv` Die Informationen in der Ausgabedatei werden im komma-separierten Werte (CSV)-Format angezeigt. Wenn Sie `detectionOutputPath` nicht festlegen, wird die Ausgabedatei `violation_detection.csv` genannt und in das aktuelle Arbeitsverzeichnis geschrieben.  |  Nein  | 
|  `numOfSegments`  | Die Anzahl der Segmente des parallelen Scans, die verwendet werden sollen, wenn Violation Detector die Tabelle scannt. Der Standardwert ist 1. Dies bedeutet, dass die Tabelle sequenziell gescannt wird. Wenn der Wert 2 oder höher lautet, unterteilt Violation Detector die Tabelle in diese Anzahl logischer Segmente und eine gleichmäßigen Anzahl von Scan-Threads. Die maximale Einstellung für `numOfSegments` lautet 4.096 Tage.Bei größeren Tabellen ist ein paralleler Scan in der Regel schneller als ein sequenzieller Scan. Wenn die Tabelle außerdem groß genug ist, um sich über mehrere Partitionen zu erstrecken, verteilt ein paralleler Scan die Lesevorgänge gleichmäßig auf mehrere Partitionen.Weitere Informationen zu parallelen Scans in DynamoDB finden Sie im Abschnitt [Parallele Scans](Scan.md#Scan.ParallelScan). |  Nein  | 
|  `numOfViolations`  |  Der obere Grenzwert für Indexschlüsselverstöße, die in die Ausgabedatei geschrieben werden sollen. Ist dieser Parameter auf `-1` (Standardwert) gesetzt, wird die gesamte Tabelle gescannt. Ist eine positive Ganzzahl festgelegt, beendet Violation Detector den Vorgang nach Erreichen dieser Anzahl von Verstößen.  |  Nein  | 
|  `numOfRecords`  |  Die Anzahl der Elemente in der Tabelle, die gescannt werden sollen. Ist dieser Parameter auf -1 (Standardwert) gesetzt, wird die gesamte Tabelle gescannt. Ist eine positive Ganzzahl festgelegt, beendet Violation Detector den Vorgang, nachdem es diese Anzahl von Elementen in der Tabelle gescannt hat.  |  Nein  | 
|  `readWriteIOPSPercent`  |  Regelt den Prozentsatz der bereitgestellten Lesekapazitätseinheiten, die während des Tabellen-Scans verbraucht werden. Der Bereich gültiger Werte lautet `1` bis `100`. Der Standardwert (`25`) bedeutet, dass Violation Detector nicht mehr als 25 % des bereitgestellten Lesedurchsatzes der Tabelle verbraucht.  |  Nein  | 
|  `correctionInputPath`  |  Der vollständige Pfad der Violation-Detector-Korrektureingabedatei. Wenn Sie Violation Detector im Korrekturmodus ausführen, wird der Inhalt dieser Datei herangezogen, um die Datenelemente in der Tabelle, die gegen den verstoßen, zu ändern oder zu löschen. Das Format der `correctionInputPath`-Datei ist identisch mit der `detectionOutputPath`-Datei. So können Sie die Ausgabe des Erkennungsmodus als Eingabe für den Korrekturmodus verarbeiten.  |  Nein  | 
|  `correctionOutputPath`  |  Der vollständige Pfad der Violation-Detector-Korrekturausgabedatei. Diese Datei wird nur erstellt, wenn bei der Aktualisierung Fehler aufgetreten sind. Dieser Parameter unterstützt das Schreiben in ein lokales Verzeichnis oder in Amazon S3. Im Folgenden sind einige Beispiele aufgeführt: `correctionOutputPath = ``//local/path/filename.csv` `correctionOutputPath = ``s3://bucket/filename.csv` Die Informationen in der Ausgabedatei werden im CSV-Format angezeigt. Wenn Sie `correctionOutputPath` nicht festlegen, wird die Ausgabedatei `violation_update_errors.csv` genannt und in das aktuelle Arbeitsverzeichnis geschrieben.  |  Nein  | 

## Erkennung
<a name="GSI.OnlineOps.ViolationDetection.Detection"></a>

Um Verstöße gegen den Indexschlüssel zu erkennen, verwenden Sie Violation Detector mit der Befehlszeilenoption `--detect`. Um zu verstehen, wie diese Option funktioniert, sollten Sie die Tabelle `ProductCatalog` zurate ziehen. Im Folgenden finden Sie eine Liste der Elemente in der Tabelle. Nur der Primärschlüssel (`Id`) und das `Price`-Attribut werden angezeigt.


****  

| ID (Primärschlüssel) | Preis | 
| --- | --- | 
| 101 |  5  | 
| 102 |  20  | 
| 103 | 200  | 
| 201 |  100  | 
| 202 |  200  | 
| 203 |  300  | 
| 204 |  400  | 
| 205 |  500  | 

Alle Werte für `Price` sind vom Typ `Number`. Da DynamoDB jedoch schemalos ist, können Sie ein Element mit einem nicht numerischen `Price` hinzufügen. Angenommen, wir fügen ein anderes Element zur Tabelle `ProductCatalog` hinzu.


****  

| ID (Primärschlüssel) | Preis | 
| --- | --- | 
| 999 | "Hello" | 

Die Tabelle verfügt jetzt über insgesamt neun Elemente.

Nun fügen Sie der Tabelle einen neuen globalen sekundären Index hinzu: `PriceIndex`. Der Primärschlüssel für diesen Index ist ein Partitionsschlüssel, `Price`, vom Typ `Number`. Nachdem der Index erstellt wurde, enthält er acht Elemente—aber die `ProductCatalog`-Tabelle verfügt aber über neun Elemente. Der Grund für diese Abweichung ist, dass der Wert `"Hello"` vom Typ `String` ist, `PriceIndex` aber über einen Primärschlüssel vom Typ `Number` verfügt. Der `String`-Wert verstößt gegen den Schlüssel des globalen sekundären Indizes, also ist er im Index nicht vorhanden.

Um Violation Detector in diesem Szenario zu verwenden, erstellen Sie zuerst eine Konfigurationsdatei, wie z. B. folgende:

```
# Properties file for violation detection tool configuration.
# Parameters that are not specified will use default values.

awsCredentialsFile = /home/alice/credentials.txt
dynamoDBRegion = us-west-2
tableName = ProductCatalog
gsiHashKeyName = Price
gsiHashKeyType = N
recordDetails = true
recordGsiValueInViolationRecord = true
detectionOutputPath = ./gsi_violation_check.csv
correctionInputPath = ./gsi_violation_check.csv
numOfSegments = 1
readWriteIOPSPercent = 40
```

Als Nächstes führen Sie den Violation Detector aus, wie im folgenden Beispiel gezeigt.

```
$  java -jar ViolationDetector.jar --configFilePath config.txt --detect keep

Violation detection started: sequential scan, Table name: ProductCatalog, GSI name: PriceIndex
Progress: Items scanned in total: 9,    Items scanned by this thread: 9,    Violations found by this thread: 1, Violations deleted by this thread: 0
Violation detection finished: Records scanned: 9, Violations found: 1, Violations deleted: 0, see results at: ./gsi_violation_check.csv
```

Wenn der Konfigurationsparameter `recordDetails` auf `true` gesetzt ist, schreibt Violation Detector die Details jedes Verstoßes in die Ausgabedatei, wie im folgenden Beispiel:

```
Table Hash Key,GSI Hash Key Value,GSI Hash Key Violation Type,GSI Hash Key Violation Description,GSI Hash Key Update Value(FOR USER),Delete Blank Attributes When Updating?(Y/N) 

999,"{""S"":""Hello""}",Type Violation,Expected: N Found: S,,
```

Die Ausgabedatei ist im CSV-Format. Die erste Zeile in der Datei ist eine Überschrift, gefolgt von einem Datensatz für jedes Element, das gegen den Indexschlüssel verstößt. Die Felder dieser die Verletzung verursachenden Datensätze sind folgende:
+ **Hash-Schlüssel der Tabelle** — Der Partitionsschlüsselwert des Elements in der Tabelle.
+ **Tabellenbereichsschlüssel** — Der Sortierschlüsselwert des Elements in der Tabelle.
+ **GSI-Hash-Schlüsselwert** – Partitionsschlüsselwert des globalen sekundären Indizes.
+ **Art des GSI-Hash-Schlüsselverstoßes** — Entweder `Type Violation` oder `Size Violation`.
+ **Beschreibung des GSI-Hash-Schlüsselverstoßes** — Die Ursache des Verstoßes.
+ **Aktualisierungswert des GSI-Hash-Schlüssels (FÜR BENUTZER)** — Im Korrekturmodus ein neuer, vom Benutzer angegebener Wert für das Attribut.
+ **Schlüsselwert des GSI-Bereichs** – Der Sortierschlüsselwert des globalen sekundären Indexes.
+ **Art des GSI-Bereichsschlüsselverstoßes** — Entweder `Type Violation` oder `Size Violation`.
+ **Beschreibung des GSI-Bereichsschlüsselverstoßes** — Die Ursache des Verstoßes.
+ **Aktualisierungswert des GSI-Bereichsschlüssels (FÜR BENUTZER)** — Im Korrekturmodus ein neuer, vom Benutzer angegebener Wert für das Attribut.
+ **Leeres Attribut bei Aktualisierung löschen (J/N)** — Bestimmt im Korrekturmodus, ob das den Verstoß verursachende Element in der Tabelle gelöscht (J) oder beibehalten (N) werden soll; jedoch nur, wenn eines der folgenden Felder leer ist:
  + `GSI Hash Key Update Value(FOR USER)`
  + `GSI Range Key Update Value(FOR USER)`

  Wenn eines dieser Felder nicht leer ist, hat `Delete Blank Attribute When Updating(Y/N)` keine Auswirkungen.

**Anmerkung**  
Das Ausgabeformat kann abhängig von der Konfigurationsdatei und den Befehlszeilenoptionen variieren. Wenn die Tabelle z. B. über einen einfachen Primärschlüssel (ohne Sortierschlüssel) verfügt, sind in der Ausgabe keine Sortierschlüsselfelder vorhanden.  
Die den Verstoß verursachenden Datensätze in der Datei sind möglicherweise nicht sortiert.

## Korrektur
<a name="GSI.OnlineOps.ViolationDetection.Correction"></a>

Um Verstöße gegen den Indexschlüssel zu korrigieren, verwenden Sie Violation Detector mit der Befehlszeilenoption `--correct`. Im Korrekturmodus liest Violation Detector die Eingabedatei, die im Parameter `correctionInputPath` angegeben ist. Diese Datei hat das gleiche Format wie die Datei `detectionOutputPath`. Sie können also die Ausgabe der Erkennung als Eingabe für die Korrektur verwenden.

Violation Detector bietet zwei verschiedene Möglichkeiten zur Korrektur von Indexschlüsselverstößen:
+ **Verstöße löschen** – Löscht die Tabellenelemente, die über Verstoß-Attributwerte verfügen.
+ **Verstöße aktualisieren** – Aktualisiert die Tabellenelemente durch Ersetzen der Verstoß-Attribute durch konforme Werte.

In beiden Fällen können Sie die Ausgabedatei aus dem Erkennungsmodus als Eingabe für den Korrekturmodus verwenden.

Setzen wir unser Beispiel `ProductCatalog` fort: Angenommen, wir möchten das Verstoß-Element aus der Tabelle löschen. Zu diesem Zweck verwenden Sie die folgenden Befehlszeile:

```
$  java -jar ViolationDetector.jar --configFilePath config.txt --correct delete
```

An diesem Punkt werden Sie gebeten zu bestätigen, ob die verletzenden Elemente gelöscht werden sollen.

```
Are you sure to delete all violations on the table?y/n
y
Confirmed, will delete violations on the table...
Violation correction from file started: Reading records from file: ./gsi_violation_check.csv, will delete these records from table.
Violation correction from file finished: Violations delete: 1, Violations Update: 0
```

Nun verfügen `ProductCatalog` und `PriceIndex` über dieselbe Anzahl Elemente.

# Arbeiten mit globalen sekundären Indizes: Java
<a name="GSIJavaDocumentAPI"></a>

Sie können die AWS SDK für Java Document API verwenden, um eine Amazon DynamoDB-Tabelle mit einem oder mehreren globalen Sekundärindizes zu erstellen, die Indizes in der Tabelle zu beschreiben und Abfragen mithilfe der Indizes durchzuführen. 

Nachfolgend sind die allgemeinen Schritte für Tabellenoperationen aufgeführt. 

1. Erstellen Sie eine Instance der `DynamoDB`-Klasse.

1. Stellen Sie den erforderlichen und optionalen Parameter für die Operation bereit, indem Sie die entsprechenden Anforderungsobjekte erstellen. 

1. Rufen Sie die entsprechende Methode auf, die vom Client, den Sie im vorhergehenden Schritt erstellt haben, bereitgestellt wird. 

**Topics**
+ [Erstellen einer Tabelle mit einem globalen sekundären Index](#GSIJavaDocumentAPI.CreateTableWithIndex)
+ [Beschreiben einer Tabelle mit einem globalen sekundären Index](#GSIJavaDocumentAPI.DescribeTableWithIndex)
+ [Abfragen eines globalen sekundären Indexes](#GSIJavaDocumentAPI.QueryAnIndex)
+ [Beispiel: Globale Sekundärindizes mithilfe der AWS SDK für Java Dokument-API](GSIJavaDocumentAPI.Example.md)

## Erstellen einer Tabelle mit einem globalen sekundären Index
<a name="GSIJavaDocumentAPI.CreateTableWithIndex"></a>

Sie können globale sekundäre Indizes gleichzeitig mit der Tabelle erstellen. Zu diesem Zweck verwenden Sie `CreateTable` und geben Ihre Spezifikationen für ein oder mehrere globale sekundäre Indizes an. Das folgende Java-Codebeispiel erstellt eine Tabelle, die Informationen über Wetterdaten enthält. Der Partitionsschlüssel ist `Location` und der Sortierschlüssel `Date`. Ein globaler sekundärer Index mit Namen `PrecipIndex` ermöglicht einen schnellen Zugriff auf Niederschlagsdaten für verschiedene Standorte.

Im Folgenden sind die Schritte zum Erstellen einer Tabelle mit einem globalen sekundären Index unter Verwendung der DynamoDB-Dokument-API aufgeführt. 

1. Erstellen Sie eine Instance der `DynamoDB`-Klasse.

1. Erstellen Sie eine Instance der `CreateTableRequest`-Klasse, um die Anforderungsinformationen bereitzustellen.

   Sie müssen den Tabellennamen, seinen zugehörigen Primärschlüssel und die Werte des bereitgestellten Durchsatzes angeben. Für den globalen sekundären Index müssen Sie den Indexnamen, seine Einstellungen des bereitgestellten Durchsatzes, die Attributdefinitionen für den Index, das Schlüsselschema für den Index und die Attributprojektion angeben.

1. Rufen Sie die `createTable`-Methode auf, indem das Anforderungsobjekt als Parameter festgelegt wird.

Im folgenden Java-Codebeispiel werden die vorherigen Schritte veranschaulicht. Der Code erstellt eine Tabelle (`WeatherData`) mit einem globalen sekundären Index (`PrecipIndex`). Der Index-Partitionsschlüssel ist `Date` und der Sortierschlüssel `Precipitation`. Alle Tabellenattribute werden in den Index projiziert. Benutzer können diesen Index abfragen, um Wetterdaten für ein bestimmtes Datum abzurufen. Optional können diese nach Niederschlagsmenge sortiert werden. 

Da es sich bei `Precipitation` um kein Schlüsselattribut für die Tabelle handelt, ist es nicht erforderlich. `WeatherData`-Elemente ohne `Precipitation` erscheinen jedoch nicht in `PrecipIndex`.

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

// Attribute definitions
ArrayList<AttributeDefinition> attributeDefinitions = new ArrayList<AttributeDefinition>();

attributeDefinitions.add(new AttributeDefinition()
    .withAttributeName("Location")
    .withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition()
    .withAttributeName("Date")
    .withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition()
    .withAttributeName("Precipitation")
    .withAttributeType("N"));

// Table key schema
ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>();
tableKeySchema.add(new KeySchemaElement()
    .withAttributeName("Location")
    .withKeyType(KeyType.HASH));  //Partition key
tableKeySchema.add(new KeySchemaElement()
    .withAttributeName("Date")
    .withKeyType(KeyType.RANGE));  //Sort key

// PrecipIndex
GlobalSecondaryIndex precipIndex = new GlobalSecondaryIndex()
    .withIndexName("PrecipIndex")
    .withProvisionedThroughput(new ProvisionedThroughput()
        .withReadCapacityUnits((long) 10)
        .withWriteCapacityUnits((long) 1))
        .withProjection(new Projection().withProjectionType(ProjectionType.ALL));

ArrayList<KeySchemaElement> indexKeySchema = new ArrayList<KeySchemaElement>();

indexKeySchema.add(new KeySchemaElement()
    .withAttributeName("Date")
    .withKeyType(KeyType.HASH));  //Partition key
indexKeySchema.add(new KeySchemaElement()
    .withAttributeName("Precipitation")
    .withKeyType(KeyType.RANGE));  //Sort key

precipIndex.setKeySchema(indexKeySchema);

CreateTableRequest createTableRequest = new CreateTableRequest()
    .withTableName("WeatherData")
    .withProvisionedThroughput(new ProvisionedThroughput()
        .withReadCapacityUnits((long) 5)
        .withWriteCapacityUnits((long) 1))
    .withAttributeDefinitions(attributeDefinitions)
    .withKeySchema(tableKeySchema)
    .withGlobalSecondaryIndexes(precipIndex);

Table table = dynamoDB.createTable(createTableRequest);
System.out.println(table.getDescription());
```

Sie müssen warten bis DynamoDB die Tabelle erstellt und den Tabellenstatus auf `ACTIVE` setzt. Im Anschluss können Sie die Daten in der Tabelle ablegen.

## Beschreiben einer Tabelle mit einem globalen sekundären Index
<a name="GSIJavaDocumentAPI.DescribeTableWithIndex"></a>

Um Informationen zu globalen sekundären Indizes in einer Tabelle zu erhalten, verwenden Sie `DescribeTable`. Sie können auf den Namen, das Schlüsselschema und die projizierten Attribute von jedem Index zugreifen.

Im Folgenden werden die Schritte zum Zugriff auf globale sekundäre Index-Informationen in einer Tabelle dargelegt. 

1. Erstellen Sie eine Instance der `DynamoDB`-Klasse.

1. Erstellen Sie eine Instance der `Table`-Klasse, um den Index darzustellen, mit dem Sie arbeiten möchten.

1. Rufen Sie die `describe`-Methode für das `Table`-Objekt auf.

Im folgenden Java-Codebeispiel werden die vorherigen Schritte veranschaulicht.

**Example**  

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

Table table = dynamoDB.getTable("WeatherData");
TableDescription tableDesc = table.describe();
    

Iterator<GlobalSecondaryIndexDescription> gsiIter = tableDesc.getGlobalSecondaryIndexes().iterator();
while (gsiIter.hasNext()) {
    GlobalSecondaryIndexDescription gsiDesc = gsiIter.next();
    System.out.println("Info for index "
         + gsiDesc.getIndexName() + ":");

    Iterator<KeySchemaElement> kseIter = gsiDesc.getKeySchema().iterator();
    while (kseIter.hasNext()) {
        KeySchemaElement kse = kseIter.next();
        System.out.printf("\t%s: %s\n", kse.getAttributeName(), kse.getKeyType());
    }
    Projection projection = gsiDesc.getProjection();
    System.out.println("\tThe projection type is: "
        + projection.getProjectionType());
    if (projection.getProjectionType().toString().equals("INCLUDE")) {
        System.out.println("\t\tThe non-key projected attributes are: "
            + projection.getNonKeyAttributes());
    }
}
```

## Abfragen eines globalen sekundären Indexes
<a name="GSIJavaDocumentAPI.QueryAnIndex"></a>

Sie können `Query` für einen globalen sekundären Index genauso nutzen, wie Sie `Query` für eine Tabelle nutzen. Sie müssen den Indexnamen, die Abfragekriterien für den Indexpartitionsschlüssel und Sortierschlüssel (falls vorhanden) und die Attribute angeben, die Sie zurückgeben möchten. In diesem Beispiel ist der Index `PrecipIndex`, der über den Partitionsschlüssel `Date` und den Sortierschlüssel `Precipitation` verfügt. Die Indexabfrage gibt alle Wetterdaten für ein bestimmtes Datum zurück, in denen der Niederschlag größer als Null ist.

Im Folgenden werden die Schritte beschrieben, um einen globalen sekundären Index mithilfe der Document API abzufragen. AWS SDK für Java 

1. Erstellen Sie eine Instance der `DynamoDB`-Klasse.

1. Erstellen Sie eine Instance der `Table`-Klasse, um den Index darzustellen, mit dem Sie arbeiten möchten.

1. Erstellen Sie für den Index, den Sie abfragen möchten, eine Instance der `Index`-Klasse.

1. Rufen Sie die `query`-Methode für das `Index`-Objekt auf.

Der Attributname `Date` ist ein DynamoDB-reserviertes Wort. Daher müssen Sie einen Ausdrucksattributnamen als Platzhalter in dem `KeyConditionExpression` verwenden.

Im folgenden Java-Codebeispiel werden die vorherigen Schritte veranschaulicht.

**Example**  

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

Table table = dynamoDB.getTable("WeatherData");
Index index = table.getIndex("PrecipIndex");

QuerySpec spec = new QuerySpec()
    .withKeyConditionExpression("#d = :v_date and Precipitation = :v_precip")
    .withNameMap(new NameMap()
        .with("#d", "Date"))
    .withValueMap(new ValueMap()
        .withString(":v_date","2013-08-10")
        .withNumber(":v_precip",0));

ItemCollection<QueryOutcome> items = index.query(spec);
Iterator<Item> iter = items.iterator(); 
while (iter.hasNext()) {
    System.out.println(iter.next().toJSONPretty());
}
```

# Beispiel: Globale Sekundärindizes mithilfe der AWS SDK für Java Dokument-API
<a name="GSIJavaDocumentAPI.Example"></a>

Das folgende Java-Codebeispiel zeigt, wie Sie mit globalen sekundären Indizes arbeiten. Das Beispiel erstellt eine Tabelle mit dem Namen `Issues`, die in einem einfachen Fehlerüberwachungssystem für Softwareentwicklung verwendet werden könnte. Der Partitionsschlüssel ist `IssueId` und der Sortierschlüssel `Title`. Es gibt drei globale sekundäre Indizes in dieser Tabelle:
+ `CreateDateIndex` – Der Partitionsschlüssel ist `CreateDate` und der Sortierschlüssel `IssueId`. Zusätzlich zu den Tabellenschlüsseln werden die Attribute `Description` und `Status` in den Index projiziert.
+ `TitleIndex` – Der Partitionsschlüssel ist `Title` und der Sortierschlüssel `IssueId`. Keine anderen Attribute als die Tabellenschlüssel werden in den Index projiziert.
+ `DueDateIndex` — Der Partitionsschlüssel ist `DueDate`. Ein Sortierschlüssel ist nicht vorhanden.) Alle Tabellenattribute werden in den Index projiziert.

Nachdem die `Issues`-Tabelle erstellt wurde, lädt das Programm die Tabelle mit Daten, die Software-Fehlerberichte darstellen. Anschließend werden die Daten mithilfe der globalen sekundären Indizes abgefragt. Schließlich löscht das Programm die `Issues`-Tabelle.

 step-by-stepAnweisungen zum Testen des folgenden Beispiels finden Sie unter[Java-Codebeispiele](CodeSamples.Java.md).

**Example**  

```
package com.amazonaws.codesamples.document;

import java.util.ArrayList;
import java.util.Iterator;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Index;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.ItemCollection;
import com.amazonaws.services.dynamodbv2.document.QueryOutcome;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.document.spec.QuerySpec;
import com.amazonaws.services.dynamodbv2.document.utils.ValueMap;
import com.amazonaws.services.dynamodbv2.model.AttributeDefinition;
import com.amazonaws.services.dynamodbv2.model.CreateTableRequest;
import com.amazonaws.services.dynamodbv2.model.GlobalSecondaryIndex;
import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
import com.amazonaws.services.dynamodbv2.model.KeyType;
import com.amazonaws.services.dynamodbv2.model.Projection;
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;

public class DocumentAPIGlobalSecondaryIndexExample {

    static AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
    static DynamoDB dynamoDB = new DynamoDB(client);

    public static String tableName = "Issues";

    public static void main(String[] args) throws Exception {

        createTable();
        loadData();

        queryIndex("CreateDateIndex");
        queryIndex("TitleIndex");
        queryIndex("DueDateIndex");

        deleteTable(tableName);

    }

    public static void createTable() {

        // Attribute definitions
        ArrayList<AttributeDefinition> attributeDefinitions = new ArrayList<AttributeDefinition>();

        attributeDefinitions.add(new AttributeDefinition().withAttributeName("IssueId").withAttributeType("S"));
        attributeDefinitions.add(new AttributeDefinition().withAttributeName("Title").withAttributeType("S"));
        attributeDefinitions.add(new AttributeDefinition().withAttributeName("CreateDate").withAttributeType("S"));
        attributeDefinitions.add(new AttributeDefinition().withAttributeName("DueDate").withAttributeType("S"));

        // Key schema for table
        ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>();
        tableKeySchema.add(new KeySchemaElement().withAttributeName("IssueId").withKeyType(KeyType.HASH)); // Partition
                                                                                                           // key
        tableKeySchema.add(new KeySchemaElement().withAttributeName("Title").withKeyType(KeyType.RANGE)); // Sort
                                                                                                          // key

        // Initial provisioned throughput settings for the indexes
        ProvisionedThroughput ptIndex = new ProvisionedThroughput().withReadCapacityUnits(1L)
                .withWriteCapacityUnits(1L);

        // CreateDateIndex
        GlobalSecondaryIndex createDateIndex = new GlobalSecondaryIndex().withIndexName("CreateDateIndex")
                .withProvisionedThroughput(ptIndex)
                .withKeySchema(new KeySchemaElement().withAttributeName("CreateDate").withKeyType(KeyType.HASH), // Partition
                                                                                                                 // key
                        new KeySchemaElement().withAttributeName("IssueId").withKeyType(KeyType.RANGE)) // Sort
                                                                                                        // key
                .withProjection(
                        new Projection().withProjectionType("INCLUDE").withNonKeyAttributes("Description", "Status"));

        // TitleIndex
        GlobalSecondaryIndex titleIndex = new GlobalSecondaryIndex().withIndexName("TitleIndex")
                .withProvisionedThroughput(ptIndex)
                .withKeySchema(new KeySchemaElement().withAttributeName("Title").withKeyType(KeyType.HASH), // Partition
                                                                                                            // key
                        new KeySchemaElement().withAttributeName("IssueId").withKeyType(KeyType.RANGE)) // Sort
                                                                                                        // key
                .withProjection(new Projection().withProjectionType("KEYS_ONLY"));

        // DueDateIndex
        GlobalSecondaryIndex dueDateIndex = new GlobalSecondaryIndex().withIndexName("DueDateIndex")
                .withProvisionedThroughput(ptIndex)
                .withKeySchema(new KeySchemaElement().withAttributeName("DueDate").withKeyType(KeyType.HASH)) // Partition
                                                                                                              // key
                .withProjection(new Projection().withProjectionType("ALL"));

        CreateTableRequest createTableRequest = new CreateTableRequest().withTableName(tableName)
                .withProvisionedThroughput(
                        new ProvisionedThroughput().withReadCapacityUnits((long) 1).withWriteCapacityUnits((long) 1))
                .withAttributeDefinitions(attributeDefinitions).withKeySchema(tableKeySchema)
                .withGlobalSecondaryIndexes(createDateIndex, titleIndex, dueDateIndex);

        System.out.println("Creating table " + tableName + "...");
        dynamoDB.createTable(createTableRequest);

        // Wait for table to become active
        System.out.println("Waiting for " + tableName + " to become ACTIVE...");
        try {
            Table table = dynamoDB.getTable(tableName);
            table.waitForActive();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void queryIndex(String indexName) {

        Table table = dynamoDB.getTable(tableName);

        System.out.println("\n***********************************************************\n");
        System.out.print("Querying index " + indexName + "...");

        Index index = table.getIndex(indexName);

        ItemCollection<QueryOutcome> items = null;

        QuerySpec querySpec = new QuerySpec();

        if (indexName == "CreateDateIndex") {
            System.out.println("Issues filed on 2013-11-01");
            querySpec.withKeyConditionExpression("CreateDate = :v_date and begins_with(IssueId, :v_issue)")
                    .withValueMap(new ValueMap().withString(":v_date", "2013-11-01").withString(":v_issue", "A-"));
            items = index.query(querySpec);
        } else if (indexName == "TitleIndex") {
            System.out.println("Compilation errors");
            querySpec.withKeyConditionExpression("Title = :v_title and begins_with(IssueId, :v_issue)")
                    .withValueMap(
                            new ValueMap().withString(":v_title", "Compilation error").withString(":v_issue", "A-"));
            items = index.query(querySpec);
        } else if (indexName == "DueDateIndex") {
            System.out.println("Items that are due on 2013-11-30");
            querySpec.withKeyConditionExpression("DueDate = :v_date")
                    .withValueMap(new ValueMap().withString(":v_date", "2013-11-30"));
            items = index.query(querySpec);
        } else {
            System.out.println("\nNo valid index name provided");
            return;
        }

        Iterator<Item> iterator = items.iterator();

        System.out.println("Query: printing results...");

        while (iterator.hasNext()) {
            System.out.println(iterator.next().toJSONPretty());
        }

    }

    public static void deleteTable(String tableName) {

        System.out.println("Deleting table " + tableName + "...");

        Table table = dynamoDB.getTable(tableName);
        table.delete();

        // Wait for table to be deleted
        System.out.println("Waiting for " + tableName + " to be deleted...");
        try {
            table.waitForDelete();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void loadData() {

        System.out.println("Loading data into table " + tableName + "...");

        // IssueId, Title,
        // Description,
        // CreateDate, LastUpdateDate, DueDate,
        // Priority, Status

        putItem("A-101", "Compilation error", "Can't compile Project X - bad version number. What does this mean?",
                "2013-11-01", "2013-11-02", "2013-11-10", 1, "Assigned");

        putItem("A-102", "Can't read data file", "The main data file is missing, or the permissions are incorrect",
                "2013-11-01", "2013-11-04", "2013-11-30", 2, "In progress");

        putItem("A-103", "Test failure", "Functional test of Project X produces errors", "2013-11-01", "2013-11-02",
                "2013-11-10", 1, "In progress");

        putItem("A-104", "Compilation error", "Variable 'messageCount' was not initialized.", "2013-11-15",
                "2013-11-16", "2013-11-30", 3, "Assigned");

        putItem("A-105", "Network issue", "Can't ping IP address 127.0.0.1. Please fix this.", "2013-11-15",
                "2013-11-16", "2013-11-19", 5, "Assigned");

    }

    public static void putItem(

            String issueId, String title, String description, String createDate, String lastUpdateDate, String dueDate,
            Integer priority, String status) {

        Table table = dynamoDB.getTable(tableName);

        Item item = new Item().withPrimaryKey("IssueId", issueId).withString("Title", title)
                .withString("Description", description).withString("CreateDate", createDate)
                .withString("LastUpdateDate", lastUpdateDate).withString("DueDate", dueDate)
                .withNumber("Priority", priority).withString("Status", status);

        table.putItem(item);
    }

}
```

# Arbeiten mit globalen sekundären Indizes: .NET
<a name="GSILowLevelDotNet"></a>

Sie können die AWS SDK für .NET Low-Level-API verwenden, um eine Amazon DynamoDB-Tabelle mit einem oder mehreren globalen Sekundärindizes zu erstellen, die Indizes in der Tabelle zu beschreiben und Abfragen mithilfe der Indizes durchzuführen. Diese Operationen entsprechen den entsprechenden DynamoDB-Operationen. Weitere Informationen finden Sie in der [Amazon-DynamoDB-API-Referenz](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/). 

Folgende sind die allgemeinen Schritte für Tabellenoperationen mithilfe der .NET-Low-Level-API. 

1. Erstellen Sie eine Instance der `AmazonDynamoDBClient`-Klasse.

1. Stellen Sie den erforderlichen und optionalen Parameter für die Operation bereit, indem Sie die entsprechenden Anforderungsobjekte erstellen.

   Erstellen Sie beispielsweise ein `CreateTableRequest`-Objekt, um eine Tabelle zu erstellen und ein `QueryRequest`-Objekt, um eine Tabelle oder einen Index abzufragen. 

1. Rufen Sie die entsprechende Methode auf, die vom Client, den Sie im vorhergehenden Schritt erstellt haben, bereitgestellt wird. 

**Topics**
+ [Erstellen einer Tabelle mit einem globalen sekundären Index](#GSILowLevelDotNet.CreateTableWithIndex)
+ [Beschreiben einer Tabelle mit einem globalen sekundären Index](#GSILowLevelDotNet.DescribeTableWithIndex)
+ [Abfragen eines globalen sekundären Indexes](#GSILowLevelDotNet.QueryAnIndex)
+ [Beispiel: Globale Sekundärindizes unter Verwendung der Low-Level-API AWS SDK für .NET](GSILowLevelDotNet.Example.md)

## Erstellen einer Tabelle mit einem globalen sekundären Index
<a name="GSILowLevelDotNet.CreateTableWithIndex"></a>

Sie können globale sekundäre Indizes gleichzeitig mit der Tabelle erstellen. Zu diesem Zweck verwenden Sie `CreateTable` und geben Ihre Spezifikationen für ein oder mehrere globale sekundäre Indizes an. Das folgende C\$1-Codebeispiel erstellt eine Tabelle, die Informationen über Wetterdaten enthält. Der Partitionsschlüssel ist `Location` und der Sortierschlüssel `Date`. Ein globaler sekundärer Index mit Namen `PrecipIndex` ermöglicht einen schnellen Zugriff auf Niederschlagsdaten für verschiedene Standorte.

Im Folgenden werden die Schritte zum Erstellen einer Tabelle mit einem globalen sekundären Index mithilfe der .NET-Low-Level-API dargelegt. 

1. Erstellen Sie eine Instance der `AmazonDynamoDBClient`-Klasse.

1. Erstellen Sie eine Instance der `CreateTableRequest`-Klasse, um die Anforderungsinformationen bereitzustellen. 

   Sie müssen den Tabellennamen, seinen zugehörigen Primärschlüssel und die Werte des bereitgestellten Durchsatzes angeben. Für den globalen sekundären Index müssen Sie den Indexnamen, seine Einstellungen des bereitgestellten Durchsatzes, die Attributdefinitionen für den Index, das Schlüsselschema für den Index und die Attributprojektion angeben.

1. Führen Sie die `CreateTable`-Methode aus, indem das Anforderungsobjekt als Parameter festgelegt wird.

Im folgenden C\$1-Codebeispiel werden die vorherigen Schritte veranschaulicht. Erstellt eine Tabelle (`WeatherData`) mit einem globalen und lokalen sekundären Index (`PrecipIndex`). Der Index-Partitionsschlüssel ist `Date` und der Sortierschlüssel `Precipitation`. Alle Tabellenattribute werden in den Index projiziert. Benutzer können diesen Index abfragen, um Wetterdaten für ein bestimmtes Datum abzurufen. Optional können diese nach Niederschlagsmenge sortiert werden. 

Da es sich bei `Precipitation` um kein Schlüsselattribut für die Tabelle handelt, ist es nicht erforderlich. `WeatherData`-Elemente ohne `Precipitation` erscheinen jedoch nicht in `PrecipIndex`.

```
client = new AmazonDynamoDBClient();
string tableName = "WeatherData";

// Attribute definitions
var attributeDefinitions = new List<AttributeDefinition>()
{
    {new AttributeDefinition{
        AttributeName = "Location",
        AttributeType = "S"}},
    {new AttributeDefinition{
        AttributeName = "Date",
        AttributeType = "S"}},
    {new AttributeDefinition(){
        AttributeName = "Precipitation",
        AttributeType = "N"}
    }
};

// Table key schema
var tableKeySchema = new List<KeySchemaElement>()
{
    {new KeySchemaElement {
        AttributeName = "Location",
        KeyType = "HASH"}},  //Partition key
    {new KeySchemaElement {
        AttributeName = "Date",
        KeyType = "RANGE"}  //Sort key
    }
};

// PrecipIndex
var precipIndex = new GlobalSecondaryIndex
{
    IndexName = "PrecipIndex",
    ProvisionedThroughput = new ProvisionedThroughput
    {
        ReadCapacityUnits = (long)10,
        WriteCapacityUnits = (long)1
    },
    Projection = new Projection { ProjectionType = "ALL" }
};

var indexKeySchema = new List<KeySchemaElement> {
    {new KeySchemaElement { AttributeName = "Date", KeyType = "HASH"}},  //Partition key
    {new KeySchemaElement{AttributeName = "Precipitation",KeyType = "RANGE"}}  //Sort key
};

precipIndex.KeySchema = indexKeySchema;

CreateTableRequest createTableRequest = new CreateTableRequest
{
    TableName = tableName,
    ProvisionedThroughput = new ProvisionedThroughput
    {
        ReadCapacityUnits = (long)5,
        WriteCapacityUnits = (long)1
    },
    AttributeDefinitions = attributeDefinitions,
    KeySchema = tableKeySchema,
    GlobalSecondaryIndexes = { precipIndex }
};

CreateTableResponse response = client.CreateTable(createTableRequest);
Console.WriteLine(response.CreateTableResult.TableDescription.TableName);
Console.WriteLine(response.CreateTableResult.TableDescription.TableStatus);
```

Sie müssen warten bis DynamoDB die Tabelle erstellt und den Tabellenstatus auf `ACTIVE` setzt. Im Anschluss können Sie die Daten in der Tabelle ablegen.

## Beschreiben einer Tabelle mit einem globalen sekundären Index
<a name="GSILowLevelDotNet.DescribeTableWithIndex"></a>

Um Informationen zu globalen sekundären Indizes in einer Tabelle zu erhalten, verwenden Sie `DescribeTable`. Sie können auf den Namen, das Schlüsselschema und die projizierten Attribute von jedem Index zugreifen.

Im Folgenden werden die Schritte zum Zugriff auf globale sekundäre Index-Informationen in einer Tabelle mithilfe der .NET-Low-Level-API dargelegt. 

1. Erstellen Sie eine Instance der `AmazonDynamoDBClient`-Klasse.

1. Führen Sie die `describeTable`-Methode aus, indem das Anforderungsobjekt als Parameter festgelegt wird.

   Erstellen Sie eine Instance der `DescribeTableRequest`-Klasse, um die Anforderungsinformationen bereitzustellen. Sie müssen den Tabellennamen angeben.

Im folgenden C\$1-Codebeispiel werden die vorherigen Schritte veranschaulicht.

**Example**  

```
client = new AmazonDynamoDBClient();
string tableName = "WeatherData";

DescribeTableResponse response = client.DescribeTable(new DescribeTableRequest { TableName = tableName});

List<GlobalSecondaryIndexDescription> globalSecondaryIndexes =
response.DescribeTableResult.Table.GlobalSecondaryIndexes;

// This code snippet will work for multiple indexes, even though
// there is only one index in this example.

foreach (GlobalSecondaryIndexDescription gsiDescription in globalSecondaryIndexes) {
     Console.WriteLine("Info for index " + gsiDescription.IndexName + ":");

     foreach (KeySchemaElement kse in gsiDescription.KeySchema) {
          Console.WriteLine("\t" + kse.AttributeName + ": key type is " + kse.KeyType);
     }

      Projection projection = gsiDescription.Projection;
      Console.WriteLine("\tThe projection type is: " + projection.ProjectionType);

      if (projection.ProjectionType.ToString().Equals("INCLUDE")) {
           Console.WriteLine("\t\tThe non-key projected attributes are: "
                + projection.NonKeyAttributes);
      }
}
```

## Abfragen eines globalen sekundären Indexes
<a name="GSILowLevelDotNet.QueryAnIndex"></a>

Sie können `Query` für einen globalen sekundären Index genauso nutzen, wie Sie `Query` für eine Tabelle nutzen. Sie müssen den Indexnamen, die Abfragekriterien für den Indexpartitionsschlüssel und Sortierschlüssel (falls vorhanden) und die Attribute angeben, die Sie zurückgeben möchten. In diesem Beispiel ist der Index `PrecipIndex`, der über den Partitionsschlüssel `Date` und den Sortierschlüssel `Precipitation` verfügt. Die Indexabfrage gibt alle Wetterdaten für ein bestimmtes Datum zurück, in denen der Niederschlag größer als Null ist.

Im Folgenden werden die Schritte zur Abfrage eines globalen sekundären Indizes mithilfe der .NET-Low-Level-API dargelegt. 

1. Erstellen Sie eine Instance der `AmazonDynamoDBClient`-Klasse.

1. Erstellen Sie eine Instance der `QueryRequest`-Klasse, um die Anforderungsinformationen bereitzustellen.

1. Führen Sie die `query`-Methode aus, indem das Anforderungsobjekt als Parameter festgelegt wird.

Der Attributname `Date` ist ein DynamoDB-reserviertes Wort. Daher müssen Sie einen Ausdrucksattributnamen als Platzhalter in dem `KeyConditionExpression` verwenden.

Im folgenden C\$1-Codebeispiel werden die vorherigen Schritte veranschaulicht.

**Example**  

```
client = new AmazonDynamoDBClient();

QueryRequest queryRequest = new QueryRequest
{
    TableName = "WeatherData",
    IndexName = "PrecipIndex",
    KeyConditionExpression = "#dt = :v_date and Precipitation > :v_precip",
    ExpressionAttributeNames = new Dictionary<String, String> {
        {"#dt", "Date"}
    },
    ExpressionAttributeValues = new Dictionary<string, AttributeValue> {
        {":v_date", new AttributeValue { S =  "2013-08-01" }},
        {":v_precip", new AttributeValue { N =  "0" }}
    },
    ScanIndexForward = true
};

var result = client.Query(queryRequest);

var items = result.Items;
foreach (var currentItem in items)
{
    foreach (string attr in currentItem.Keys)
    {
        Console.Write(attr + "---> ");
        if (attr == "Precipitation")
        {
            Console.WriteLine(currentItem[attr].N);
    }
    else
    {
        Console.WriteLine(currentItem[attr].S);
    }

         }
     Console.WriteLine();
}
```

# Beispiel: Globale Sekundärindizes unter Verwendung der Low-Level-API AWS SDK für .NET
<a name="GSILowLevelDotNet.Example"></a>

Das folgende C\$1-Codebeispiel zeigt, wie Sie mit globalen sekundären Indizes arbeiten. Das Beispiel erstellt eine Tabelle mit dem Namen `Issues`, die in einem einfachen Fehlerüberwachungssystem für Softwareentwicklung verwendet werden könnte. Der Partitionsschlüssel ist `IssueId` und der Sortierschlüssel `Title`. Es gibt drei globale sekundäre Indizes in dieser Tabelle:
+ `CreateDateIndex` – Der Partitionsschlüssel ist `CreateDate` und der Sortierschlüssel `IssueId`. Zusätzlich zu den Tabellenschlüsseln werden die Attribute `Description` und `Status` in den Index projiziert.
+ `TitleIndex` – Der Partitionsschlüssel ist `Title` und der Sortierschlüssel `IssueId`. Keine anderen Attribute als die Tabellenschlüssel werden in den Index projiziert.
+ `DueDateIndex` — Der Partitionsschlüssel ist `DueDate`. Ein Sortierschlüssel ist nicht vorhanden.) Alle Tabellenattribute werden in den Index projiziert.

Nachdem die `Issues`-Tabelle erstellt wurde, lädt das Programm die Tabelle mit Daten, die Software-Fehlerberichte darstellen. Anschließend werden die Daten mithilfe der globalen sekundären Indizes abgefragt. Schließlich löscht das Programm die `Issues`-Tabelle.

 step-by-stepAnweisungen zum Testen des folgenden Beispiels finden Sie unter. [.NET-Codebeispiele](CodeSamples.DotNet.md)

**Example**  

```
using System;
using System.Collections.Generic;
using System.Linq;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;
using Amazon.DynamoDBv2.DocumentModel;
using Amazon.DynamoDBv2.Model;
using Amazon.Runtime;
using Amazon.SecurityToken;

namespace com.amazonaws.codesamples
{
    class LowLevelGlobalSecondaryIndexExample
    {
        private static AmazonDynamoDBClient client = new AmazonDynamoDBClient();
        public static String tableName = "Issues";

        public static void Main(string[] args)
        {
            CreateTable();
            LoadData();

            QueryIndex("CreateDateIndex");
            QueryIndex("TitleIndex");
            QueryIndex("DueDateIndex");

            DeleteTable(tableName);

            Console.WriteLine("To continue, press enter");
            Console.Read();
        }

        private static void CreateTable()
        {
            // Attribute definitions
            var attributeDefinitions = new List<AttributeDefinition>()
        {
            {new AttributeDefinition {
                 AttributeName = "IssueId", AttributeType = "S"
             }},
            {new AttributeDefinition {
                 AttributeName = "Title", AttributeType = "S"
             }},
            {new AttributeDefinition {
                 AttributeName = "CreateDate", AttributeType = "S"
             }},
            {new AttributeDefinition {
                 AttributeName = "DueDate", AttributeType = "S"
             }}
        };

            // Key schema for table
            var tableKeySchema = new List<KeySchemaElement>() {
            {
                new KeySchemaElement {
                    AttributeName= "IssueId",
                    KeyType = "HASH" //Partition key
                }
            },
            {
                new KeySchemaElement {
                    AttributeName = "Title",
                    KeyType = "RANGE" //Sort key
                }
            }
        };

            // Initial provisioned throughput settings for the indexes
            var ptIndex = new ProvisionedThroughput
            {
                ReadCapacityUnits = 1L,
                WriteCapacityUnits = 1L
            };

            // CreateDateIndex
            var createDateIndex = new GlobalSecondaryIndex()
            {
                IndexName = "CreateDateIndex",
                ProvisionedThroughput = ptIndex,
                KeySchema = {
                new KeySchemaElement {
                    AttributeName = "CreateDate", KeyType = "HASH" //Partition key
                },
                new KeySchemaElement {
                    AttributeName = "IssueId", KeyType = "RANGE" //Sort key
                }
            },
                Projection = new Projection
                {
                    ProjectionType = "INCLUDE",
                    NonKeyAttributes = {
                    "Description", "Status"
                }
                }
            };

            // TitleIndex
            var titleIndex = new GlobalSecondaryIndex()
            {
                IndexName = "TitleIndex",
                ProvisionedThroughput = ptIndex,
                KeySchema = {
                new KeySchemaElement {
                    AttributeName = "Title", KeyType = "HASH" //Partition key
                },
                new KeySchemaElement {
                    AttributeName = "IssueId", KeyType = "RANGE" //Sort key
                }
            },
                Projection = new Projection
                {
                    ProjectionType = "KEYS_ONLY"
                }
            };

            // DueDateIndex
            var dueDateIndex = new GlobalSecondaryIndex()
            {
                IndexName = "DueDateIndex",
                ProvisionedThroughput = ptIndex,
                KeySchema = {
                new KeySchemaElement {
                    AttributeName = "DueDate",
                    KeyType = "HASH" //Partition key
                }
            },
                Projection = new Projection
                {
                    ProjectionType = "ALL"
                }
            };



            var createTableRequest = new CreateTableRequest
            {
                TableName = tableName,
                ProvisionedThroughput = new ProvisionedThroughput
                {
                    ReadCapacityUnits = (long)1,
                    WriteCapacityUnits = (long)1
                },
                AttributeDefinitions = attributeDefinitions,
                KeySchema = tableKeySchema,
                GlobalSecondaryIndexes = {
                createDateIndex, titleIndex, dueDateIndex
            }
            };

            Console.WriteLine("Creating table " + tableName + "...");
            client.CreateTable(createTableRequest);

            WaitUntilTableReady(tableName);
        }

        private static void LoadData()
        {
            Console.WriteLine("Loading data into table " + tableName + "...");

            // IssueId, Title,
            // Description,
            // CreateDate, LastUpdateDate, DueDate,
            // Priority, Status

            putItem("A-101", "Compilation error",
                "Can't compile Project X - bad version number. What does this mean?",
                "2013-11-01", "2013-11-02", "2013-11-10",
                1, "Assigned");

            putItem("A-102", "Can't read data file",
                "The main data file is missing, or the permissions are incorrect",
                "2013-11-01", "2013-11-04", "2013-11-30",
                2, "In progress");

            putItem("A-103", "Test failure",
                "Functional test of Project X produces errors",
                "2013-11-01", "2013-11-02", "2013-11-10",
                1, "In progress");

            putItem("A-104", "Compilation error",
                "Variable 'messageCount' was not initialized.",
                "2013-11-15", "2013-11-16", "2013-11-30",
                3, "Assigned");

            putItem("A-105", "Network issue",
                "Can't ping IP address 127.0.0.1. Please fix this.",
                "2013-11-15", "2013-11-16", "2013-11-19",
                5, "Assigned");
        }

        private static void putItem(
            String issueId, String title,
            String description,
            String createDate, String lastUpdateDate, String dueDate,
            Int32 priority, String status)
        {
            Dictionary<String, AttributeValue> item = new Dictionary<string, AttributeValue>();

            item.Add("IssueId", new AttributeValue
            {
                S = issueId
            });
            item.Add("Title", new AttributeValue
            {
                S = title
            });
            item.Add("Description", new AttributeValue
            {
                S = description
            });
            item.Add("CreateDate", new AttributeValue
            {
                S = createDate
            });
            item.Add("LastUpdateDate", new AttributeValue
            {
                S = lastUpdateDate
            });
            item.Add("DueDate", new AttributeValue
            {
                S = dueDate
            });
            item.Add("Priority", new AttributeValue
            {
                N = priority.ToString()
            });
            item.Add("Status", new AttributeValue
            {
                S = status
            });

            try
            {
                client.PutItem(new PutItemRequest
                {
                    TableName = tableName,
                    Item = item
                });
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        private static void QueryIndex(string indexName)
        {
            Console.WriteLine
                ("\n***********************************************************\n");
            Console.WriteLine("Querying index " + indexName + "...");

            QueryRequest queryRequest = new QueryRequest
            {
                TableName = tableName,
                IndexName = indexName,
                ScanIndexForward = true
            };


            String keyConditionExpression;
            Dictionary<string, AttributeValue> expressionAttributeValues = new Dictionary<string, AttributeValue>();

            if (indexName == "CreateDateIndex")
            {
                Console.WriteLine("Issues filed on 2013-11-01\n");

                keyConditionExpression = "CreateDate = :v_date and begins_with(IssueId, :v_issue)";
                expressionAttributeValues.Add(":v_date", new AttributeValue
                {
                    S = "2013-11-01"
                });
                expressionAttributeValues.Add(":v_issue", new AttributeValue
                {
                    S = "A-"
                });
            }
            else if (indexName == "TitleIndex")
            {
                Console.WriteLine("Compilation errors\n");

                keyConditionExpression = "Title = :v_title and begins_with(IssueId, :v_issue)";
                expressionAttributeValues.Add(":v_title", new AttributeValue
                {
                    S = "Compilation error"
                });
                expressionAttributeValues.Add(":v_issue", new AttributeValue
                {
                    S = "A-"
                });

                // Select
                queryRequest.Select = "ALL_PROJECTED_ATTRIBUTES";
            }
            else if (indexName == "DueDateIndex")
            {
                Console.WriteLine("Items that are due on 2013-11-30\n");

                keyConditionExpression = "DueDate = :v_date";
                expressionAttributeValues.Add(":v_date", new AttributeValue
                {
                    S = "2013-11-30"
                });

                // Select
                queryRequest.Select = "ALL_PROJECTED_ATTRIBUTES";
            }
            else
            {
                Console.WriteLine("\nNo valid index name provided");
                return;
            }

            queryRequest.KeyConditionExpression = keyConditionExpression;
            queryRequest.ExpressionAttributeValues = expressionAttributeValues;

            var result = client.Query(queryRequest);
            var items = result.Items;
            foreach (var currentItem in items)
            {
                foreach (string attr in currentItem.Keys)
                {
                    if (attr == "Priority")
                    {
                        Console.WriteLine(attr + "---> " + currentItem[attr].N);
                    }
                    else
                    {
                        Console.WriteLine(attr + "---> " + currentItem[attr].S);
                    }
                }
                Console.WriteLine();
            }
        }

        private static void DeleteTable(string tableName)
        {
            Console.WriteLine("Deleting table " + tableName + "...");
            client.DeleteTable(new DeleteTableRequest
            {
                TableName = tableName
            });
            WaitForTableToBeDeleted(tableName);
        }

        private static void WaitUntilTableReady(string tableName)
        {
            string status = null;
            // Let us wait until table is created. Call DescribeTable.
            do
            {
                System.Threading.Thread.Sleep(5000); // Wait 5 seconds.
                try
                {
                    var res = client.DescribeTable(new DescribeTableRequest
                    {
                        TableName = tableName
                    });

                    Console.WriteLine("Table name: {0}, status: {1}",
                              res.Table.TableName,
                              res.Table.TableStatus);
                    status = res.Table.TableStatus;
                }
                catch (ResourceNotFoundException)
                {
                    // DescribeTable is eventually consistent. So you might
                    // get resource not found. So we handle the potential exception.
                }
            } while (status != "ACTIVE");
        }

        private static void WaitForTableToBeDeleted(string tableName)
        {
            bool tablePresent = true;

            while (tablePresent)
            {
                System.Threading.Thread.Sleep(5000); // Wait 5 seconds.
                try
                {
                    var res = client.DescribeTable(new DescribeTableRequest
                    {
                        TableName = tableName
                    });

                    Console.WriteLine("Table name: {0}, status: {1}",
                              res.Table.TableName,
                              res.Table.TableStatus);
                }
                catch (ResourceNotFoundException)
                {
                    tablePresent = false;
                }
            }
        }
    }
}
```

# Arbeiten mit globalen sekundären Indizes in DynamoDB mithilfe der AWS CLI
<a name="GCICli"></a>

Sie können die AWS CLI zum Erstellen einer Amazon-DynamoDB-Tabelle mit einem oder mehreren globalen sekundären Indizes, zum Beschreiben der Indizes in der Tabelle und zur Ausführung von Abfragen mithilfe des Indizes, verwenden.

**Topics**
+ [Erstellen einer Tabelle mit einem globalen sekundären Index](#GCICli.CreateTableWithIndex)
+ [Hinzufügen eines globalen sekundären Indexes zu einer vorhandenen Tabelle](#GCICli.CreateIndexAfterTable)
+ [Beschreiben einer Tabelle mit einem globalen sekundären Index](#GCICli.DescribeTableWithIndex)
+ [Abfragen eines globalen sekundären Indexes](#GCICli.QueryAnIndex)

## Erstellen einer Tabelle mit einem globalen sekundären Index
<a name="GCICli.CreateTableWithIndex"></a>

Globale sekundäre Indizes können gleichzeitig mit der Tabelle erstellt werden. Zu diesem Zweck verwenden Sie den `create-table`-Parameter und geben Ihre Spezifikationen für ein oder mehrere globale sekundäre Indizes an. Im folgenden Beispiel wird eine Tabelle mit dem Namen `GameScores` mit einem globalen sekundären Index `GameTitleIndex` erstellt. Die Basistabelle hat einen Partitionsschlüssel von `UserId` und einen Sortierschlüssel von `GameTitle`, mit dem Sie effizient die beste Punktzahl eines einzelnen Benutzers für ein bestimmtes Spiel finden können, während die GSI einen Partitionsschlüssel von `GameTitle` und einen Sortierschlüssel von `TopScore` hat, mit dem Sie finden Sie schnell die höchste Gesamtpunktzahl für ein bestimmtes Spiel.

```
aws dynamodb create-table \
    --table-name GameScores \
    --attribute-definitions AttributeName=UserId,AttributeType=S \
                            AttributeName=GameTitle,AttributeType=S \
                            AttributeName=TopScore,AttributeType=N  \
    --key-schema AttributeName=UserId,KeyType=HASH \
                 AttributeName=GameTitle,KeyType=RANGE \
    --provisioned-throughput ReadCapacityUnits=10,WriteCapacityUnits=5 \
    --global-secondary-indexes \
        "[
            {
                \"IndexName\": \"GameTitleIndex\",
                \"KeySchema\": [{\"AttributeName\":\"GameTitle\",\"KeyType\":\"HASH\"},
                                {\"AttributeName\":\"TopScore\",\"KeyType\":\"RANGE\"}],
                \"Projection\":{
                    \"ProjectionType\":\"INCLUDE\",
                    \"NonKeyAttributes\":[\"UserId\"]
                },
                \"ProvisionedThroughput\": {
                    \"ReadCapacityUnits\": 10,
                    \"WriteCapacityUnits\": 5
                }
            }
        ]"
```

Sie müssen warten bis DynamoDB die Tabelle erstellt und den Tabellenstatus auf `ACTIVE` setzt. Im Anschluss können Sie die Daten in der Tabelle ablegen. Mit [describe-table](https://docs.aws.amazon.com/cli/latest/reference/dynamodb/describe-table.html) können Sie den Status der Tabellenerstellung ermitteln.

## Hinzufügen eines globalen sekundären Indexes zu einer vorhandenen Tabelle
<a name="GCICli.CreateIndexAfterTable"></a>

Globale sekundäre Indizes können auch nach der Tabellenerstellung hinzugefügt oder geändert werden. Zu diesem Zweck verwenden Sie den `update-table`-Parameter und geben Ihre Spezifikationen für ein oder mehrere globale sekundäre Indizes an. Im folgenden Beispiel wird dasselbe Schema wie im vorherigen Beispiel verwendet, es wird jedoch davon ausgegangen, dass die Tabelle bereits erstellt wurde und die GSI später hinzugefügt wird.

```
aws dynamodb update-table \
    --table-name GameScores \
    --attribute-definitions AttributeName=TopScore,AttributeType=N  \
    --global-secondary-index-updates \
        "[
            {
                \"Create\": {
                    \"IndexName\": \"GameTitleIndex\",
                    \"KeySchema\": [{\"AttributeName\":\"GameTitle\",\"KeyType\":\"HASH\"},
                                    {\"AttributeName\":\"TopScore\",\"KeyType\":\"RANGE\"}],
                    \"Projection\":{
                        \"ProjectionType\":\"INCLUDE\",
                        \"NonKeyAttributes\":[\"UserId\"]
                    }
                }
            }
        ]"
```

## Beschreiben einer Tabelle mit einem globalen sekundären Index
<a name="GCICli.DescribeTableWithIndex"></a>

Um Informationen zu globalen sekundären Indizes in einer Tabelle zu erhalten, verwenden Sie den Parameter `describe-table`. Sie können auf den Namen, das Schlüsselschema und die projizierten Attribute von jedem Index zugreifen.

```
aws dynamodb describe-table --table-name GameScores
```

## Abfragen eines globalen sekundären Indexes
<a name="GCICli.QueryAnIndex"></a>

Sie können die Operation `query` für einen globalen sekundären Index genauso nutzen, wie Sie `query` für eine Tabelle nutzen. Sie müssen den Indexnamen, die Abfragekriterien für den Indexsortierschlüssel und die Attribute angeben, die Sie zurückgeben möchten. In diesem Beispiel ist der Index `GameTitleIndex` und der Indexsortierschlüssel `GameTitle`.

Die einzigen zurückgegebenen Attribute sind die, die in den Index projiziert wurden. Sie könnten diese Abfrage ändern, um auch Nicht-Schlüsselattribute auszuwählen, aber dies würde eine Tabellenabrufaktivität erfordern, die relativ teuer ist. Weitere Informationen zum Abrufen von Tabellen finden Sie unter [Attributprojektionen](GSI.md#GSI.Projections).

```
aws dynamodb query --table-name GameScores\
    --index-name GameTitleIndex \
    --key-condition-expression "GameTitle = :v_game" \
    --expression-attribute-values '{":v_game":{"S":"Alien Adventure"} }'
```