

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 在 Amazon Keyspaces 中使用 CQL 查询
<a name="working-with-queries"></a>

本节介绍如何使用 Amazon Keyspaces（Apache Cassandra 兼容）中的查询。可用于查询、转换和管理数据的 CQL 语句是 `SELECT`、`INSERT`、`UPDATE` 和 `DELETE`。以下主题概述了使用查询时可用的一些比较复杂的选项。有关完整的语言语法及其示例，请参阅 [Amazon Keyspaces 中的 DML（数据操作语言）语句](cql.dml.md)。

**Topics**
+ [在 Amazon Keyspaces 的查询中将 `IN` 运算符与 `SELECT` 语句配合使用](in.select.md)
+ [在 Amazon Keyspaces 中使用批处理语句](batchStatements.md)
+ [在 Amazon Keyspaces 中使用 `ORDER BY` 对结果排序](ordering-results.md)
+ [对 Amazon Keyspaces 中的结果进行分页](paginating-results.md)

# 在 Amazon Keyspaces 的查询中将 `IN` 运算符与 `SELECT` 语句配合使用
<a name="in.select"></a>

**SELECT IN**

您可以使用 `SELECT` 语句查询表中的数据，这将读取表中一行或多行的一列或多列，并返回包含与请求匹配的行的结果集。`SELECT` 语句包含一个 `select_clause`，该子句用于确定读取并在结果集中返回哪些列。该子句可包含在返回数据之前对数据进行转换的指令。可选的 `WHERE` 子句指定必须查询哪些行，该子句由作为主键一部分的列上的关系组成。Amazon Keyspaces 支持在 `IN` 子句中使用 `WHERE` 关键字。本节通过示例来说明 Amazon Keyspaces 如何处理包含 `IN` 关键字的 `SELECT` 语句。

以下示例展示了 Amazon Keyspaces 如何将包含 `IN` 关键字的 `SELECT` 语句分解为*子查询*。在此示例中，我们使用名为 `my_keyspace.customers` 的表。此表包含一个主键列 `department_id`、两个聚类列 `sales_region_id` 和 `sales_representative_id`，以及在 `customer_name` 列中包含客户姓名的一个列。

```
SELECT * FROM my_keyspace.customers;

         department_id | sales_region_id | sales_representative_id | customer_name
        ---------------+-----------------+-------------------------+--------------
          0            |        0        |            0            |    a
          0            |        0        |            1            |    b
          0            |        1        |            0            |    c
          0            |        1        |            1            |    d
          1            |        0        |            0            |    e
          1            |        0        |            1            |    f
          1            |        1        |            0            |    g
          1            |        1        |            1            |    h
```

使用此表，您可以通过在 `WHERE` 子句中使用 `IN` 关键字，运行以下 `SELECT` 语句来查找您感兴趣的部门和销售区域中的客户。下面是一个示例语句。

```
SELECT * FROM my_keyspace.customers WHERE department_id IN (0, 1) AND sales_region_id IN (0, 1);
```

Amazon Keyspaces 将此语句分为四个子查询，如以下输出所示。

```
SELECT * FROM my_keyspace.customers WHERE department_id = 0 AND sales_region_id = 0;

 department_id | sales_region_id | sales_representative_id | customer_name
---------------+-----------------+-------------------------+--------------
  0            |        0        |           0             |    a
  0            |        0        |           1             |    b

SELECT * FROM my_keyspace.customers WHERE department_id = 0 AND sales_region_id = 1;

 department_id | sales_region_id | sales_representative_id | customer_name
---------------+-----------------+-------------------------+--------------
  0            |        1        |          0              |    c
  0            |        1        |          1              |    d

SELECT * FROM my_keyspace.customers WHERE department_id = 1 AND sales_region_id = 0;

 department_id | sales_region_id | sales_representative_id | customer_name
---------------+-----------------+-------------------------+--------------
  1            |        0        |          0              |    e
  1            |        0        |          1              |    f

SELECT * FROM my_keyspace.customers WHERE department_id = 1 AND sales_region_id = 1;

 department_id | sales_region_id | sales_representative_id | customer_name
---------------+-----------------+-------------------------+--------------
  1            |        1        |           0             |    g
  1            |        1        |           1             |    h
```

