

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

# 在 Amazon QLDB 中使用資料和歷史記錄
<a name="working-with-data"></a>

**重要**  
支援終止通知：現有客戶將可以使用 Amazon QLDB，直到 07/31/2025 的支援結束為止。如需詳細資訊，請參閱[將 Amazon QLDB Ledger 遷移至 Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/)。

下列主題提供*建立、讀取、更新和刪除* (CRUD) 陳述式的基本範例。您可以使用 [QLDB 主控台](console_QLDB.md)上的 *PartiQL 編輯器*或 [QLDB shell](data-shell.md) 手動執行這些陳述式。本指南也會逐步解說 QLDB 在您變更分類帳時如何處理您的資料。

QLDB 支援 [PartiQL](https://partiql.org/) 查詢語言。

如需示範如何使用 QLDB 驅動程式以程式設計方式執行類似陳述式的程式碼範例，請參閱 中的教學課程[驅動程式入門](getting-started-driver.md)。

**提示**  
以下是在 QLDB 中使用 PartiQL 的提示和最佳實務的簡短摘要：  
**了解並行和交易限制** – 所有陳述式，包括`SELECT`查詢，都受到[樂觀並行控制 (OCC)](concurrency.md) 衝突和[交易限制](limits.md#limits.fixed)的約束，包括 30 秒的交易逾時。
**使用索引** – 使用高基數索引並執行目標查詢，以最佳化您的陳述式並避免完整資料表掃描。如需進一步了解，請參閱 [最佳化查詢效能](working.optimize.md)。
**使用等式述詞** – 索引查詢需要*等式*運算子 (`=` 或 `IN`)。不平等運算子 (`<`、`LIKE`、、`BETWEEN`) `>`不符合索引查詢資格，並導致完整資料表掃描。
**僅使用內部聯結** – QLDB 僅支援內部聯結。最佳實務是，加入針對您加入的每個資料表編製索引的欄位。選擇聯結條件和等式述詞的高基數索引。

**Topics**
+ [使用索引建立資料表並插入文件](working.create.md)
+ [查詢您的資料](working.userdata.md)
+ [查詢文件中繼資料](working.metadata.md)
+ [使用 BY 子句查詢文件 ID](working.metadata.by-clause.md)
+ [更新和刪除文件](working.revisions.md)
+ [查詢修訂歷史記錄](working.history.md)
+ [編輯文件修訂](working.redaction.md)
+ [最佳化查詢效能](working.optimize.md)
+ [取得 PartiQL 陳述式統計資料](working.statement-stats.md)
+ [查詢系統目錄](working.catalog.md)
+ [管理資料表](working.manage-tables.md)
+ [管理索引](working.manage-indexes.md)
+ [Amazon QLDB 中的唯一 IDs](working.unique-id.md)

# 使用索引建立資料表並插入文件
<a name="working.create"></a>

**重要**  
支援終止通知：現有客戶將可以使用 Amazon QLDB，直到 07/31/2025 的支援結束為止。如需詳細資訊，請參閱[將 Amazon QLDB Ledger 遷移至 Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/)。

建立 Amazon QLDB 分類帳後，您的第一個步驟是建立具有基本[CREATE TABLE](ql-reference.create-table.md)陳述式的資料表。資料表包含 [QLDB 文件](ql-reference.docs.md)，這是 [Amazon Ion](ion.md) `struct` 格式的資料集。

**Topics**
+ [建立資料表和索引](#working.create.tables-indexes)
+ [插入文件](#working.create.insert)

## 建立資料表和索引
<a name="working.create.tables-indexes"></a>

資料表具有簡單、區分大小寫的名稱，沒有命名空間。QLDB 支援開啟的內容，不會強制執行結構描述，因此您在建立資料表時不會定義屬性或資料類型。

```
CREATE TABLE VehicleRegistration
```

```
CREATE TABLE Vehicle
```

`CREATE TABLE` 陳述式會傳回新資料表的系統指派 ID。QLDB 中所有[系統指派IDs](working.unique-id.md) 都是通用的唯一識別符 (UUID)，每個識別符都以 Base62-encoded字串表示。

**注意**  
或者，您可以在建立資料表時定義資料表資源的標籤。如要瞭解如何作業，請參閱[建立時標記資料表](working.manage-tables.md#working.manage-tables.tags)。

您也可以在資料表上建立索引，以最佳化查詢效能。

```
CREATE INDEX ON VehicleRegistration (VIN)
```

```
CREATE INDEX ON VehicleRegistration (LicensePlateNumber)
```

```
CREATE INDEX ON Vehicle (VIN)
```

**重要**  
QLDB 需要索引才能有效率地查詢文件。如果沒有索引，QLDB 讀取文件時需要執行完整資料表掃描。這可能會導致大型資料表的效能問題，包括並行衝突和交易逾時。  
若要避免資料表掃描，您必須在索引欄位或文件 ID 上使用*等式*運算子 (`=` 或 `IN`) 搭配`WHERE`述詞子句執行陳述式。如需詳細資訊，請參閱[最佳化查詢效能](working.optimize.md)。

建立索引時請注意下列限制：
+ 索引只能在單一最上層欄位上建立。不支援複合、巢狀、唯一和以函數為基礎的索引。
+ 您可以在任何 [Ion 資料類型](ql-reference.data-types.md)上建立索引，包括 `list`和 `struct`。不過，無論 Ion 類型為何，您都只能依整個 Ion 值的相等性執行索引查詢。例如，使用 `list` 類型做為索引時，您無法透過清單中的一個項目進行索引查詢。
+ 只有在您使用等式述詞時，查詢效能才會獲得改善；例如， `WHERE indexedField = 123`或 `WHERE indexedField IN (456, 789)`。

  QLDB 不會遵守查詢述詞中的不等式。因此，不會實作範圍篩選掃描。
+ 索引欄位的名稱區分大小寫，最多可有 128 個字元。
+ QLDB 中的索引建立是非同步的。在非空白資料表上完成建立索引所需的時間，會因資料表大小而有所不同。如需詳細資訊，請參閱[管理索引](working.manage-indexes.md)。

## 插入文件
<a name="working.create.insert"></a>

然後，您可以將文件插入資料表。QLDB 文件會以 Amazon Ion 格式儲存。下列 PartiQL [INSERT](ql-reference.insert.md)陳述式包含 中使用的車輛註冊範例資料子集[Amazon QLDB 主控台入門](getting-started.md)。

```
INSERT INTO VehicleRegistration
<< {
    'VIN' : '1N4AL11D75C109151',
    'LicensePlateNumber' : 'LEWISR261LL',
    'State' : 'WA',
    'City' : 'Seattle',
    'PendingPenaltyTicketAmount' : 90.25,
    'ValidFromDate' : `2017-08-21T`,
    'ValidToDate' : `2020-05-11T`,
    'Owners' : {
        'PrimaryOwner' : { 'PersonId' : '294jJ3YUoH1IEEm8GSabOs' },
        'SecondaryOwners' : [ { 'PersonId' : '5Ufgdlnj06gF5CWcOIu64s' } ]
    }
},
{
    'VIN' : 'KM8SRDHF6EU074761',
    'LicensePlateNumber' : 'CA762X',
    'State' : 'WA',
    'City' : 'Kent',
    'PendingPenaltyTicketAmount' : 130.75,
    'ValidFromDate' : `2017-09-14T`,
    'ValidToDate' : `2020-06-25T`,
    'Owners' : {
        'PrimaryOwner' : { 'PersonId': 'IN7MvYtUjkp1GMZu0F6CG9' },
        'SecondaryOwners' : []
    }
} >>
```

```
INSERT INTO Vehicle
<< {
    'VIN' : '1N4AL11D75C109151',
    'Type' : 'Sedan',
    'Year' : 2011,
    'Make' : 'Audi',
    'Model' : 'A5',
    'Color' : 'Silver'
} ,
{
    'VIN' : 'KM8SRDHF6EU074761',
    'Type' : 'Sedan',
    'Year' : 2015,
    'Make' : 'Tesla',
    'Model' : 'Model S',
    'Color' : 'Blue'
} >>
```

**PartiQL 語法和語義**
+ 欄位名稱以單引號括住 (`'...'`)。
+ 字串值也以單引號括住 (`'...'`)。
+ 時間戳記以反引號括住 (``...``)。後端可用於表示任何 Ion 常值。
+ 整數和小數是不需要表示的常值。

如需 PartiQL 語法和語義的詳細資訊，請參閱 [在 Amazon QLDB 中使用 PartiQL 查詢離子](ql-reference.query.md)。

`INSERT` 陳述式會建立版本編號為零的文件初始修訂。為了唯一識別每個文件，QLDB 會指派*文件 ID* 做為中繼資料的一部分。插入陳述式會傳回每個插入文件的 ID。

**重要**  
由於 QLDB 不會強制執行結構描述，因此您可以將相同的文件多次插入資料表。每個插入陳述式都會將個別的文件項目遞交至日誌，QLDB 會為每個文件指派唯一的 ID。

若要了解如何查詢您插入資料表的文件，請繼續 [查詢您的資料](working.userdata.md)。

# 查詢您的資料
<a name="working.userdata"></a>

**重要**  
支援終止通知：現有客戶將可以使用 Amazon QLDB，直到 07/31/2025 的支援結束為止。如需詳細資訊，請參閱[將 Amazon QLDB Ledger 遷移至 Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/)。

*使用者檢視*只會傳回使用者資料的最新未刪除修訂。這是 Amazon QLDB 中的預設檢視。這表示當您只想查詢資料時，不需要特殊限定詞。

如需下列查詢範例語法和參數的詳細資訊，請參閱《*Amazon QLDB PartiQL 參考*[SELECT](ql-reference.select.md)》中的 。

**Topics**
+ [基本查詢](#working.userdata.basic)
+ [投影和篩選條件](#working.userdata.projections-filters)
+ [聯結](#working.userdata.joins)
+ [巢狀資料](#working.userdata.nested)

## 基本查詢
<a name="working.userdata.basic"></a>

基本`SELECT`查詢會傳回您插入資料表的文件。

**警告**  
當您在沒有索引查詢的情況下在 QLDB 中執行查詢時，它會叫用完整資料表掃描。PartiQL 支援此類查詢，因為它與 SQL 相容。不過，*請勿*在 QLDB 中執行生產使用案例的資料表掃描。資料表掃描可能會導致大型資料表的效能問題，包括並行衝突和交易逾時。  
若要避免資料表掃描，您必須在索引欄位或文件 ID 上使用*等式*運算子，以`WHERE`述詞子句執行陳述式；例如， `WHERE indexedField = 123`或 `WHERE indexedField IN (456, 789)`。如需詳細資訊，請參閱[最佳化查詢效能](working.optimize.md)。

下列查詢顯示您先前插入 的車輛註冊文件結果[使用索引建立資料表並插入文件](working.create.md)。結果的順序並不特定，而且每個`SELECT`查詢可能有所不同。您不應依賴結果順序來查詢 QLDB 中的任何查詢。

```
SELECT * FROM VehicleRegistration
WHERE LicensePlateNumber IN ('LEWISR261LL', 'CA762X')
```

```
{
    VIN: "1N4AL11D75C109151",
    LicensePlateNumber: "LEWISR261LL",
    State: "WA",
    City: "Seattle",
    PendingPenaltyTicketAmount: 90.25,
    ValidFromDate: 2017-08-21T,
    ValidToDate: 2020-05-11T,
    Owners: {
        PrimaryOwner: { PersonId: "294jJ3YUoH1IEEm8GSabOs" },
        SecondaryOwners: [{ PersonId: "5Ufgdlnj06gF5CWcOIu64s" }]
    }
},
{
    VIN: "KM8SRDHF6EU074761",
    LicensePlateNumber: "CA762X",
    State: "WA",
    City: "Kent",
    PendingPenaltyTicketAmount: 130.75,
    ValidFromDate: 2017-09-14T,
    ValidToDate: 2020-06-25T,
    Owners: {
        PrimaryOwner: { PersonId: "IN7MvYtUjkp1GMZu0F6CG9" },
        SecondaryOwners: []
    }
}
```

```
SELECT * FROM Vehicle
WHERE VIN IN ('1N4AL11D75C109151', 'KM8SRDHF6EU074761')
```

```
{
    VIN: "1N4AL11D75C109151",
    Type: "Sedan",
    Year: 2011,
    Make: "Audi",
    Model: "A5",
    Color: "Silver"
},
{
    VIN: "KM8SRDHF6EU074761",
    Type: "Sedan",
    Year: 2015,
    Make: "Tesla",
    Model: "Model S",
    Color: "Blue"
}
```

**重要**  
在 PartiQL 中，您可以使用單引號來表示資料處理語言 (DML) 或查詢陳述式中的字串。但是，QLDB 主控台和 QLDB shell 傳回查詢會以 Amazon Ion 文字格式產生，因此您會看到以雙引號括住的字串。  
此語法允許 PartiQL 查詢語言維持 SQL 相容性，而 Amazon Ion 文字格式則維持 JSON 相容性。

## 投影和篩選條件
<a name="working.userdata.projections-filters"></a>

您可以執行投影 （目標 `SELECT`) 和其他標準篩選條件 (`WHERE` 子句）。下列查詢會從`VehicleRegistration`資料表傳回文件欄位的子集。它會針對符合下列條件的車輛進行篩選條件：
+ **字串篩選條件** – 已在西雅圖註冊。
+ **小數篩選條件** – 其待定的懲罰票證金額小於 `100.0`。
+ **日期篩選條件** – 其註冊日期在 2019 年 9 月 4 日或之後有效。

```
SELECT r.VIN, r.PendingPenaltyTicketAmount, r.Owners
FROM VehicleRegistration AS r
WHERE r.VIN IN ('1N4AL11D75C109151', 'KM8SRDHF6EU074761')
AND r.City = 'Seattle' --string
AND r.PendingPenaltyTicketAmount < 100.0 --decimal
AND r.ValidToDate >= `2019-09-04T` --timestamp with day precision
```

```
{
    VIN: "1N4AL11D75C109151",
    PendingPenaltyTicketAmount: 90.25,
    Owners: {
        PrimaryOwner: { PersonId: "294jJ3YUoH1IEEm8GSabOs" },
        SecondaryOwners: [{ PersonId: "5Ufgdlnj06gF5CWcOIu64s" }]
    }
}
```

## 聯結
<a name="working.userdata.joins"></a>

您也可以撰寫內部聯結查詢。下列範例顯示隱含內部聯結查詢，其會傳回所有註冊文件以及已註冊車輛的屬性。

```
SELECT * FROM VehicleRegistration AS r, Vehicle AS v
WHERE r.VIN = v.VIN
AND r.VIN IN ('1N4AL11D75C109151', 'KM8SRDHF6EU074761')
```

```
{
    VIN: "1N4AL11D75C109151",
    LicensePlateNumber: "LEWISR261LL",
    State: "WA",
    City: "Seattle",
    PendingPenaltyTicketAmount: 90.25,
    ValidFromDate: 2017-08-21T,
    ValidToDate: 2020-05-11T,
    Owners: {
        PrimaryOwner: { PersonId: "294jJ3YUoH1IEEm8GSabOs" },
        SecondaryOwners: [{ PersonId: "5Ufgdlnj06gF5CWcOIu64s" }]
    },
    Type: "Sedan",
    Year: 2011,
    Make: "Audi",
    Model: "A5",
    Color: "Silver"
},
{
    VIN: "KM8SRDHF6EU074761",
    LicensePlateNumber: "CA762X",
    State: "WA",
    City: "Kent",
    PendingPenaltyTicketAmount: 130.75,
    ValidFromDate: 2017-09-14T,
    ValidToDate: 2020-06-25T,
    Owners: {
        PrimaryOwner: { PersonId: "IN7MvYtUjkp1GMZu0F6CG9" },
        SecondaryOwners: []
    },
    Type: "Sedan",
    Year: 2015,
    Make: "Tesla",
    Model: "Model S",
    Color: "Blue"
}
```

或者，您可以在明確語法中寫入相同的內部聯結查詢，如下所示。

```
SELECT * FROM VehicleRegistration AS r INNER JOIN Vehicle AS v
ON r.VIN = v.VIN
WHERE r.VIN IN ('1N4AL11D75C109151', 'KM8SRDHF6EU074761')
```

## 巢狀資料
<a name="working.userdata.nested"></a>

您可以使用 QLDB 中的 PartiQL 來查詢文件中的巢狀資料。下列範例顯示可扁平化巢狀資料的關聯子查詢。這裡的`@`字元技術上是選用的。但它明確表示您想要 中的`Owners`結構`VehicleRegistration`，而不是名為 的不同集合 `Owners`（如果有的話）。

```
SELECT 
    r.VIN, 
    o.SecondaryOwners
FROM
    VehicleRegistration AS r, @r.Owners AS o
WHERE
    r.VIN IN ('1N4AL11D75C109151', 'KM8SRDHF6EU074761')
```

```
{
    VIN: "1N4AL11D75C109151",
    SecondaryOwners: [{ PersonId: "5Ufgdlnj06gF5CWcOIu64s" }]
},
{
    VIN: "KM8SRDHF6EU074761",
    SecondaryOwners: []
}
```

以下顯示`SELECT`清單中的子查詢，除了內部聯結之外，還會投影巢狀資料。

```
SELECT 
    v.Make, 
    v.Model, 
    (SELECT VALUE o.PrimaryOwner.PersonId FROM @r.Owners AS o) AS PrimaryOwner
FROM 
    VehicleRegistration AS r, Vehicle AS v
WHERE 
    r.VIN = v.VIN AND r.VIN IN ('1N4AL11D75C109151', 'KM8SRDHF6EU074761')
```

```
{
    Make: "Audi",
    Model: "A5",
    PrimaryOwner: ["294jJ3YUoH1IEEm8GSabOs"]
},
{
    Make: "Tesla",
    Model: "Model S",
    PrimaryOwner: ["IN7MvYtUjkp1GMZu0F6CG9"]
}
```

下列查詢會傳回 `VehicleRegistration` 文件`Owners.SecondaryOwners`清單中每個人的 `PersonId`和索引 （一般） 編號。

```
SELECT s.PersonId, owner_idx
FROM VehicleRegistration AS r, @r.Owners.SecondaryOwners AS s AT owner_idx
WHERE r.VIN = '1N4AL11D75C109151'
```

```
{
    PersonId: "5Ufgdlnj06gF5CWcOIu64s",
    owner_idx: 0
}
```

若要了解如何查詢文件中繼資料，請繼續 [查詢文件中繼資料](working.metadata.md)。

# 查詢文件中繼資料
<a name="working.metadata"></a>

**重要**  
支援終止通知：現有客戶將可以使用 Amazon QLDB，直到 07/31/2025 的支援結束為止。如需詳細資訊，請參閱[將 Amazon QLDB Ledger 遷移至 Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/)。

`INSERT` 陳述式會建立版本編號為零的文件初始修訂。為了唯一識別每個文件，Amazon QLDB 會指派*文件 ID* 做為中繼資料的一部分。

除了文件 ID 和版本編號之外，QLDB 還會將每個文件的其他系統產生的中繼資料存放在資料表中。此中繼資料包括交易資訊、日誌屬性和文件的雜湊值。

所有系統指派IDs 都是通用的唯一識別符 (UUID)，每個識別符都以 Base62-encoded字串表示。如需詳細資訊，請參閱[Amazon QLDB 中的唯一 IDs](working.unique-id.md)。

**Topics**
+ [遞交檢視](#working.metadata.committed)
+ [加入遞交和使用者檢視](#working.metadata.committed-joins)

## 遞交檢視
<a name="working.metadata.committed"></a>

您可以透過查詢*遞交的檢視*來存取文件中繼資料。此檢視會從系統定義的資料表傳回直接對應至使用者資料表的文件。它包含資料和系統產生的中繼資料的最新遞交、未刪除的修訂。若要查詢此檢視，請將 字首新增至查詢中的`_ql_committed_`資料表名稱。（字首`_ql_`保留在 QLDB 中供系統物件使用。)

```
SELECT * FROM _ql_committed_VehicleRegistration AS r
WHERE r.data.VIN IN ('1N4AL11D75C109151', 'KM8SRDHF6EU074761')
```

使用先前插入 的資料[使用索引建立資料表並插入文件](working.create.md)，此查詢的輸出會顯示每個未刪除文件最新修訂的系統內容。系統文件的中繼資料巢狀在 `metadata` 欄位中，而您的使用者資料巢狀在 `data`欄位中。

```
{
    blockAddress:{
        strandId:"JdxjkR9bSYB5jMHWcI464T",
        sequenceNo:14
    },
    hash:{{wCsmM6qD4STxz0WYmE+47nZvWtcCz9D6zNtCiM5GoWg=}},
    data:{
        VIN: "1N4AL11D75C109151",
        LicensePlateNumber: "LEWISR261LL",
        State: "WA",
        City: "Seattle",
        PendingPenaltyTicketAmount: 90.25,
        ValidFromDate: 2017-08-21T,
        ValidToDate: 2020-05-11T,
        Owners: {
            PrimaryOwner: { PersonId: "294jJ3YUoH1IEEm8GSabOs" },
            SecondaryOwners: [{ PersonId: "5Ufgdlnj06gF5CWcOIu64s" }]
        }
    },
    metadata:{
        id:"3Qv67yjXEwB9SjmvkuG6Cp",
        version:0,
        txTime:2019-06-05T20:53:321d-3Z,
        txId:"HgXAkLjAtV0HQ4lNYdzX60"
    }
},
{
    blockAddress:{
        strandId:"JdxjkR9bSYB5jMHWcI464T",
        sequenceNo:14
    },
    hash:{{wPuwH60TtcCvg/23BFp+redRXuCALkbDihkEvCX22Jk=}},
    data:{
        VIN: "KM8SRDHF6EU074761",
        LicensePlateNumber: "CA762X",
        State: "WA",
        City: "Kent",
        PendingPenaltyTicketAmount: 130.75,
        ValidFromDate: 2017-09-14T,
        ValidToDate: 2020-06-25T,
        Owners: {
            PrimaryOwner: { PersonId: "IN7MvYtUjkp1GMZu0F6CG9" },
            SecondaryOwners: []
        }
    },
    metadata:{
        id:"JOzfB3lWqGU727mpPeWyxg",
        version:0,
        txTime:2019-06-05T20:53:321d-3Z,
        txId:"HgXAkLjAtV0HQ4lNYdzX60"
    }
}
```

**遞交的檢視欄位**
+ `blockAddress` – 您分類帳日誌中文件修訂遞交的區塊位置。可用於密碼編譯驗證的地址有兩個欄位。
  + `strandId` – 包含 區塊之日誌鏈的唯一 ID。
  + `sequenceNo` – 索引號碼，指定 鏈中區塊的位置。
**注意**  
此範例中的兩份文件都具有與`blockAddress`相同 相同的 `sequenceNo`。由於這些文件是插入單一交易中 （在此案例中是單一陳述式中），因此它們被遞交至相同的區塊。
+ `hash` – 唯一代表文件修訂的 SHA-256 Ion 雜湊值。雜湊涵蓋修訂版 `data` 和 `metadata` 欄位，可用於[密碼編譯驗證](verification.md)。
+ `data` – 文件的使用者資料屬性。

  如果您修訂修訂，則此`data`結構會替換為`dataHash`欄位，其值為已移除`data`結構的 Ion 雜湊。
+ `metadata` – 文件的中繼資料屬性。
  + `id` – 文件的系統指派唯一 ID。
  + `version` – 文件的版本編號。這是以零為基礎的整數，會隨著每個文件修訂而遞增。
  + `txTime` – 文件修訂遞交至日誌時的時間戳記。
  + `txId` – 遞交文件修訂之交易的唯一 ID。

## 加入遞交和使用者檢視
<a name="working.metadata.committed-joins"></a>

您可以在遞交檢視中，使用使用者檢視中的資料表撰寫聯結資料表的查詢。例如，您可能想要將`id`一個資料表的文件與另一個資料表的使用者定義欄位聯結。

下列查詢會使用後者的承諾檢視，分別在 `PersonId`和 文件`id`欄位`Person`上聯結兩個名為 `DriversLicense`和 的資料表。

```
SELECT * FROM DriversLicense AS d INNER JOIN _ql_committed_Person AS p
ON d.PersonId = p.metadata.id
WHERE p.metadata.id = '1CWScY2qHYI9G88C2SjvtH'
```

若要了解如何在預設使用者檢視中查詢文件 ID 欄位，請繼續 [使用 BY 子句查詢文件 ID](working.metadata.by-clause.md)。

# 使用 BY 子句查詢文件 ID
<a name="working.metadata.by-clause"></a>

**重要**  
支援終止通知：現有客戶將可以使用 Amazon QLDB，直到 07/31/2025 的支援結束為止。如需詳細資訊，請參閱[將 Amazon QLDB Ledger 遷移至 Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/)。

雖然您可以定義旨在成為唯一識別符的欄位 （例如，車輛的 VIN)，但文件的真正唯一識別符是`id`中繼資料欄位，如中所述[插入文件](working.create.md#working.create.insert)。因此，您可以使用 `id` 欄位來建立資料表之間的關係。

文件`id`欄位只能在遞交檢視中直接存取，但您也可以使用 `BY`子句在預設使用者檢視中投影。如需範例，請參閱下列查詢及其結果。

```
SELECT r_id, r.VIN, r.LicensePlateNumber, r.State, r.City, r.Owners
FROM VehicleRegistration AS r BY r_id
WHERE r_id = '3Qv67yjXEwB9SjmvkuG6Cp'
```

```
{
    r_id: "3Qv67yjXEwB9SjmvkuG6Cp",
    VIN: "1N4AL11D75C109151",
    LicensePlateNumber: "LEWISR261LL",
    State: "WA",
    City: "Seattle",
    Owners: {
        PrimaryOwner: { PersonId: "294jJ3YUoH1IEEm8GSabOs" },
        SecondaryOwners: [{ PersonId: "5Ufgdlnj06gF5CWcOIu64s" }]
    }
}
```

在此查詢中， `r_id`是使用者定義的別名，在 `FROM`子句中使用 `BY`關鍵字宣告。此`r_id`別名會繫結至查詢結果集中每個文件的`id`中繼資料欄位。您可以在 `SELECT` 使用者*檢視*的查詢子句和 子`WHERE`句中使用此別名。

不過，若要存取其他中繼資料屬性，您必須查詢遞交的檢視。

## 加入文件 ID
<a name="working.by-clause.joining-on-id"></a>

假設您使用`id`一個資料表的文件做為另一個資料表的使用者定義欄位中的外部索引鍵。您可以使用 `BY`子句，為這些欄位上的兩個資料表撰寫內部聯結查詢 （類似於上一個主題[加入遞交和使用者檢視](working.metadata.md#working.metadata.committed-joins))。

下列範例會使用後者子`BY`句，分別在 `PersonId`和 文件`id`欄位`Person`上聯結兩個名為 `DriversLicense`和 的資料表。

```
SELECT * FROM DriversLicense AS d INNER JOIN Person AS p BY pid
ON d.PersonId = pid
WHERE pid = '1CWScY2qHYI9G88C2SjvtH'
```

若要了解如何變更資料表中的文件，請繼續[更新和刪除文件](working.revisions.md)。

# 更新和刪除文件
<a name="working.revisions"></a>

**重要**  
支援終止通知：現有客戶將可以使用 Amazon QLDB，直到 07/31/2025 的支援結束為止。如需詳細資訊，請參閱[將 Amazon QLDB Ledger 遷移至 Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/)。

在 Amazon QLDB 中，*文件修訂*是 Amazon Ion 結構，代表由唯一文件 ID 識別的一系列文件的單一版本。每個修訂都包含文件的完整資料集，包括您的使用者資料和系統產生的中繼資料。每個修訂都是由文件 ID 和零基版本編號的組合唯一識別。

當您更新文件時，QLDB 會使用相同的文件 ID 和遞增的版本編號建立新的修訂。從資料表刪除文件時，文件的生命週期會結束。這表示無法再次建立具有相同文件 ID 的文件修訂版。

## 進行文件修訂
<a name="working.revisions.making"></a>

例如，下列陳述式會插入新車輛註冊、更新註冊城市，然後刪除註冊。這會導致文件的三個修訂。

```
INSERT INTO VehicleRegistration
{
    'VIN' : '1HVBBAANXWH544237',
    'LicensePlateNumber' : 'LS477D',
    'State' : 'WA',
    'City' : 'Tacoma',
    'PendingPenaltyTicketAmount' : 42.20,
    'ValidFromDate' : `2011-10-26T`,
    'ValidToDate' : `2023-09-25T`,
    'Owners' : {
        'PrimaryOwner' : { 'PersonId': 'KmA3XPKKFqYCP2zhR3d0Ho' },
        'SecondaryOwners' : []
    }
}
```

**注意**  
插入陳述式和其他 DML 陳述式會傳回每個受影響文件的 ID。在繼續之前，請儲存此 ID，因為下一個主題需要它來做為歷史記錄函數。您也可以使用下列查詢找到文件 ID。  

```
SELECT r_id FROM VehicleRegistration AS r BY r_id
WHERE r.VIN = '1HVBBAANXWH544237'
```

```
UPDATE VehicleRegistration AS r 
SET r.City = 'Bellevue' 
WHERE r.VIN = '1HVBBAANXWH544237'
```

```
DELETE FROM VehicleRegistration AS r 
WHERE r.VIN = '1HVBBAANXWH544237'
```

如需這些 DML 陳述式語法的更多範例和資訊，請參閱 *Amazon QLDB PartiQL 參考*[DELETE](ql-reference.delete.md)中的 [UPDATE](ql-reference.update.md)和 。

若要在文件中插入和移除特定元素，您可以使用以`FROM`關鍵字開頭的`UPDATE`陳述式或其他 DML 陳述式。如需資訊和範例，請參閱 [FROM (INSERT、Remove 或 SET)](ql-reference.from.md)參考。

刪除文件後，您無法再在遞交或使用者檢視中查詢該文件。若要了解如何使用內建歷史記錄函數查詢本文件的修訂歷史記錄，請繼續[查詢修訂歷史記錄](working.history.md)。

# 查詢修訂歷史記錄
<a name="working.history"></a>

**重要**  
支援終止通知：現有客戶將可以使用 Amazon QLDB，直到 07/31/2025 的支援結束為止。如需詳細資訊，請參閱[將 Amazon QLDB Ledger 遷移至 Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/)。

Amazon QLDB 會將每個文件的完整歷史記錄存放在資料表中。您可以透過[更新和刪除文件](working.revisions.md)查詢內建歷史記錄函數，查看您先前在 中插入、更新和刪除的車輛註冊文件的所有三個修訂。

**Topics**
+ [歷史記錄函數](#working.history.function)
+ [歷史記錄查詢範例](#working.history.example)

## 歷史記錄函數
<a name="working.history.function"></a>

QLDB 中的歷史記錄函數是 PartiQL 延伸模組，會從您資料表的系統定義檢視傳回修訂。因此，它在與遞交檢視相同的結構描述中同時包含您的資料和相關聯的中繼資料。

**語法**

```
SELECT * FROM history( table_name | 'table_id' [, `start-time` [, `end-time` ] ] ) AS h
[ WHERE h.metadata.id = 'id' ]
```引數

***table\$1name* \$1 '*table\$1id*'**  
資料表名稱或資料表 ID。資料表名稱是 PartiQL 識別符，您可以用雙引號或無引號表示。資料表 ID 是字串常值，必須以單引號括住。若要進一步了解如何使用資料表 IDs，請參閱 [查詢非作用中資料表的歷史記錄](working.manage-tables.md#working.history.inactive-table)。

**`*start-time*`、`*end-time*`**  
（選用） 指定任何修訂作用中的時間範圍。*這些參數不會指定在交易中將修訂遞交至日誌的時間範圍。*  
開始和結束時間是 Ion 時間戳記常值，可以用反引號表示 (``...``)。如需進一步了解，請參閱 [在 Amazon QLDB 中使用 PartiQL 查詢離子](ql-reference.query.md)。  
這些時間參數的行為如下：  
+ *開始時間*和*結束時間*都包含在內。它們必須是 [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) 日期和時間格式，並且是國際標準時間 (UTC)。
+ *開始時間*必須小於或等於*結束時間，*並且可以是過去的任何任意日期。
+ *結束時間*必須小於或等於目前的 UTC 日期和時間。
+ 如果您指定*開始時間*，但不是*結束時間*，您的查詢會將*結束時間*預設為目前的日期和時間。如果不指定，您的查詢會傳回整個歷史記錄。

**'*id*'**  
（選用） 您要查詢修訂歷史記錄的文件 ID，以單引號表示。

**提示**  
最佳實務是，使用日期範圍 *（開始時間*和*結束時間*) 和文件 ID () 來限定歷史記錄查詢`metadata.id`。在 QLDB 中，每個`SELECT`查詢都會在交易中處理，並受到[交易逾時限制](limits.md#limits.fixed)。  
歷史記錄查詢不會使用您在資料表上建立的索引。QLDB 歷史記錄僅依文件 ID 編製索引，您目前無法建立其他歷史記錄索引。包含開始時間和結束時間的歷史記錄查詢可受益於日期範圍資格。

## 歷史記錄查詢範例
<a name="working.history.example"></a>

若要查詢車輛註冊文件的歷史記錄，請使用`id`您先前儲存在 中的 [更新和刪除文件](working.revisions.md)。例如，下列歷史記錄查詢`ADR2Ll1fGsU4Jr4EqTdnQF`會傳回 `2019-06-05T00:00:00Z`和 之間曾經作用中的文件 ID 的任何修訂`2019-06-05T23:59:59Z`。

**注意**  
請記住，開始和結束時間參數*不會*指定將修訂遞交至交易中的日誌的時間範圍。例如，如果修訂在 之前遞交，`2019-06-05T00:00:00Z`並在該開始時間之後保持作用中，則此範例查詢會在結果中傳回該修訂。

請務必視需要將 `id`、開始時間和結束時間取代為您自己的值。

```
SELECT * FROM history(VehicleRegistration, `2019-06-05T00:00:00Z`, `2019-06-05T23:59:59Z`) AS h
WHERE h.metadata.id = 'ADR2Ll1fGsU4Jr4EqTdnQF' --replace with your id
```

您的查詢結果看起來應該類似於以下內容。

```
{
    blockAddress:{
        strandId:"JdxjkR9bSYB5jMHWcI464T",
        sequenceNo:14
    },
    hash:{{B2wYwrHKOWsmIBmxUgPRrTx9lv36tMlod2xVvWNiTbo=}},
    data: {
        VIN: "1HVBBAANXWH544237",
        LicensePlateNumber: "LS477D",
        State: "WA",
        City: "Tacoma",
        PendingPenaltyTicketAmount: 42.20,
        ValidFromDate: 2011-10-26T,
        ValidToDate: 2023-09-25T,
        Owners: {
            PrimaryOwner: { PersonId: "KmA3XPKKFqYCP2zhR3d0Ho" },
            SecondaryOwners: []
        }
    },
    metadata:{
        id:"ADR2Ll1fGsU4Jr4EqTdnQF",
        version:0,
        txTime:2019-06-05T20:53:321d-3Z,
        txId:"HgXAkLjAtV0HQ4lNYdzX60"
    }
},
{
    blockAddress:{
        strandId:"JdxjkR9bSYB5jMHWcI464T",
        sequenceNo:17
    },
    hash:{{LGSFZ4iEYWZeMwmAqcxxNyT4wbCtuMOmFCj8pEd6Mp0=}},
    data: {
        VIN: "1HVBBAANXWH544237",
        LicensePlateNumber: "LS477D",
        State: "WA",
        PendingPenaltyTicketAmount: 42.20,
        ValidFromDate: 2011-10-26T,
        ValidToDate: 2023-09-25T,
        Owners: {
            PrimaryOwner: { PersonId: "KmA3XPKKFqYCP2zhR3d0Ho" },
            SecondaryOwners: []
        },
        City: "Bellevue"
    },
    metadata:{
        id:"ADR2Ll1fGsU4Jr4EqTdnQF",
        version:1,
        txTime:2019-06-05T21:01:442d-3Z,
        txId:"9cArhIQV5xf5Tf5vtsPwPq"
    }
},
{
    blockAddress:{
        strandId:"JdxjkR9bSYB5jMHWcI464T",
        sequenceNo:19
    },
    hash:{{7bm5DUwpqJFGrmZpb7h9wAxtvggYLPcXq+LAobi9fDg=}},
    metadata:{
        id:"ADR2Ll1fGsU4Jr4EqTdnQF",
        version:2,
        txTime:2019-06-05T21:03:76d-3Z,
        txId:"9GslbtDtpVHAgYghR5FXbZ"
    }
}
```

輸出包含中繼資料屬性，可提供每個項目修改時間的詳細資訊，以及交易的依據。從這些資料中，您可以看到以下內容：
+ 文件由系統指派 唯一識別`id`：`ADR2Ll1fGsU4Jr4EqTdnQF`。這是以 Base62-encoded字串表示的 UUID。
+ `INSERT` 陳述式會建立文件的初始修訂 （版本 `0`)。
+ 每個後續更新都會建立具有相同文件`id`和遞增版本編號的新修訂。
+ `txId` 欄位指出遞交每個修訂的交易，並`txTime`顯示每個修訂的遞交時間。
+ `DELETE` 陳述式會建立新的文件最終修訂。此最終修訂僅包含中繼資料。

若要了解如何永久刪除修訂，請繼續 [編輯文件修訂](working.redaction.md)。

# 編輯文件修訂
<a name="working.redaction"></a>

**重要**  
支援終止通知：現有客戶將可以使用 Amazon QLDB，直到 07/31/2025 的支援結束為止。如需詳細資訊，請參閱[將 Amazon QLDB Ledger 遷移至 Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/)。

在 Amazon QLDB 中，`DELETE`陳述式只會透過建立新的修訂，將其標示為已刪除，以邏輯方式刪除文件。QLDB 也支援*資料修訂*操作，可讓您永久刪除資料表歷史記錄中的非作用中文件修訂。

**注意**  
任何在 2021 年 7 月 22 日之前建立的分類帳目前不符合修訂資格。您可以在 Amazon QLDB 主控台上檢視分類帳的建立時間。

編輯操作只會刪除指定修訂中的使用者資料，並使日誌序列和文件中繼資料保持不變。這可維護您分類帳的整體資料完整性。

開始使用 QLDB 中的資料修訂之前，請務必在 *Amazon QLDB PartiQL 參考*[修訂考量和限制](ql-stored-procedures.redact_revision.md#ql-stored-procedures.redact_revision.considerations)中檢閱 。

**Topics**
+ [編輯預存程序](#working.redaction.stored-proc)
+ [檢查編輯是否完成](#working.redaction.check-completion)
+ [編輯範例](#working.redaction.example)
+ [刪除和編輯作用中修訂](#working.redaction.active-revision)
+ [在修訂中編輯特定欄位](#working.redaction.field)

## 編輯預存程序
<a name="working.redaction.stored-proc"></a>

您可以使用[REDACT\$1REVISION](ql-stored-procedures.redact_revision.md)預存程序永久刪除分類帳中的個別非作用中修訂。此預存程序會刪除索引儲存和日誌儲存中指定修訂中的所有使用者資料。不過，它會保留日誌序列和文件中繼資料，包括文件 ID 和雜湊，保持不變。*此操作不可復原。*

指定的文件修訂版本必須是歷史記錄中的非作用中修訂。文件的最新作用中修訂不符合修訂資格。

若要修訂多個修訂，您必須為每個修訂執行一次預存程序。您可以編輯每筆交易的一個修訂。

**語法**

```
EXEC REDACT_REVISION `block-address`, 'table-id', 'document-id'
```引數

`*block-address*`  
要修訂之文件修訂的日誌區塊位置。地址是具有兩個欄位的 Amazon Ion 結構： `strandId`和 `sequenceNo`。  
這是以反引號表示的 Ion 常值。例如：  

```
`{strandId:"JdxjkR9bSYB5jMHWcI464T", sequenceNo:17}`
```

'*table-id*'  
您想要修訂文件的資料表的唯一 ID，以單引號表示。

'*document-id*'  
要修訂修訂的唯一文件 ID，以單引號表示。

## 檢查編輯是否完成
<a name="working.redaction.check-completion"></a>

當您執行預存程序提交修訂請求時，QLDB 會以非同步方式處理資料修訂。完成後，修訂中的使用者資料 （由`data`結構表示） 會永久移除。若要檢查修訂請求是否已完成，您可以使用下列其中一項：
+ [日誌匯出](export-journal.md)
+ [日誌串流](streams.md)
+ [GetBlock API 操作](https://docs.aws.amazon.com/qldb/latest/developerguide/API_GetBlock.html)
+ [GetRevision API 操作](https://docs.aws.amazon.com/qldb/latest/developerguide/API_GetRevision.html)
+ [歷史記錄函數](working.history.md#working.history.function) – **注意：**在日誌中完成修訂後，可能需要一些時間，歷史記錄查詢才會顯示修訂的結果。當非同步修訂完成時，您可能會在其他修訂之前看到一些修訂，但歷史記錄查詢最終會顯示已完成的結果。

修訂修訂完成後，修訂的`data`結構會替換為新的`dataHash`欄位。此欄位的值是已移除`data`結構的 Ion 雜湊，如下列範例所示。因此，分類帳會維護其整體資料完整性，並透過現有的驗證 API 操作以密碼編譯方式進行驗證。若要進一步了解驗證，請參閱 [Amazon QLDB 中的資料驗證](verification.md)。

## 編輯範例
<a name="working.redaction.example"></a>

請考慮您之前在 中檢閱的車輛註冊文件[查詢修訂歷史記錄](working.history.md)。假設您想要修訂第二個修訂版 (`version:1`)。下列查詢範例顯示此修訂之後的修訂。在查詢結果中，要修訂的`data`結構會以*紅色斜體*反白顯示。

```
SELECT * FROM history(VehicleRegistration) AS h
WHERE h.metadata.id = 'ADR2Ll1fGsU4Jr4EqTdnQF' --replace with your id
AND h.metadata.version = 1
```

```
{
    blockAddress:{
        strandId:"JdxjkR9bSYB5jMHWcI464T",
        sequenceNo:17
    },
    hash:{{LGSFZ4iEYWZeMwmAqcxxNyT4wbCtuMOmFCj8pEd6Mp0=}},
    data: {
        VIN: "1HVBBAANXWH544237",
        LicensePlateNumber: "LS477D",
        State: "WA",
        PendingPenaltyTicketAmount: 42.20,
        ValidFromDate: 2011-10-26T,
        ValidToDate: 2023-09-25T,
        Owners: {
            PrimaryOwner: { PersonId: "KmA3XPKKFqYCP2zhR3d0Ho" },
            SecondaryOwners: []
        },
        City: "Bellevue"
    },
    metadata:{
        id:"ADR2Ll1fGsU4Jr4EqTdnQF",
        version:1,
        txTime:2019-06-05T21:01:442d-3Z,
        txId:"9cArhIQV5xf5Tf5vtsPwPq"
    }
}
```

在查詢結果`blockAddress`中記下 ，因為您需要將此值傳遞至`REDACT_REVISION`預存程序。然後，透過查詢[系統目錄](working.catalog.md)來尋找`VehicleRegistration`資料表的唯一 ID，如下所示。

```
SELECT tableId FROM information_schema.user_tables
WHERE name = 'VehicleRegistration'
```

使用此資料表 ID 搭配文件 ID 和區塊地址來執行 `REDACT_REVISION`。資料表 ID 和文件 ID 是字串常值，必須以單一引號括住，而區塊地址是以反引號括住的 Ion 常值。請務必視需要將這些引數取代為您自己的值。

```
EXEC REDACT_REVISION `{strandId:"JdxjkR9bSYB5jMHWcI464T", sequenceNo:17}`, '5PLf9SXwndd63lPaSIa0O6', 'ADR2Ll1fGsU4Jr4EqTdnQF'
```

**提示**  
當您使用 QLDB 主控台或 QLDB shell 來查詢資料表 ID 或文件 ID （或任何字串常值） 時，傳回的值會以*雙*引號括住。不過，當您指定預`REDACT_REVISION`存程序的資料表 ID 和文件 ID 引數時，必須以*單一*引號括住值。  
這是因為您以 PartiQL 格式撰寫陳述式，但 QLDB 會以 Amazon Ion 格式傳回結果。如需 QLDB 中 PartiQL 語法和語義的詳細資訊，請參閱 [使用 PartiQL 查詢 Ion](ql-reference.query.md)。

有效的修訂請求會傳回 Ion 結構，其代表您正在修訂的文件修訂，如下所示。

```
{
  blockAddress: {
    strandId: "JdxjkR9bSYB5jMHWcI464T",
    sequenceNo: 17
  },
  tableId: "5PLf9SXwndd63lPaSIa0O6",
  documentId: "ADR2Ll1fGsU4Jr4EqTdnQF",
  version: 1
}
```

當您執行此預存程序時，QLDB 會以非同步方式處理您的修訂請求。完成修訂後，`data`結構會永久移除，並以新*`dataHash`*欄位取代。此欄位的值是已移除`data`結構的 Ion 雜湊，如下所示。

**注意**  
此`dataHash`範例僅供參考，並非實際計算的雜湊值。

```
{
    blockAddress:{
        strandId:"JdxjkR9bSYB5jMHWcI464T",
        sequenceNo:17
    },
    hash:{{LGSFZ4iEYWZeMwmAqcxxNyT4wbCtuMOmFCj8pEd6Mp0=}},
    dataHash: {{s83jd7sfhsdfhksj7hskjdfjfpIPP/DP2hvionas2d4=}},
    metadata:{
        id:"ADR2Ll1fGsU4Jr4EqTdnQF",
        version:1,
        txTime:2019-06-05T21:01:442d-3Z,
        txId:"9cArhIQV5xf5Tf5vtsPwPq"
    }
}
```

## 刪除和編輯作用中修訂
<a name="working.redaction.active-revision"></a>

作用中文件修訂 （亦即每個文件的最新未刪除修訂） 不符合資料修訂的資格。您必須先更新或刪除它，才能編輯作用中的修訂。這會將先前作用中的修訂移至歷史記錄，並使其符合修訂資格。

如果您的使用案例需要將整個文件標示為已刪除，則您首先會使用 [DELETE](ql-reference.delete.md) 陳述式。例如，下列陳述式會以邏輯方式刪除 VIN 為 `VehicleRegistration`的文件`1HVBBAANXWH544237`。

```
DELETE FROM VehicleRegistration AS r
WHERE r.VIN = '1HVBBAANXWH544237'
```

然後，在此刪除之前修訂先前的修訂，如前所述。如有需要，您也可以個別修訂任何先前的修訂。

如果您的使用案例需要文件保持作用中狀態，您首先會使用 [UPDATE](ql-reference.update.md) 或 [FROM](ql-reference.from.md) 陳述式來隱藏或移除您要編輯的欄位。此程序會在下節中說明。

## 在修訂中編輯特定欄位
<a name="working.redaction.field"></a>

QLDB 不支援修訂文件修訂中的特定欄位。若要這麼做，您可以先使用 [UPDATE-REMOVE](ql-reference.update.md) 或 [FROM-REMOVE](ql-reference.from.md) 陳述式，從修訂中移除現有欄位。例如，下列陳述式會從 VIN 為 `VehicleRegistration`的文件中移除 `LicensePlateNumber` 欄位`1HVBBAANXWH544237`。

```
UPDATE VehicleRegistration AS r
REMOVE r.LicensePlateNumber
WHERE r.VIN = '1HVBBAANXWH544237'
```

然後，在此移除之前修訂先前的修訂版，如前所述。如有需要，您也可以個別修訂任何包含此現在移除欄位的先前修訂。

若要了解如何最佳化查詢，請繼續 [最佳化查詢效能](working.optimize.md)。

# 最佳化查詢效能
<a name="working.optimize"></a>

**重要**  
支援終止通知：現有客戶將可以使用 Amazon QLDB，直到 07/31/2025 的支援結束為止。如需詳細資訊，請參閱[將 Amazon QLDB Ledger 遷移至 Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/)。

Amazon QLDB 旨在解決高效能線上交易處理 (OLTP) 工作負載的需求。這表示 QLDB 已針對一組特定的查詢模式進行最佳化，即使它支援類似 SQL 的查詢功能。設計應用程式及其資料模型以使用這些查詢模式至關重要。否則，隨著資料表的增長，您會遇到嚴重的效能問題，包括查詢延遲、交易逾時和並行衝突。

本節說明 QLDB 中的查詢限制條件，並針對這些限制條件提供撰寫最佳查詢的指引。

**Topics**
+ [交易逾時限制](#working.optimize.txn-timeout)
+ [並行衝突](#working.optimize.occ)
+ [最佳查詢模式](#working.optimize.patterns)
+ [要避免的查詢模式](#working.optimize.avoid)
+ [監控效能](#working.optimize.monitor)

## 交易逾時限制
<a name="working.optimize.txn-timeout"></a>

在 QLDB 中，每個 PartiQL 陳述式 （包括每個`SELECT`查詢） 都會在交易中處理，並受到[交易逾時限制](limits.md#limits.fixed)。交易最多可執行 **30 秒**，然後再遞交。在此限制之後，QLDB 會拒絕對交易完成的任何工作，並捨棄執行交易的[工作階段](concurrency.md#concurrency.sessions)。此限制會啟動交易，而不是遞交或取消工作階段，以保護服務的用戶端免於工作階段洩漏。

## 並行衝突
<a name="working.optimize.occ"></a>

QLDB 使用*樂觀並行控制 (OCC) 實作並行控制*。欠佳查詢也可能導致更多 OCC 衝突。如需 OCC 的詳細資訊，請參閱 [Amazon QLDB 並行模型](concurrency.md)。

## 最佳查詢模式
<a name="working.optimize.patterns"></a>

最佳實務是，您應該使用`WHERE`述詞子句來執行陳述式，該子句會篩選索引欄位或文件 ID。QLDB 需要索引欄位上的*等式*運算子 (`=` 或 `IN`)，才能有效率地查詢文件。

以下是[使用者檢視](working.userdata.md)中最佳查詢模式的範例。

```
--Indexed field (VIN) lookup using the = operator
SELECT * FROM VehicleRegistration
WHERE VIN = '1N4AL11D75C109151'

--Indexed field (VIN) AND non-indexed field (City) lookup
SELECT * FROM VehicleRegistration
WHERE VIN = '1N4AL11D75C109151' AND City = 'Seattle'

--Indexed field (VIN) lookup using the IN operator
SELECT * FROM VehicleRegistration
WHERE VIN IN ('1N4AL11D75C109151', 'KM8SRDHF6EU074761')

--Document ID (r_id) lookup using the BY clause
SELECT * FROM VehicleRegistration BY r_id
WHERE r_id = '3Qv67yjXEwB9SjmvkuG6Cp'
```

任何不遵循這些模式的查詢都會叫用*完整的資料表掃描*。資料表掃描可能會導致大型資料表上的查詢或傳回大型結果集的查詢的交易逾時。它們也[可能導致 OCC 與競爭交易發生衝突](concurrency.md#concurrency.indexes)。

**高基數索引**

我們建議為包含高基數值的欄位編製索引。例如，`VehicleRegistration`資料表中的 `VIN`和 `LicensePlateNumber` 欄位是索引欄位，旨在是唯一的。

避免索引低基數欄位，例如狀態碼、地址狀態或省份，以及郵遞區號。如果您為此類欄位編製索引，您的查詢可能會產生大型結果集，這*更可能導致交易逾時或導致意外的 OCC 衝突*。

**遞交的檢視查詢**

您在[遞交檢視](working.metadata.md)中執行的查詢，會遵循與使用者檢視查詢相同的最佳化準則。您在資料表上建立的索引也會用於遞交檢視中的查詢。

**歷史記錄函數查詢**

[歷史記錄函數](working.history.md)查詢不會使用您在資料表上建立的索引。QLDB 歷史記錄僅依文件 ID 編製索引，您目前無法建立其他歷史記錄索引。

最佳實務是，使用日期範圍 (*開始時間*和*結束時間*) 和文件 ID () 來限定歷史記錄查詢`metadata.id`。包含開始時間和結束時間的歷史記錄查詢，可受益於日期範圍資格。

**內部聯結查詢**

對於內部聯結查詢，請使用聯結條件，其中至少包含聯結右側資料表的索引欄位。如果沒有聯結索引，聯結查詢會叫用多個資料表掃描，對於聯結左側資料表中的每個文件，查詢會完全掃描右側資料表。最佳實務是加入針對您加入的每個資料表編製索引的欄位，以及為至少一個資料表指定`WHERE`等式述詞。

例如，下列查詢會聯結各自`VIN`欄位上的 `VehicleRegistration`和 `Vehicle`資料表，這兩個欄位都會編製索引。此查詢在 上也具有等式述詞`VehicleRegistration.VIN`。

```
SELECT * FROM VehicleRegistration AS r INNER JOIN Vehicle AS v
ON r.VIN = v.VIN
WHERE r.VIN IN ('1N4AL11D75C109151', 'KM8SRDHF6EU074761')
```

針對聯結條件和聯結查詢中的等式述詞，選擇高基數索引。

## 要避免的查詢模式
<a name="working.optimize.avoid"></a>

以下是一些無法妥善擴展至 QLDB 中較大資料表的**次佳**陳述式範例。強烈建議您不要依賴這些類型的查詢來尋找隨時間增長的資料表，因為您的查詢最終會導致交易逾時。由於資料表包含大小不同的文件，因此很難定義未索引查詢的精確限制。

```
--No predicate clause
SELECT * FROM Vehicle

--COUNT() is not an optimized function
SELECT COUNT(*) FROM Vehicle

--Low-cardinality predicate
SELECT * FROM Vehicle WHERE Color = 'Silver'

--Inequality (>) does not qualify for indexed lookup
SELECT * FROM Vehicle WHERE "Year" > 2019

--Inequality (LIKE)
SELECT * FROM Vehicle WHERE VIN LIKE '1N4AL%'

--Inequality (BETWEEN)
SELECT SUM(PendingPenaltyTicketAmount) FROM VehicleRegistration
WHERE ValidToDate BETWEEN `2020-01-01T` AND `2020-07-01T`

--No predicate clause
DELETE FROM Vehicle

--No document id, and no date range for the history() function
SELECT * FROM history(Vehicle)
```

一般而言，*我們不建議*在 QLDB 中針對生產使用案例執行下列類型的查詢模式：
+ 線上分析處理 (OLAP) 查詢
+ 沒有述詞子句的探索性查詢
+ 報告查詢
+ 文字搜尋

反之，我們建議您將資料串流到針對分析使用案例最佳化的專用資料庫服務。例如，您可以將 QLDB 資料串流至 Amazon OpenSearch Service，以透過文件提供全文搜尋功能。如需示範此使用案例的範例應用程式，請參閱 GitHub 儲存庫 [aws-samples/amazon-qldb-streaming-amazon-opensearch-service-sample-python](https://github.com/aws-samples/amazon-qldb-streaming-amazon-opensearch-service-sample-python)。如需 QLDB 串流的相關資訊，請參閱 [從 Amazon QLDB 串流日誌資料](streams.md)。

## 監控效能
<a name="working.optimize.monitor"></a>

QLDB 驅動程式會在陳述式的結果物件中提供已耗用 I/O 用量和時間資訊。您可以使用這些指標來識別效率不佳的 PartiQL 陳述式。若要進一步了解，請繼續 [取得 PartiQL 陳述式統計資料](working.statement-stats.md)。

您也可以使用 Amazon CloudWatch 來追蹤您分類帳的資料操作效能。監控指定 `CommandLatency` `LedgerName`和 的指標`CommandType`。如需詳細資訊，請參閱[使用 Amazon CloudWatch 監控](monitoring-cloudwatch.md)。若要了解 QLDB 如何使用命令來管理資料操作，請參閱 [使用驅動程式進行工作階段管理](driver-session-management.md)。

# 取得 PartiQL 陳述式統計資料
<a name="working.statement-stats"></a>

**重要**  
支援終止通知：現有客戶將可以使用 Amazon QLDB，直到 07/31/2025 的支援結束為止。如需詳細資訊，請參閱[將 Amazon QLDB Ledger 遷移至 Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/)。

Amazon QLDB 提供陳述式執行統計資料，可協助您透過執行更有效率的 PartiQL 陳述式來最佳化 QLDB 的使用。QLDB 會傳回這些統計資料以及陳述式的結果。其中包括量化已耗用 I/O 用量和伺服器端處理時間的指標，可用來識別效率不佳的陳述式。

此功能目前可在 [QLDB 主控台](console_QLDB.md)的 *PartiQL 編輯器*、[QLDB shell](data-shell.md) 和所有支援語言的最新版本 [QLDB 驅動程式](getting-started-driver.md)中使用。您也可以在主控台上檢視查詢歷史記錄的陳述式統計資料。

**Topics**
+ [I/O 用量](#statement-stats.io-usage)
+ [時間資訊](#statement-stats.timing-information)

## I/O 用量
<a name="statement-stats.io-usage"></a>

I/O 用量指標說明讀取 I/O 請求的數量。如果讀取 I/O 請求的數量高於預期，則表示陳述式未最佳化，例如缺少索引。建議您在上一個主題[最佳查詢模式](working.optimize.md#working.optimize.patterns)中檢閱*最佳化查詢效能*。

**注意**  
當您在非空白資料表上執行`CREATE INDEX`陳述式時，I/O 用量指標只會包含同步索引建立呼叫的讀取請求。  
QLDB 會以非同步方式為資料表中的任何現有文件建立索引。這些非同步讀取請求不會包含在陳述式結果的 I/O 用量指標中。非同步讀取請求會個別收費，並在索引建置完成後新增至您的總讀取 I/O。

### 使用 QLDB 主控台
<a name="statement-stats.io-usage.con"></a>

若要使用 QLDB 主控台取得陳述式的讀取輸入/輸出用量，請執行下列步驟：

1. 在 [https://console.aws.amazon.com/qldb](https://console.aws.amazon.com/qldb)：// 開啟 Amazon QLDB 主控台。

1. 在導覽窗格中，選擇 **PartiQL 編輯器**。

1. 從分類帳下拉式清單中選擇分類帳。

1. 在查詢編輯器視窗中，輸入您選擇的任何陳述式，然後選擇**執行**。以下是查詢範例。

   ```
   SELECT * FROM testTable WHERE firstName = 'Jim'
   ```

   若要執行陳述式，您也可以使用 Windows 的鍵盤快速鍵 Ctrl＋Enter，或 macOS 的鍵盤快速鍵 Cmd＋Return。如需更多鍵盤快速鍵，請參閱 [PartiQL 編輯器鍵盤快速鍵](console_QLDB.md#console_QLDB.partiql-editor-shortcuts)。

1. 在查詢編輯器視窗下方，您的查詢結果包含*讀取 I/O*，這是陳述式提出的讀取請求數目。

您也可以執行下列步驟來檢視查詢歷史記錄的讀取 I/O：

1. 在導覽窗格中，選擇 **PartiQL 編輯器**下的**最近查詢**。

1. **讀取輸入/輸出**欄會顯示每個陳述式提出的讀取請求數量。

### 使用 QLDB 驅動程式
<a name="statement-stats.io-usage.driver"></a>

若要使用 QLDB 驅動程式取得陳述式的輸入/輸出用量，請呼叫結果串流游標或緩衝游標`getConsumedIOs`的操作。

下列程式碼範例示範如何從陳述式結果的*串流游標*取得讀取輸入/輸出。

------
#### [ Java ]

```
import com.amazon.ion.IonSystem;
import com.amazon.ion.IonValue;
import com.amazon.ion.system.IonSystemBuilder;
import software.amazon.qldb.IOUsage;
import software.amazon.qldb.Result;

IonSystem ionSystem = IonSystemBuilder.standard().build();
IonValue ionFirstName = ionSystem.newString("Jim");

driver.execute(txn -> {
    Result result = txn.execute("SELECT * FROM testTable WHERE firstName = ?", ionFirstName);

    for (IonValue ionValue : result) {
        // User code here to handle results
    }

    IOUsage ioUsage = result.getConsumedIOs();
    long readIOs = ioUsage.getReadIOs();
});
```

------
#### [ .NET ]

```
using Amazon.IonDotnet.Builders;
using Amazon.IonDotnet.Tree;
using Amazon.QLDB.Driver;
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;

// This is one way of creating Ion values. We can also use a ValueFactory.
// For more details, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-cookbook-dotnet.html#cookbook-dotnet.ion
IIonValue ionFirstName = IonLoader.Default.Load("Jim");

await driver.Execute(async txn =>
{
    IAsyncResult result = await txn.Execute("SELECT * FROM testTable WHERE firstName = ?", ionFirstName);

    // Iterate through stream cursor to accumulate read IOs.
    await foreach (IIonValue ionValue in result)
    {
        // User code here to handle results.
        // Warning: It is bad practice to rely on results within a lambda block, unless
        // it is to check the state of a result. This is because lambdas are retryable.
    }

    var ioUsage = result.GetConsumedIOs();
    var readIOs = ioUsage?.ReadIOs;
});
```

**注意**  
若要轉換為同步程式碼，請移除 `await`和 `async`關鍵字，並將 `IAsyncResult` 類型變更為 `IResult`。

------
#### [ Go ]

```
import (
    "context"
    "fmt"
    "github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver"
)

driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
    result, err := txn.Execute("SELECT * FROM testTable WHERE firstName = ?", "Jim")

    if err != nil {
        panic(err)
    }

    for result.Next(txn) {
        // User code here to handle results
    }

    ioUsage := result.GetConsumedIOs()
    readIOs := *ioUsage.GetReadIOs()
    fmt.Println(readIOs)
    return nil,nil
})
```

------
#### [ Node.js ]

```
import { IOUsage, ResultReadable, TransactionExecutor } from "amazon-qldb-driver-nodejs";

await driver.executeLambda(async (txn: TransactionExecutor) => {
    const result: ResultReadable = await txn.executeAndStreamResults("SELECT * FROM testTable WHERE firstName = ?", "Jim");

    for await (const chunk of result) {
        // User code here to handle results
    }

    const ioUsage: IOUsage = result.getConsumedIOs();
    const readIOs: number = ioUsage.getReadIOs();
});
```

------
#### [ Python ]

```
def get_read_ios(transaction_executor):
    cursor = transaction_executor.execute_statement("SELECT * FROM testTable WHERE firstName = ?", "Jim")

    for row in cursor:
        # User code here to handle results
        pass

    consumed_ios = cursor.get_consumed_ios()
    read_ios = consumed_ios.get('ReadIOs')

qldb_driver.execute_lambda(lambda txn: get_read_ios(txn))
```

------

下列程式碼範例示範如何從陳述式結果的*緩衝游標*取得讀取輸入/輸出。這會傳回來自 `ExecuteStatement`和 `FetchPage`請求的讀取 I/O 總數。

------
#### [ Java ]

```
import com.amazon.ion.IonSystem;
import com.amazon.ion.IonValue;
import com.amazon.ion.system.IonSystemBuilder;
import software.amazon.qldb.IOUsage;
import software.amazon.qldb.Result;

IonSystem ionSystem = IonSystemBuilder.standard().build();
IonValue ionFirstName = ionSystem.newString("Jim");

Result result = driver.execute(txn -> {
    return txn.execute("SELECT * FROM testTable WHERE firstName = ?", ionFirstName);
});

IOUsage ioUsage = result.getConsumedIOs();
long readIOs = ioUsage.getReadIOs();
```

------
#### [ .NET ]

```
using Amazon.IonDotnet.Builders;
using Amazon.IonDotnet.Tree;
using Amazon.QLDB.Driver;
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;

IIonValue ionFirstName = IonLoader.Default.Load("Jim");

IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("SELECT * FROM testTable WHERE firstName = ?", ionFirstName);
});

var ioUsage = result.GetConsumedIOs();
var readIOs = ioUsage?.ReadIOs;
```

**注意**  
若要轉換為同步程式碼，請移除 `await`和 `async`關鍵字，並將 `IAsyncResult` 類型變更為 `IResult`。

------
#### [ Go ]

```
import (
    "context"
    "fmt"
    "github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver"
)

result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
    result, err :=  txn.Execute("SELECT * FROM testTable WHERE firstName = ?", "Jim")
    if err != nil {
        return nil, err
    }
    return txn.BufferResult(result)
})

if err != nil {
    panic(err)
}

qldbResult := result.(*qldbdriver.BufferedResult)
ioUsage := qldbResult.GetConsumedIOs()
readIOs := *ioUsage.GetReadIOs()
fmt.Println(readIOs)
```

------
#### [ Node.js ]

```
import { IOUsage, Result, TransactionExecutor } from "amazon-qldb-driver-nodejs";

const result: Result = await driver.executeLambda(async (txn: TransactionExecutor) => {
    return await txn.execute("SELECT * FROM testTable WHERE firstName = ?", "Jim");
});

const ioUsage: IOUsage = result.getConsumedIOs();
const readIOs: number = ioUsage.getReadIOs();
```

------
#### [ Python ]

```
cursor = qldb_driver.execute_lambda(
    lambda txn: txn.execute_statement("SELECT * FROM testTable WHERE firstName = ?", "Jim"))

consumed_ios = cursor.get_consumed_ios()
read_ios = consumed_ios.get('ReadIOs')
```

------

**注意**  
串流游標具有狀態，因為它會分頁結果集。因此， `getConsumedIOs`和 `getTimingInformation`操作會從您呼叫時傳回累積的指標。  
緩衝游標會在記憶體中緩衝結果集，並傳回總累積指標。

## 時間資訊
<a name="statement-stats.timing-information"></a>

時間資訊指標以毫秒為單位說明伺服器端的處理時間。伺服器端處理時間定義為 QLDB 處理陳述式所花費的時間量。這不包括網路呼叫或暫停所花費的時間。此指標會使 QLDB 服務端的處理時間與用戶端的處理時間不同。

### 使用 QLDB 主控台
<a name="statement-stats.timing-information.con"></a>

若要使用 QLDB 主控台取得陳述式的時間資訊，請執行下列步驟：

1. 在 [https://console.aws.amazon.com/qldb](https://console.aws.amazon.com/qldb)：// 開啟 Amazon QLDB 主控台。

1. 在導覽窗格中，選擇 **PartiQL 編輯器**。

1. 從分類帳下拉式清單中選擇分類帳。

1. 在查詢編輯器視窗中，輸入您選擇的任何陳述式，然後選擇**執行**。以下是查詢範例。

   ```
   SELECT * FROM testTable WHERE firstName = 'Jim'
   ```

   若要執行陳述式，您也可以使用 Windows 的鍵盤快速鍵 Ctrl＋Enter，或 macOS 的鍵盤快速鍵 Cmd＋Return。如需更多鍵盤快速鍵，請參閱 [PartiQL 編輯器鍵盤快速鍵](console_QLDB.md#console_QLDB.partiql-editor-shortcuts)。

1. 在查詢編輯器視窗下方，您的查詢結果包含*伺服器端延遲*，這是 QLDB 收到陳述式請求到傳送回應之間的時間量。這是總查詢持續時間的子集。

您也可以執行下列步驟來檢視查詢歷史記錄的時間資訊：

1. 在導覽窗格中，選擇 **PartiQL 編輯器**下的**最近查詢**。

1. **執行時間 (ms)** 欄會顯示每個陳述式的此時間資訊。

### 使用 QLDB 驅動程式
<a name="statement-stats.timing-information.driver"></a>

若要使用 QLDB 驅動程式取得陳述式的時間資訊，請呼叫結果的串流游標或緩衝游標`getTimingInformation`的操作。

下列程式碼範例示範如何從陳述式結果的*串流游標*取得處理時間。

------
#### [ Java ]

```
import com.amazon.ion.IonSystem;
import com.amazon.ion.IonValue;
import com.amazon.ion.system.IonSystemBuilder;
import software.amazon.qldb.Result;
import software.amazon.qldb.TimingInformation;

IonSystem ionSystem = IonSystemBuilder.standard().build();
IonValue ionFirstName = ionSystem.newString("Jim");

driver.execute(txn -> {
    Result result = txn.execute("SELECT * FROM testTable WHERE firstName = ?", ionFirstName);

    for (IonValue ionValue : result) {
        // User code here to handle results
    }

    TimingInformation timingInformation = result.getTimingInformation();
    long processingTimeMilliseconds = timingInformation.getProcessingTimeMilliseconds();
});
```

------
#### [ .NET ]

```
using Amazon.IonDotnet.Builders;
using Amazon.IonDotnet.Tree;
using Amazon.QLDB.Driver;
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;

IIonValue ionFirstName = IonLoader.Default.Load("Jim");

await driver.Execute(async txn =>
{
    IAsyncResult result = await txn.Execute("SELECT * FROM testTable WHERE firstName = ?", ionFirstName);

    // Iterate through stream cursor to accumulate processing time.
    await foreach(IIonValue ionValue in result)
    {
        // User code here to handle results.
        // Warning: It is bad practice to rely on results within a lambda block, unless
        // it is to check the state of a result. This is because lambdas are retryable.
    }

    var timingInformation = result.GetTimingInformation();
    var processingTimeMilliseconds = timingInformation?.ProcessingTimeMilliseconds;
});
```

**注意**  
若要轉換為同步程式碼，請移除 `await`和 `async`關鍵字，並將 `IAsyncResult` 類型變更為 `IResult`。

------
#### [ Go ]

```
import (
    "context"
    "fmt"
    "github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver"
)

driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
    result, err := txn.Execute("SELECT * FROM testTable WHERE firstName = ?", "Jim")

    if err != nil {
        panic(err)
    }

    for result.Next(txn) {
        // User code here to handle results
    }

    timingInformation := result.GetTimingInformation()
    processingTimeMilliseconds := *timingInformation.GetProcessingTimeMilliseconds()
    fmt.Println(processingTimeMilliseconds)
    return nil, nil
})
```

------
#### [ Node.js ]

```
import { ResultReadable, TimingInformation, TransactionExecutor } from "amazon-qldb-driver-nodejs";

await driver.executeLambda(async (txn: TransactionExecutor) => {
    const result: ResultReadable = await txn.executeAndStreamResults("SELECT * FROM testTable WHERE firstName = ?", "Jim");

    for await (const chunk of result) {
        // User code here to handle results
    }

    const timingInformation: TimingInformation = result.getTimingInformation();
    const processingTimeMilliseconds: number = timingInformation.getProcessingTimeMilliseconds();
});
```

------
#### [ Python ]

```
def get_processing_time_milliseconds(transaction_executor):
    cursor = transaction_executor.execute_statement("SELECT * FROM testTable WHERE firstName = ?", "Jim")

    for row in cursor:
        # User code here to handle results
        pass

    timing_information = cursor.get_timing_information()
    processing_time_milliseconds = timing_information.get('ProcessingTimeMilliseconds')

qldb_driver.execute_lambda(lambda txn: get_processing_time_milliseconds(txn))
```

------

下列程式碼範例示範如何從陳述式結果的*緩衝游標*取得處理時間。這會傳回來自 `ExecuteStatement`和 `FetchPage`請求的總處理時間。

------
#### [ Java ]

```
import com.amazon.ion.IonSystem;
import com.amazon.ion.IonValue;
import com.amazon.ion.system.IonSystemBuilder;
import software.amazon.qldb.Result;
import software.amazon.qldb.TimingInformation;

IonSystem ionSystem = IonSystemBuilder.standard().build();
IonValue ionFirstName = ionSystem.newString("Jim");

Result result = driver.execute(txn -> {
    return txn.execute("SELECT * FROM testTable WHERE firstName = ?", ionFirstName);
});

TimingInformation timingInformation = result.getTimingInformation();
long processingTimeMilliseconds = timingInformation.getProcessingTimeMilliseconds();
```

------
#### [ .NET ]

```
using Amazon.IonDotnet.Builders;
using Amazon.IonDotnet.Tree;
using Amazon.QLDB.Driver;
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;

IIonValue ionFirstName = IonLoader.Default.Load("Jim");

IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("SELECT * FROM testTable WHERE firstName = ?", ionFirstName);
});

var timingInformation = result.GetTimingInformation();
var processingTimeMilliseconds = timingInformation?.ProcessingTimeMilliseconds;
```

**注意**  
若要轉換為同步程式碼，請移除 `await`和 `async`關鍵字，並將 `IAsyncResult` 類型變更為 `IResult`。

------
#### [ Go ]

```
import (
    "context"
    "fmt"
    "github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver"
)

result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
    result, err :=  txn.Execute("SELECT * FROM testTable WHERE firstName = ?", "Jim")
    if err != nil {
        return nil, err
    }
    return txn.BufferResult(result)
})

if err != nil {
    panic(err)
}

qldbResult := result.(*qldbdriver.BufferedResult)
timingInformation := qldbResult.GetTimingInformation()
processingTimeMilliseconds := *timingInformation.GetProcessingTimeMilliseconds()
fmt.Println(processingTimeMilliseconds)
```

------
#### [ Node.js ]

```
import { Result, TimingInformation, TransactionExecutor } from "amazon-qldb-driver-nodejs";

const result: Result = await driver.executeLambda(async (txn: TransactionExecutor) => {
    return await txn.execute("SELECT * FROM testTable WHERE firstName = ?", "Jim");
});

const timingInformation: TimingInformation = result.getTimingInformation();
const processingTimeMilliseconds: number = timingInformation.getProcessingTimeMilliseconds();
```

------
#### [ Python ]

```
cursor = qldb_driver.execute_lambda(
    lambda txn: txn.execute_statement("SELECT * FROM testTable WHERE firstName = ?", "Jim"))

timing_information = cursor.get_timing_information()
processing_time_milliseconds = timing_information.get('ProcessingTimeMilliseconds')
```

------

**注意**  
串流游標具有狀態，因為它會分頁結果集。因此， `getConsumedIOs`和 `getTimingInformation`操作會從您呼叫時傳回累積的指標。  
緩衝游標會在記憶體中緩衝結果集，並傳回總累積指標。

若要了解如何查詢系統目錄，請繼續 [查詢系統目錄](working.catalog.md)。

# 查詢系統目錄
<a name="working.catalog"></a>

**重要**  
支援終止通知：現有客戶將可以使用 Amazon QLDB，直到 07/31/2025 的支援結束為止。如需詳細資訊，請參閱[將 Amazon QLDB Ledger 遷移至 Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/)。

您在 Amazon QLDB 分類帳中建立的每個資料表都有系統指派的唯一 ID。您可以透過查詢系統目錄資料表 來尋找資料表的 ID、其索引清單和其他中繼資料`information_schema.user_tables`。

所有系統指派IDs 都是通用的唯一識別符 (UUID)，每個識別符都以 Base62-encoded字串表示。如需詳細資訊，請參閱[Amazon QLDB 中的唯一 IDs](working.unique-id.md)。

下列範例顯示傳回`VehicleRegistration`資料表中繼資料屬性的查詢結果。

```
SELECT * FROM information_schema.user_tables
WHERE name = 'VehicleRegistration'
```

```
{
    tableId: "5PLf9SXwndd63lPaSIa0O6",
    name: "VehicleRegistration",
    indexes: [
        { indexId: "Djg2nt0yIs2GY0T29Kud1z", expr: "[VIN]", status: "ONLINE" },
        { indexId: "4tPW3fUhaVhDinRgKRLhGU", expr: "[LicensePlateNumber]", status: "BUILDING" }
    ],
    status: "ACTIVE"
}
```

**資料表中繼資料欄位**
+ `tableId` – 資料表的唯一 ID。
+ `name` – 資料表名稱。
+ `indexes` – 資料表上的索引清單。
  + `indexId` – 索引的唯一 ID。
  + `expr` – 索引文件路徑。此欄位是格式為： 的字串`[fieldName]`。
  + `status` – 索引的目前狀態 (`BUILDING`、`FINALIZING`、`FAILED`、 `ONLINE`或 `DELETING`)。在狀態為 之前，QLDB 不會在查詢中使用索引`ONLINE`。
  + `message` – 描述索引具有`FAILED`狀態之原因的錯誤訊息。只有失敗的索引才包含此欄位。
+ `status` – 資料表的目前狀態 (`ACTIVE` 或 `INACTIVE`)。資料表會在您使用`INACTIVE`時變成 `DROP`。

若要了解如何使用 `DROP TABLE`和 `UNDROP TABLE`陳述式管理資料表，請繼續[管理資料表](working.manage-tables.md)。

# 管理資料表
<a name="working.manage-tables"></a>

**重要**  
支援終止通知：現有客戶將可以使用 Amazon QLDB，直到 07/31/2025 的支援結束為止。如需詳細資訊，請參閱[將 Amazon QLDB Ledger 遷移至 Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/)。

本節說明如何使用 Amazon QLDB 中的 `DROP TABLE`和 `UNDROP TABLE`陳述式來管理資料表。它也說明如何在建立資料表時標記資料表。您可以在 中定義作用中資料表數量和總資料表的配額[Amazon QLDB 中的配額和限制](limits.md#limits.fixed)。

**Topics**
+ [建立時標記資料表](#working.manage-tables.tags)
+ [捨棄資料表](#working.manage-tables.drop)
+ [查詢非作用中資料表的歷史記錄](#working.history.inactive-table)
+ [重新啟用資料表](#working.history.undrop-table)

## 建立時標記資料表
<a name="working.manage-tables.tags"></a>

**注意**  
 建立時標記資料表目前僅支援`STANDARD`許可模式中的分類帳。

您可以標記資料表資源。若要管理現有資料表的標籤，請使用 AWS 管理主控台 或 API 操作 `TagResource`、 `UntagResource`和 `ListTagsForResource`。如需詳細資訊，請參閱[標記 Amazon QLDB 資源](tagging.md)。

您也可以在建立資料表時，使用 QLDB 主控台定義資料表標籤，或在 `CREATE TABLE` PartiQL 陳述式中指定它們。下列範例`Vehicle`會使用標籤 建立名為 的資料表`environment=production`。

```
CREATE TABLE Vehicle WITH (aws_tags = `{'environment': 'production'}`)
```

藉由在建立時為資源建立標籤，您可以消除在資源建立後執行自訂標籤指令碼的必要。標記資料表之後，您可以根據這些標籤控制對資料表的存取。例如，您只能授予具有特定標籤之資料表的完整存取權。如需 JSON 政策範例，請參閱 [根據資料表標籤完整存取所有動作](security_iam_id-based-policy-examples.md#security_iam_id-based-policy-examples-full-tags)。

## 捨棄資料表
<a name="working.manage-tables.drop"></a>

若要捨棄資料表，請使用基本[DROP TABLE](ql-reference.drop-table.md)陳述式。當您在 QLDB 中捨棄資料表時，您只是在停用它。

例如，下列陳述式會停用`VehicleRegistration`資料表。

```
DROP TABLE VehicleRegistration
```

`DROP TABLE` 陳述式會傳回資料表的系統指派 ID。的狀態`VehicleRegistration`現在應該位於系統目錄資料表 [information\$1schema.user\$1tables ](working.catalog.md)`INACTIVE`中。

```
SELECT status FROM information_schema.user_tables
WHERE name = 'VehicleRegistration'
```

## 查詢非作用中資料表的歷史記錄
<a name="working.history.inactive-table"></a>

除了資料表名稱之外，您也可以[歷史記錄函數](working.history.md#working.history.function)使用資料表 ID 做為第一個輸入引數來查詢 QLDB。您必須使用資料表 ID 來查詢非作用中資料表的歷史記錄。資料表停用後，您就無法再使用資料表名稱查詢其歷史記錄。

首先，查詢系統目錄資料表以尋找資料表 ID。例如，下列查詢會傳回`VehicleRegistration`資料表`tableId`的 。

```
SELECT tableId FROM information_schema.user_tables
WHERE name = 'VehicleRegistration'
```

然後，您可以使用此 ID 從 執行相同的歷史記錄查詢[查詢修訂歷史記錄](working.history.md)。以下是`ADR2Ll1fGsU4Jr4EqTdnQF`從資料表 ID 查詢文件 ID 歷史記錄的範例`5PLf9SXwndd63lPaSIa0O6`。資料表 ID 是字串常值，必須以單引號括住。

```
--replace both the table and document IDs with your values
SELECT * FROM history('5PLf9SXwndd63lPaSIa0O6', `2000T`, `2019-06-05T23:59:59Z`) AS h
WHERE h.metadata.id = 'ADR2Ll1fGsU4Jr4EqTdnQF'
```

## 重新啟用資料表
<a name="working.history.undrop-table"></a>

在 QLDB 中停用資料表後，您可以使用 [UNDROP 資料表](ql-reference.undrop-table.md)陳述式來重新啟用它。

首先，從 尋找資料表 ID`information_schema.user_tables`。例如，下列查詢會傳回`VehicleRegistration`資料表`tableId`的 。狀態應為 `INACTIVE`。

```
SELECT tableId FROM information_schema.user_tables
WHERE name = 'VehicleRegistration'
```

然後，使用此 ID 重新啟用資料表。下列範例會*捨棄*資料表 ID `5PLf9SXwndd63lPaSIa0O6`。在這種情況下，資料表 ID 是您以雙引號括住的唯一識別符。

```
UNDROP TABLE "5PLf9SXwndd63lPaSIa0O6"
```

的狀態現在`VehicleRegistration`應為 `ACTIVE`。

若要了解如何建立、描述和捨棄索引，請繼續 [管理索引](working.manage-indexes.md)。

# 管理索引
<a name="working.manage-indexes"></a>

**重要**  
支援終止通知：現有客戶將可以使用 Amazon QLDB，直到 07/31/2025 的支援結束為止。如需詳細資訊，請參閱[將 Amazon QLDB Ledger 遷移至 Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/)。

本節說明如何在 Amazon QLDB 中建立、描述和捨棄索引。您可以在 中定義每個資料表的索引數量配額[Amazon QLDB 中的配額和限制](limits.md#limits.fixed)。

**Topics**
+ [建立索引](#working.manage-indexes.create)
+ [描述索引](#working.manage-indexes.describe)
+ [捨棄索引](#working.manage-indexes.drop)
+ [常見錯誤](#working.manage-indexes.errors)

## 建立索引
<a name="working.manage-indexes.create"></a>

如 中所述[建立資料表和索引](working.create.md#working.create.tables-indexes)，您可以使用 [CREATE INDEX](ql-reference.create-index.md) 陳述式，在指定最上層欄位的資料表上建立索引，如下所示。資料表名稱和索引欄位名稱都區分大小寫。

```
CREATE INDEX ON VehicleRegistration (VIN)
```

```
CREATE INDEX ON VehicleRegistration (LicensePlateNumber)
```

您在資料表上建立的每個索引都有系統指派的唯一 ID。若要尋找此索引 ID，請參閱以下章節 [描述索引](#working.manage-indexes.describe)。

**重要**  
QLDB 需要索引才能有效率地查詢文件。如果沒有索引，QLDB 讀取文件時需要執行完整資料表掃描。這可能會導致大型資料表的效能問題，包括並行衝突和交易逾時。  
若要避免資料表掃描，您必須在索引欄位或文件 ID 上使用*等式*運算子 (`=` 或 `IN`) 搭配`WHERE`述詞子句執行陳述式。如需詳細資訊，請參閱[最佳化查詢效能](working.optimize.md)。

建立索引時請注意下列限制：
+ 索引只能在單一最上層欄位上建立。不支援複合、巢狀、唯一和以函數為基礎的索引。
+ 您可以在任何 [Ion 資料類型](ql-reference.data-types.md)上建立索引，包括 `list`和 `struct`。不過，無論 Ion 類型為何，您都只能依整個 Ion 值的相等性執行索引查詢。例如，使用 `list` 類型做為索引時，您無法透過清單中的一個項目進行索引查詢。
+ 只有在您使用等式述詞時，查詢效能才會獲得改善；例如， `WHERE indexedField = 123`或 `WHERE indexedField IN (456, 789)`。

  QLDB 不會遵守查詢述詞中的不等式。因此，不會實作範圍篩選掃描。
+ 索引欄位的名稱區分大小寫，最多可有 128 個字元。
+ QLDB 中的索引建立是非同步的。在非空白資料表上完成建立索引所需的時間，會因資料表大小而有所不同。如需詳細資訊，請參閱[管理索引](#working.manage-indexes)。

## 描述索引
<a name="working.manage-indexes.describe"></a>

QLDB 中的索引建立是非同步的。在非空白資料表上完成建立索引所需的時間，會因資料表大小而有所不同。若要檢查索引組建的狀態，您可以查詢系統目錄資料表 [information\$1schema.user\$1tables](working.catalog.md)。

例如，下列陳述式會查詢 `VehicleRegistration` 資料表上所有索引的系統目錄。

```
SELECT VALUE indexes
FROM information_schema.user_tables info, info.indexes indexes
WHERE info.name = 'VehicleRegistration'
```

```
{
    indexId: "Djg2nt0yIs2GY0T29Kud1z",
    expr: "[VIN]",
    status: "ONLINE"
},
{
    indexId: "4tPW3fUhaVhDinRgKRLhGU",
    expr: "[LicensePlateNumber]",
    status: "FAILED",
    message: "aws.ledger.errors.InvalidEntityError: Document contains multiple values for indexed field: LicensePlateNumber"
}
```

**索引欄位**
+ `indexId` – 索引的唯一 ID。
+ `expr` – 索引文件路徑。此欄位是格式為： 的字串`[fieldName]`。
+ `status` – 索引的目前狀態。索引的狀態可以是下列其中一個值：
  + `BUILDING` – 正在積極建立資料表的索引。
  + `FINALIZING` – 已完成建立索引，並開始啟用索引以供使用。
  + `ONLINE` – 處於作用中狀態，並準備好在查詢中使用。在狀態為上線之前，QLDB 不會在查詢中使用索引。
  + `FAILED` – 由於無法復原的錯誤而無法建置索引。此狀態的索引仍會計入每個資料表的索引配額。如需詳細資訊，請參閱[常見錯誤](#working.manage-indexes.errors)。
  + `DELETING` – 使用者捨棄索引後，正在主動刪除索引。
+ `message` – 描述索引具有`FAILED`狀態之原因的錯誤訊息。只有失敗的索引才包含此欄位。

### 使用主控台
<a name="working.manage-indexes.describe.con"></a>

您也可以使用 AWS 管理主控台 來檢查索引的狀態。

**檢查索引的狀態 （主控台）**

1. 登入 AWS 管理主控台，並在 [https://console.aws.amazon.com/qldb](https://console.aws.amazon.com/qldb)：// 開啟 Amazon QLDB 主控台。

1. 在導覽窗格中，選擇 **Ledgers**。

1. 在 **Ledgers** 清單中，選擇您要管理其索引的分類帳名稱。

1. 在分類帳詳細資訊頁面的**資料表**索引標籤下，選擇您要檢查其索引的資料表名稱。

1. 在資料表詳細資訊頁面上，找到**索引欄位**卡。**索引狀態**欄會顯示資料表上每個索引的目前狀態。

## 捨棄索引
<a name="working.manage-indexes.drop"></a>

使用 [DROP INDEX](ql-reference.drop-index.md)陳述式來捨棄索引。當您捨棄索引時，它會從資料表中永久刪除。

首先，從 尋找索引 ID`information_schema.user_tables`。例如，下列查詢會傳回`VehicleRegistration`資料表上索引`LicensePlateNumber`欄位`indexId`的 。

```
SELECT indexes.indexId
FROM information_schema.user_tables info, info.indexes indexes
WHERE info.name = 'VehicleRegistration' and indexes.expr = '[LicensePlateNumber]'
```

然後，使用此 ID 來捨棄索引。以下是捨棄索引 ID 的範例`4tPW3fUhaVhDinRgKRLhGU`。索引 ID 是應該以雙引號括住的唯一識別符。

```
DROP INDEX "4tPW3fUhaVhDinRgKRLhGU" ON VehicleRegistration WITH (purge = true)
```

**注意**  
所有`DROP INDEX`陳述式`WITH (purge = true)`都需要 子句，目前`true`唯一支援的值。  
關鍵字區分`purge`大小寫，且必須是所有小寫。

### 使用主控台
<a name="working.manage-indexes.drop.con"></a>

您也可以使用 AWS 管理主控台 來捨棄索引。

**捨棄索引 （主控台）**

1. 登入 AWS 管理主控台，並在 [https://console.aws.amazon.com/qldb](https://console.aws.amazon.com/qldb)：// 開啟 Amazon QLDB 主控台。

1. 在導覽窗格中，選擇 **Ledgers**。

1. 在 **Ledgers** 清單中，選擇您要管理其索引的分類帳名稱。

1. 在分類帳詳細資訊頁面的**資料表**索引標籤下，選擇您要捨棄其索引的資料表名稱。

1. 在資料表詳細資訊頁面上，找到**索引欄位**卡。選取您要捨棄的索引，然後選擇**捨棄索引**。

## 常見錯誤
<a name="working.manage-indexes.errors"></a>

本節說明您在建立索引時可能遇到的常見錯誤，並建議可能的解決方案。

**注意**  
狀態為 的索引`FAILED`仍會計入每個資料表的索引配額。失敗的索引也可讓您修改或刪除導致資料表上索引建立失敗的任何文件。  
您必須明確[捨棄](#working.manage-indexes.drop)索引，才能將其從配額中移除。

**文件包含索引欄位的多個值：*fieldName*。**  
QLDB 無法為指定的欄位名稱建立索引，因為資料表包含具有相同欄位多個值 （即重複欄位名稱） 的文件。  
您必須先捨棄失敗的索引。然後，請確定資料表中的所有文件在重試建立索引之前，每個欄位名稱都只有一個值。您也可以為沒有重複的另一個欄位建立索引。  
如果您嘗試為已在資料表上編製索引的欄位插入包含多個值的文件，QLDB 也會傳回此錯誤。

**超過索引限制：Table *tableName* 已有 *n* 個索引，且無法建立更多索引。**  
QLDB 會強制執行每個資料表的五個索引限制，包括失敗的索引。您必須先捨棄現有的索引，才能建立新的索引。

**沒有具有識別符的已定義索引：*indexId*。**  
您嘗試捨棄的索引不存在於指定的資料表和索引 ID 組合。若要了解如何檢查現有的索引，請參閱 [描述索引](#working.manage-indexes.describe)。

# Amazon QLDB 中的唯一 IDs
<a name="working.unique-id"></a>

**重要**  
支援終止通知：現有客戶將可以使用 Amazon QLDB，直到 07/31/2025 的支援結束為止。如需詳細資訊，請參閱[將 Amazon QLDB Ledger 遷移至 Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/)。

本節說明 Amazon QLDB 中系統指派的唯一識別符的屬性和用量準則。它也提供一些 QLDB 唯一 IDs的範例。

**Topics**
+ [屬性](#working.unique-id.properties)
+ [用量](#working.unique-id.usage)
+ [範例](#working.unique-id.examples)

## 屬性
<a name="working.unique-id.properties"></a>

QLDB 中所有系統指派IDs 都是通用的唯一識別碼 (UUID)。每個 ID 都有下列屬性：
+ 128 位元 UUID 號碼
+ 以 Base62-encoded的文字表示
+ 22 個字元的固定長度英數字串 （例如：`3Qv67yjXEwB9SjmvkuG6Cp`)

## 用量
<a name="working.unique-id.usage"></a>

在應用程式中使用 QLDB IDs 時，請注意下列準則：

**執行**
+ 將 ID 視為字串。

**不要**
+ 嘗試解碼字串。
+ 將語意意義指派給字串 （例如產生時間元件）。
+ 依語意順序排序字串。

## 範例
<a name="working.unique-id.examples"></a>

下列屬性是 QLDB 中唯一 IDs 的一些範例：
+ 文件 ID
+ 索引 ID
+ 字串 ID
+ 資料表 ID
+ 交易 ID