### 8.3.14 TIMESTAMP 列的索引查找

[](<>)[](<>)

时间值存储在[`时间戳`](datetime.html)列作为 UTC 值，以及插入和检索的值[`时间戳`](datetime.html)列在会话时区和 UTC 之间转换。（这与由[`转换_TZ()`](date-and-time-functions.html#function_convert-tz)功能。如果会话时区是 UTC，则实际上没有时区转换。）

[](<>)

由于夏令时 (DST) 等本地时区更改的约定，UTC 和非 UTC 时区之间的转换在两个方向上并不是一对一的。不同的 UTC 值在另一个时区可能不同。以下示例显示了在非 UTC 时区中变得相同的不同 UTC 值：

```
mysql> CREATE TABLE tstable (ts TIMESTAMP);
mysql> SET time_zone = 'UTC'; -- insert UTC values
mysql> INSERT INTO tstable VALUES
       ('2018-10-28 00:30:00'),
       ('2018-10-28 01:30:00');
mysql> SELECT ts FROM tstable;
+---------------------+
| ts                  |
+---------------------+
| 2018-10-28 00:30:00 |
| 2018-10-28 01:30:00 |
+---------------------+
mysql> SET time_zone = 'MET'; -- retrieve non-UTC values
mysql> SELECT ts FROM tstable;
+---------------------+
| ts                  |
+---------------------+
| 2018-10-28 02:30:00 |
| 2018-10-28 02:30:00 |
+---------------------+
```

笔记

使用命名时区，例如`'遇见'`要么`“欧洲/阿姆斯特丹”`，时区表必须正确设置。有关说明，请参阅[第 5.1.15 节，“MySQL 服务器时区支持”](time-zone-support.html).

您可以看到两个不同的 UTC 值在转换为`'遇见'`时区。这种现象可能导致给定的不同结果[`时间戳`](datetime.html)列查询，取决于优化器是否使用索引来执行查询。

假设查询从前面显示的表中使用`在哪里`子句来搜索`ts`单个特定值的列，例如用户提供的时间戳文字：

```
SELECT ts FROM tstable
WHERE ts = 'literal';
```

进一步假设查询在以下条件下执行：

-   会话时区不是UTC，并且有DST班次。例如：

    ```
    SET time_zone = 'MET';
    ```

-   存储在中的唯一UTC值[`时间戳`](datetime.html)由于DST移位，列在会话时区中不唯一。（前面显示的示例说明了这是如何发生的。）

-   该查询指定一个搜索值，该值在会话时区中输入DST的小时内。

    在这种情况下，在`哪里`子句以不同的方式用于非索引和索引查找，并导致不同的结果：

-   如果没有索引或优化器无法使用它，则会在会话时区中进行比较。优化器执行表扫描，在其中检索每个`ts`列值，将其从UTC转换为会话时区，并将其与搜索值（也在会话时区中解释）进行比较：

    ```
    mysql> SELECT ts FROM tstable
           WHERE ts = '2018-10-28 02:30:00';
    +---------------------+
    | ts                  |
    +---------------------+
    | 2018-10-28 02:30:00 |
    | 2018-10-28 02:30:00 |
    +---------------------+
    ```

    因为`ts`如果将值转换为会话时区，则查询可以返回两个时间戳值，它们与UTC值不同，但在会话时区中相等：一个值发生在时钟更改时DST移位之前，另一个值发生在DST移位之后。

-   如果有可用的索引，则以UTC为单位进行比较。优化器执行索引扫描，首先将搜索值从会话时区转换为UTC，然后将结果与UTC索引项进行比较：

    ```
    mysql> ALTER TABLE tstable ADD INDEX (ts);
    mysql> SELECT ts FROM tstable
           WHERE ts = '2018-10-28 02:30:00';
    +---------------------+
    | ts                  |
    +---------------------+
    | 2018-10-28 02:30:00 |
    +---------------------+
    ```

    在这种情况下，（转换后的）搜索值仅与索引项匹配，而且由于存储的不同UTC值的索引项也是不同的，因此搜索值只能与其中一个匹配。

    由于优化器对非索引和索引查找的操作不同，查询在每种情况下都会产生不同的结果。非索引查找的结果返回会话时区中匹配的所有值。索引查找无法执行此操作：

-   它在存储引擎中执行，存储引擎只知道UTC值。

-   对于映射到同一UTC值的两个不同会话时区值，索引查找仅匹配相应的UTC索引项，并仅返回一行。

    在前面的讨论中，存储在`tstable`恰好由不同的UTC值组成。在这种情况下，所有使用所示形式的查询的索引最多匹配一个索引项。

    如果索引不是`唯一的`，表（和索引）可以存储给定UTC值的多个实例。例如`ts`列可能包含UTC值的多个实例`'2018-10-28 00:30:00'`。在这种情况下，使用query的索引将返回它们中的每一个（转换为MET值）`'2018-10-28 02:30:00'`在结果集中）。确实，使用索引的查询将转换后的搜索值与UTC索引项中的单个值匹配，而不是与转换为会话时区中搜索值的多个UTC值匹配。

    如果重要的是归还所有`ts`在会话时区中匹配的值，解决方法是禁止使用带有`忽略索引`提示：


```
mysql> SELECT ts FROM tstable
       IGNORE INDEX (ts)
       WHERE ts = '2018-10-28 02:30:00';
+---------------------+
| ts                  |
+---------------------+
| 2018-10-28 02:30:00 |
| 2018-10-28 02:30:00 |
+---------------------+
```

在其他情况下，如使用[`来自_UNIXTIME（）`](date-and-time-functions.html#function_from-unixtime)和[`UNIX_时间戳（）`](date-and-time-functions.html#function_unix-timestamp)功能。看见[第12.7节，“日期和时间函数”](date-and-time-functions.html).