使用 `IN` 关键字时，在以下任一情况下，Amazon Keyspaces 都会自动对结果进行分页：
+ 每处理 10 个子查询之后。
+ 处理 1MB 的逻辑 IO 之后。
+ 如果您配置了 `PAGE SIZE`，则 Amazon Keyspaces 会在根据设置的 `PAGE SIZE` 读取要处理的查询数量后进行分页。
+ 如果您使用 `LIMIT` 关键字来减少返回的行数，则 Amazon Keyspaces 会在根据设置的 `LIMIT` 读取要处理的查询数量后进行分页。

 下表通过一个示例来具体说明。

有关分页的更多信息，请参阅[对 Amazon Keyspaces 中的结果进行分页](paginating-results.md)。

```
SELECT * FROM my_keyspace.customers;

         department_id | sales_region_id | sales_representative_id | customer_name
        ---------------+-----------------+-------------------------+--------------
          2            |        0        |          0              |    g
          2            |        1        |          1              |    h
          2            |        2        |          2              |    i
          0            |        0        |          0              |    a
          0            |        1        |          1              |    b
          0            |        2        |          2              |    c
          1            |        0        |          0              |    d
          1            |        1        |          1              |    e
          1            |        2        |          2              |    f
          3            |        0        |          0              |    j
          3            |        1        |          1              |    k
          3            |        2        |          2              |    l
```

您可以对此表运行以下语句来查看分页的运作方式。

```
SELECT * FROM my_keyspace.customers WHERE department_id IN (0, 1, 2, 3) AND sales_region_id IN (0, 1, 2) AND sales_representative_id IN (0, 1);
```

Amazon Keyspaces 将此语句处理为 24 个子查询，因为此查询中包含的所有 `IN` 词语的笛卡尔乘积的基数为 24。

```
 department_id | sales_region_id | sales_representative_id | customer_name
---------------+-----------------+-------------------------+--------------
  0            |        0        |          0              |    a
  0            |        1        |          1              |    b
  1            |        0        |          0              |    d
  1            |        1        |          1              |    e

---MORE---
 department_id | sales_region_id | sales_representative_id | customer_name
---------------+-----------------+-------------------------+--------------
  2            |        0        |          0              |    g
  2            |        1        |          1              |    h
  3            |        0        |          0              |    j

---MORE---
 department_id | sales_region_id | sales_representative_id | customer_name
---------------+-----------------+-------------------------+--------------
  3            |        1        |          1              |    k
```

以下示例展示了如何在包含 `IN` 关键字的 `SELECT` 语句中使用 `ORDER BY` 子句。

```
SELECT * FROM my_keyspace.customers WHERE department_id IN (3, 2, 1) ORDER BY sales_region_id DESC;
        
         department_id | sales_region_id | sales_representative_id | customer_name
        ---------------+-----------------+-------------------------+--------------
          3            |        2        |          2              |    l
          3            |        1        |          1              |    k
          3            |        0        |          0              |    j
          2            |        2        |          2              |    i
          2            |        1        |          1              |    h
          2            |        0        |          0              |    g
          1            |        2        |          2              |    f
          1            |        1        |          1              |    e
          1            |        0        |          0              |    d
```

子查询按照查询中显示分区键和聚类键列的顺序进行处理。在下面的示例中，首先处理分区键值为“2”的子查询，然后处理分区键值为“3”和“1”的子查询。给定子查询的结果根据查询的排序子句（如有）或表创建期间定义的表的聚类顺序进行排序。

```
SELECT * FROM my_keyspace.customers WHERE department_id IN (2, 3, 1) ORDER BY sales_region_id DESC;

         department_id | sales_region_id | sales_representative_id | customer_name
        ---------------+-----------------+-------------------------+--------------
          2            |        2        |          2              |    i
          2            |        1        |          1              |    h
          2            |        0        |          0              |    g
          3            |        2        |          2              |    l
          3            |        1        |          1              |    k
          3            |        0        |          0              |    j
          1            |        2        |          2              |    f
          1            |        1        |          1              |    e
          1            |        0        |          0              |    d
```

# 在 Amazon Keyspaces 中使用批处理语句
<a name="batchStatements"></a>

您可以将多个`INSERT``UPDATE`、和`DELETE`操作组合成一条`BATCH`语句。 `LOGGED`批处理是默认值。

```
batch_statement ::=     BEGIN [ UNLOGGED ] BATCH
                        [ USING update_parameter( AND update_parameter)* ]
                        modification_statement ( ';' modification_statement )*
                        APPLY BATCH
modification_statement ::= insert_statement | update_statement | delete_statement
```

