

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 管理索引時，關聯式 (SQL) 資料庫與 DynamoDB 的差異
<a name="SQLtoNoSQL.Indexes"></a>

索引提供您替代的查詢模式，可加速查詢。本節會比較及比對 SQL 及 Amazon DynamoDB 中索引的建立及使用。

無論您是使用關聯式資料庫或 DynamoDB，都應明智地建立索引。每當寫入資料表時，資料表的所有索引都必須更新。在大型資料表的大量寫入環境中，這可能會使用大量系統資源。在唯讀或大部分讀取的環境中，這不算是問題。但是，您應確認應用程式確實使用了索引，而非只是占用空間。

**Topics**
+ [建立索引時，關聯式 (SQL) 資料庫與 DynamoDB 的差異](#SQLtoNoSQL.Indexes.Creating)
+ [查詢與掃描索引時，關聯式 (SQL) 資料庫與 DynamoDB 的差異](#SQLtoNoSQL.Indexes.QueryAndScan)

## 建立索引時，關聯式 (SQL) 資料庫與 DynamoDB 的差異
<a name="SQLtoNoSQL.Indexes.Creating"></a>

比較 SQL 中的 `CREATE INDEX` 陳述式及 Amazon DynamoDB 中的 `UpdateTable` 操作。

**Topics**
+ [使用 SQL 建立索引](#SQLtoNoSQL.Indexes.Creating.SQL)
+ [在 DynamoDB 中建立索引](#SQLtoNoSQL.Indexes.Creating.DynamoDB)

### 使用 SQL 建立索引
<a name="SQLtoNoSQL.Indexes.Creating.SQL"></a>

在關聯式資料庫中，索引是一種資料結構，可讓您快速查詢資料表中的不同資料行。您可以使用 `CREATE INDEX` SQL 陳述式對現有的資料表新增索引，並指定要編製索引的資料行。在索引建立後，您可以如常地查詢資料表中的資料，但資料庫現在已可使用索引在快速尋找資料表中的指定資料列，而不用掃描整個資料表。

在您建立索引後，資料庫會為您維護。每當您修改資料表中的資料時，索引就會自動修改，以反映資料表的變更。

在 MySQL 中，您會建立如下的索引。

```
CREATE INDEX GenreAndPriceIndex
ON Music (genre, price);
```

### 在 DynamoDB 中建立索引
<a name="SQLtoNoSQL.Indexes.Creating.DynamoDB"></a>

在 DynamoDB 中，您可以建立及使用*次要索引*來達到類似目的。

DynamoDB 中的索引與關聯式資料庫中的索引不同。在您建立次要索引時，必須指定其索引鍵屬性，即一個分割區索引鍵和一個排序索引鍵。在建立次要索引後，您便可以 `Query` 或 `Scan` 該索引，正如您對資料表所做的那樣。DynamoDB 沒有查詢最佳化程式，因此只有在您 `Query` 或 `Scan` 次要索引時才會使用此類索引。

DynamoDB 支援兩種不同的索引：
+ 全域次要索引：索引的主索引鍵可以是資料表中任兩個屬性。
+ 本機次要索引：索引的分割區索引鍵必須與資料表的分割區索引鍵相同。但是，排序索引鍵可以是其他任何屬性。

DynamoDB 會確認次要索引中的資料最終與其資料表一致。您可以對資料表或本機次要索引請求高度一致的 `Query` 或 `Scan` 操作。但是，全域次要索引只支援最終一致性。

您可以使用 `UpdateTable` 操作並指定 `GlobalSecondaryIndexUpdates`，對現有的資料表新增全域次要索引。

```
{
    TableName: "Music",
    AttributeDefinitions:[
        {AttributeName: "Genre", AttributeType: "S"},
        {AttributeName: "Price", AttributeType: "N"}
    ],
    GlobalSecondaryIndexUpdates: [
        {
            Create: {
                IndexName: "GenreAndPriceIndex",
                KeySchema: [
                    {AttributeName: "Genre", KeyType: "HASH"}, //Partition key
                    {AttributeName: "Price", KeyType: "RANGE"}, //Sort key
                ],
                Projection: {
                    "ProjectionType": "ALL"
                },
                ProvisionedThroughput: {                                // Only specified if using provisioned mode
                    "ReadCapacityUnits": 1,"WriteCapacityUnits": 1
                }
            }
        }
    ]
}
```

您必須提供下列參數給 `UpdateTable`：
+ `TableName`：會與索引相關聯的資料表。
+ `AttributeDefinitions`：索引之索引鍵結構描述屬性的資料類型。
+ `GlobalSecondaryIndexUpdates`：您想要建立之索引的相關詳細資訊：
  + `IndexName`：索引的名稱。
  + `KeySchema`：用於索引主索引鍵的屬性。
  + `Projection`：資料表中複製到索引的屬性。在此案例中，`ALL` 表示會複製所有屬性。
  + `ProvisionedThroughput (for provisioned tables)`：此索引所需的每秒讀取及寫入數。(這和資料表的佈建輸送量設定是分開的。) 

部分此操作會涉及將資料表中的資料回填到新的索引。在回填過程中，資料表仍可使用。但是，在索引的 `Backfilling` 屬性從 true 變更為 false 之前，索引都尚未就緒。您可以使用 `DescribeTable` 操作來檢視此屬性。

## 查詢與掃描索引時，關聯式 (SQL) 資料庫與 DynamoDB 的差異
<a name="SQLtoNoSQL.Indexes.QueryAndScan"></a>

比較在 SQL 中使用 SELECT 陳述式及在 Amazon DynamoDB 中使用 `Query` 及 `Scan` 操作查詢及掃描索引。

**Topics**
+ [在 SQL 中查詢及掃描索引](#SQLtoNoSQL.Indexes.QueryAndScan.SQL)
+ [在 DynamoDB 中查詢及掃描索引](#SQLtoNoSQL.Indexes.QueryAndScan.DynamoDB)

### 在 SQL 中查詢及掃描索引
<a name="SQLtoNoSQL.Indexes.QueryAndScan.SQL"></a>

在關聯式資料庫中，您不會直接使用索引。而是透過發出 `SELECT` 陳述式查詢資料表，然後查詢最佳化器就可使用任何索引。

*查詢最佳化器*是一項關聯式資料庫管理系統 (RDBMS) 元件，可評估可用的索引，並判斷是否可用其加速查詢。若索引可用來加速查詢，RDBMS 便會先存取索引，然後用它尋找資料表中的資料。

以下為一些可使用 *GenreAndPriceIndex* 改善效能的 SQL 陳述式。我們假設 *Music* 資料表具有足夠的資料，可讓查詢最佳化器決定使用此索引，而非單純地掃描整個資料表。

```
/* All of the rock songs */

SELECT * FROM Music
WHERE Genre = 'Rock';
```

```
/* All of the cheap country songs */

SELECT Artist, SongTitle, Price FROM Music
WHERE Genre = 'Country' AND Price < 0.50;
```

### 在 DynamoDB 中查詢及掃描索引
<a name="SQLtoNoSQL.Indexes.QueryAndScan.DynamoDB"></a>

在 DynamoDB 中，您可以直接在索引上執行 `Query` 和 `Scan` 操作，與您在資料表上的操作方式相同。您可以使用 DynamoDB API 或 [PartiQL](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.html) (SQL 相容查詢語言) 來查詢或掃描索引。您必須同時指定 `TableName` 和 `IndexName`。

下列是關於 DynamoDB 中 *GenreAndPriceIndex* 的部分查詢。(此索引的索引鍵結構描述包含 *Genre* 和 *Price*)。

------
#### [ DynamoDB API ]

```
// All of the rock songs

{
    TableName: "Music",
    IndexName: "GenreAndPriceIndex",
    KeyConditionExpression: "Genre = :genre",
    ExpressionAttributeValues: {
        ":genre": "Rock"
    },
};
```

此範例使用 `ProjectionExpression` 指出您只想要在結果中顯示部分屬性，而非所有屬性。

```
// All of the cheap country songs

{
    TableName: "Music",
    IndexName: "GenreAndPriceIndex",
    KeyConditionExpression: "Genre = :genre and Price < :price",
    ExpressionAttributeValues: {
        ":genre": "Country",
        ":price": 0.50
    },
    ProjectionExpression: "Artist, SongTitle, Price"
};
```

下列是對 *GenreAndPriceIndex* 執行的掃描。

```
// Return all of the data in the index

{
    TableName:  "Music",
    IndexName: "GenreAndPriceIndex"
}
```

------
#### [ PartiQL for DynamoDB ]

使用 PartiQL，您可以使用 PartiQL `Select` 陳述式對索引執行查詢和掃描。

```
// All of the rock songs

SELECT * 
FROM Music.GenreAndPriceIndex
WHERE Genre = 'Rock'
```

```
// All of the cheap country songs

SELECT * 
FROM Music.GenreAndPriceIndex
WHERE Genre = 'Rock' AND Price < 0.50
```

下列是對 *GenreAndPriceIndex* 執行的掃描。

```
// Return all of the data in the index

SELECT *
FROM Music.GenreAndPriceIndex
```

**注意**  
如需使用 `Select` 的程式碼範例，請參閱 [適用於 DynamoDB 的 PartiQL Select 陳述式](ql-reference.select.md)。

------