运行批处理语句时，驱动程序会将批处理中的所有语句合并为一个批处理操作。

要决定使用哪种类型的批量操作，可以考虑以下准则。

在以下情况下使用已记录的批次：  
+ 你需要原子交易保障。
+ 稍高的延迟是可以接受的权衡取舍。

在以下情况下使用未记录的批次：  
+ 您需要优化单分区操作。
+ 您想减少网络开销。
+ 您有高吞吐量要求。

有关批处理语句配额的信息，请参见[Amazon Keyspaces（Apache Cassandra 兼容）限额](quotas.md)。

## 未记录的批次
<a name="batchStatements-unlogged"></a>

对于**未记录的批处理**，Amazon Keyspaces 可以将多个操作作为单个请求处理，而无需维护批处理日志。在未记录的批处理操作中，有些操作可能会成功，而另一些操作则失败。当您想执行以下操作时，未记录的批次非常有用：
+ 优化单个分区内的操作。
+ 通过对相关请求进行分组来减少网络流量。

未记录批次的语法与已记录批次的语法类似，但增加了`UNLOGGED`关键字。

```
BEGIN UNLOGGED BATCH
    INSERT INTO users (id, firstname, lastname) VALUES (1, 'John', 'Doe');
    INSERT INTO users (id, firstname, lastname) VALUES (2, 'Jane', 'Smith');
APPLY BATCH;
```

## 已记录的批次
<a name="batchStatements-logged"></a>

**记录**的批处理将多个写入操作合并为一个原子操作。运行已记录的批处理时：
+ 所有行动要么一起成功，要么一起失败。
+ 该操作是同步的，并且是等性的。
+ 您可以写入多个 Amazon Keyspaces 表，前提是它们位于同一个 AWS 账户中，并且。 AWS 区域

记录的批次的延迟可能稍高一些。对于高吞吐量的应用程序，可以考虑使用未记录的批处理。

在 Amazon Keyspaces 中使用已记录的批次不会产生额外费用。您只需为作为批处理操作一部分的写入操作付费。Amazon Keyspaces 对批处理中的每一行执行两次基础写入：一次用于为批处理准备该行，另一次用于提交批处理。在为使用已记录批处理的表规划容量时，请记住，批次中的每行需要的容量是标准写入操作的两倍。例如，如果您的应用程序每秒运行一个包含三行 1KB 的记录批次，则需要预置六个写入容量单位 (WCUs)，而 WCUs对于单个写入或未记录的批次，则只需要配置三个写入容量单位。

有关定价的信息，请参阅 [Amazon Keyspaces（Apache Cassandra 兼容）定价](https://aws.amazon.com/keyspaces/pricing)。

### 批量操作的最佳实践
<a name="batchStatements-best-practice"></a>

使用 Amazon Keyspaces 批量操作时，请考虑以下推荐做法。
+ 启用自动扩展，这样您的表就有足够的吞吐量容量来处理批处理操作和记录批次的额外吞吐量要求。
+ 如果操作可以在不影响应用程序正确性的情况下独立运行，则使用单个操作或未记录的批处理。
+ 将应用程序设计为最大限度地减少对同一行的并发更新，因为同时进行的批处理操作可能会发生冲突和失败。
+ 对于没有原子性要求的高吞吐量批量数据摄取，请使用单独的写入操作或未记录的批处理。

### 一致性和并发性
<a name="batchStatements-consistency"></a>

Amazon Keyspaces 对记录的批次强制执行以下一致性和并发性规则：
+ 所有批处理操作都使用`LOCAL_QUORUM`一致性级别。
+ 影响不同行的并发批次可以同时执行。
+ 对正在进行的批处理中涉及的行的并发`INSERT``UPDATE`、或`DELETE`操作因冲突而失败。

### 支持的运算符和条件
<a name="batchStatements-operators"></a>

支持的`WHERE`子句运算符：  
+ 平等 (=)

不支持的运算符：  
+ 范围运算符 (>、<、>=、<=)
+ `IN` 运算符
+ `LIKE` 运算符
+ `BETWEEN` 运算符

在记录的批处理中不支持：  
+ 影响同一行的多条语句
+ 反击行动
+ 范围删除

### 记录的批处理语句的失败条件
<a name="batchStatement-failures"></a>

在以下任何一种情况下，记录的批处理操作都可能失败：
+ 条件表达式（如`IF NOT EXISTS`或`IF`）的计算结果为 false。
+ 一个或多个操作包含无效的参数。
+ 该请求与在同一行上运行的另一个批处理操作发生冲突。
+ 该表缺少足够的预置容量。
+ 一行超过了最大大小限制。
+ 输入数据格式无效。

### Batch 语句和多区域复制
<a name="batchStatements-multiregion"></a>

在多区域部署中：
+ 源区域操作是同步和原子的。
+ 目标区域的操作是异步的。
+ 所有批处理操作都会复制到目标区域，但在应用程序期间可能无法保持隔离。

### 监控批量操作
<a name="batchStatements-monitoring"></a>

您可以使用 Amazon CloudWatch 指标监控批量操作，以跟踪性能、错误和使用模式。Amazon Keyspaces 提供了以下 CloudWatch 指标来监控每个表的批量操作：
+ `SuccessfulRequestCount`— 跟踪成功的批量操作。
+ `Latency`— 测量批量操作性能。
+ `ConsumedWriteCapacityUnits`— 监控批量操作的容量消耗。

有关更多信息，请参阅 [Amazon Keyspaces 指标](metrics-dimensions.md#keyspaces-metrics-dimensions)。

除了 CloudWatch 指标外，您还可以使用 AWS CloudTrail 记录所有 Amazon Keyspaces API 操作。批次中的每个 API 操作都已登录， CloudTrail 因此可以更轻松地跟踪和审计 Amazon Keyspaces 表中的批量操作。

### Batch 操作示例
<a name="batchStatements-examples"></a>

以下是一个基本的已记录批处理语句的示例。

```
BEGIN BATCH
    INSERT INTO users (id, firstname, lastname) VALUES (1, 'John', 'Doe');
    INSERT INTO users (id, firstname, lastname) VALUES (2, 'Jane', 'Smith');
APPLY BATCH;
```

这是包含`INSERT``UPDATE`、和`DELETE`语句的批处理示例。

```
BEGIN BATCH
    INSERT INTO users (id, firstname, lastname) VALUES (1, 'John', 'Doe');
    UPDATE users SET firstname = 'Johnny' WHERE id = 2;
    DELETE FROM users WHERE id = 3;
APPLY BATCH;
```

这是使用客户端时间戳的批处理示例。

```
BEGIN BATCH
    INSERT INTO users (id, firstname, lastname) VALUES (1, 'John', 'Stiles') USING TIMESTAMP 1669069624;
    INSERT INTO users (id, firstname, lastname) VALUES (2, 'Jane', 'Doe') USING TIMESTAMP 1669069624;
APPLY BATCH;

BEGIN BATCH
    UPDATE users USING TIMESTAMP 1669069624 SET firstname = 'Carlos' WHERE id = 1;
    UPDATE users USING TIMESTAMP 1669069624 SET firstname = 'Diego' WHERE id = 2;
APPLY BATCH;
```

这是条件批处理的示例。

```
BEGIN BATCH
    INSERT INTO users (id, firstname, lastname) VALUES (1, 'Jane', 'Doe') IF NOT EXISTS;
    INSERT INTO users (id, firstname, lastname) VALUES (2, 'John', 'Doe') IF NOT EXISTS;
APPLY BATCH;


BEGIN BATCH
    UPDATE users SET lastname = 'Stiles' WHERE id = 1 IF lastname = 'Doe';
    UPDATE users SET lastname = 'Stiles' WHERE id = 2 IF lastname = 'Doe';
APPLY BATCH;
```

这是使用存活时间 (TTL) 的批处理示例。

```
BEGIN BATCH
    INSERT INTO users (id, firstname, lastname) VALUES (1, 'John', 'Doe') USING TTL 3600;
    INSERT INTO users (id, firstname, lastname) VALUES (2, 'Jane', 'Smith') USING TTL 7200;
APPLY BATCH;
```

这是更新多个表的批处理语句的示例。

```
BEGIN BATCH
    INSERT INTO users (id, firstname) VALUES (1, 'John');
    INSERT INTO user_emails (user_id, email) VALUES (1, 'john@example.com');
APPLY BATCH;
```

这是使用用户定义类型 (UDTs) 进行批量操作的示例。该示例假设 UDT `address` 存在。

```
BEGIN BATCH
    INSERT INTO users (id, firstname, address)
    VALUES (1, 'John', {street: '123 Main St', city: 'NYC', zip: '10001'});
    INSERT INTO users (id, firstname, address)
    VALUES (2, 'Jane', {street: '456 Oak Ave', city: 'LA', zip: '90210'});
APPLY BATCH;

BEGIN BATCH
    UPDATE users SET address.zip = '10002' WHERE id = 1;
    UPDATE users SET address.city = 'Boston' WHERE id = 2;
APPLY BATCH;
```

# 在 Amazon Keyspaces 中使用 `ORDER BY` 对结果排序
<a name="ordering-results"></a>

`ORDER BY` 子句指定 `SELECT` 语句中返回的结果的排序顺序。此语句将一系列列名称作为参数，您可以为每个列指定数据的排序顺序。您只能在排序子句中指定聚类列，不能指定非聚类列。

可用于返回的结果的两个排序顺序选项是 `ASC`（用于升序排序顺序）和 `DESC`（用于降序排序顺序）。

```
SELECT * FROM my_keyspace.my_table ORDER BY (col1 ASC, col2 DESC, col3 ASC);

         col1 | col2 | col3  
        ------+------+------
          0   |  6   |  a   
          1   |  5   |  b   
          2   |  4   |  c   
          3   |  3   |  d   
          4   |  2   |  e   
          5   |  1   |  f   
          6   |  0   |  g
```

```
SELECT * FROM my_keyspace.my_table ORDER BY (col1 DESC, col2 ASC, col3 DESC);

         col1 | col2 | col3  
        ------+------+------
          6   |  0   |  g   
          5   |  1   |  f   
          4   |  2   |  e   
          3   |  3   |  d   
          2   |  4   |  c   
          1   |  5   |  b   
          0   |  6   |  a
```

如果您未在查询语句中指定排序顺序，则使用聚类列的默认排序顺序。

您可以在排序子句中使用的可能排序顺序取决于创建表时为每个聚类列分配的排序顺序。查询结果只能按照创建表时为所有聚类列定义的顺序或与定义的排序顺序相反的顺序进行排序。不允许使用其他可能的组合。

例如，如果表的 `CLUSTERING ORDER` 是 (col1 ASC, col2 DESC, col3 ASC)，则 `ORDER BY` 的有效参数是 (col1 ASC, col2 DESC, col3 ASC) 或 (col1 DESC, col2 ASC, col3 DESC)。有关 `CLUSTERING ORDER` 的更多信息，请参阅 [CREATE TABLE](cql.ddl.table.md#cql.ddl.table.create) 下的 `table_options`。

# 对 Amazon Keyspaces 中的结果进行分页
<a name="paginating-results"></a>

当为处理 `SELECT` 语句而读取的数据超过 1MB 时，Amazon Keyspaces 会自动对 `SELECT` 语句中的结果进行*分页*。利用分页，`SELECT` 语句中的结果将分成若干“页”大小为 1MB（或更小）的数据。应用程序可以先处理第一页结果，然后处理第二页结果，依此类推。在处理返回多行的 `SELECT` 查询时，客户应始终检查分页令牌。

 如果客户提供的 `PAGE SIZE` 需要读取超过 1MB 的数据，则 Amazon Keyspaces 会根据 1MB 的数据读取增量自动将结果分成多页。

例如，如果一行的平均大小为 100KB，而您将 `PAGE SIZE` 指定为 20，则 Amazon Keyspaces 会在读取 10 行（读取 1000KB 的数据）后自动对数据进行分页。

由于 Amazon Keyspaces 根据为处理请求而读取的行数（而不是结果集中返回的行数）对结果进行分页，因此如果您运行过滤查询，某些页面可能不包含任何行。

例如，如果您将 `PAGE SIZE` 设置为 10，Keyspaces 评估需要读取 30 行才能处理您的 `SELECT` 查询，则 Amazon Keyspaces 将返回三页。如果只有一部分行与您的查询相匹配，则某些页面的行数可能少于 10。有关 `LIMIT` 查询的 `PAGE SIZE` 如何影响读取容量的示例，请参阅[估算限制查询的读取容量消耗](limit_queries.md)。

有关与 Apache Cassandra 分页的比较，请参阅[分页](functional-differences.md#functional-differences.paging)。