mysql - 在 MySQL 5.5 中,用户变量可以从上一行中选择值,但在 5.6 中,它似乎以不同的顺序运行查询的 SELECT 部分

标签 mysql

我有一个相当不寻常的问题,从本地环境迁移到临时服务器时出现。 MySQL 版本不同,但我无法找到发生此问题的任何原因。

我使用的代码是:

DECLARE _first_event_time datetime;

SELECT event_time INTO _first_event_time
FROM session_events
WHERE session_id = _session_id
ORDER BY event_id ASC LIMIT 0,1;

-- Select Data

SELECT event_id,
event_time,
timediff(event_time, _first_event_time) AS event_time_from_start,
(@previous := unix_timestamp(event_time) - @previous) AS event_duration,
(@previous := unix_timestamp(event_time)) AS previous_event_timestamp
FROM session_events e,

(SELECT @previous:=0) c
ORDER BY event_id ASC LIMIT 0, 400;

我期望得到以下结果,这是我在运行 MAMP 的 DEV 版本上运行 5.5 时得到的结果:

+----------+------------------+-----------------------+----------------+--------------------------+
| event_id |    event_time    | event_time_from_start | event_duration | previous_event_timestamp |
+----------+------------------+-----------------------+----------------+--------------------------+
|     5074 | 24/09/2015 20:35 | 00:00:00              |     1443123315 |               1443123315 |
|     5075 | 24/09/2015 20:35 | 00:00:02              |              2 |               1443123317 |
|     5076 | 24/09/2015 20:35 | 00:00:03              |              1 |               1443123318 |
|     5077 | 24/09/2015 20:35 | 00:00:03              |              0 |               1443123318 |
|     5078 | 24/09/2015 20:35 | 00:00:04              |              1 |               1443123319 |
|     5079 | 24/09/2015 20:35 | 00:00:05              |              1 |               1443123320 |
|     5080 | 24/09/2015 20:35 | 00:00:06              |              1 |               1443123321 |
|     5081 | 24/09/2015 20:35 | 00:00:06              |              0 |               1443123321 |
|     5082 | 24/09/2015 20:35 | 00:00:07              |              1 |               1443123322 |
|     5083 | 24/09/2015 20:35 | 00:00:08              |              1 |               1443123323 |
|     5084 | 24/09/2015 20:35 | 00:00:09              |              1 |               1443123324 |
|     5085 | 24/09/2015 20:35 | 00:00:10              |              1 |               1443123325 |
+----------+------------------+-----------------------+----------------+--------------------------+

但是数据却像这样输出(请注意,数据值不同,因为我在两台服务器上创建了相同的值,但您可以看到数据偏移的位置,存储过程中的代码是相同的在两台服务器上):

+----------+------------------+-----------------------+----------------+--------------------------+
| event_id |    event_time    | event_time_from_start | event_duration | previous_event_timestamp |
+----------+------------------+-----------------------+----------------+--------------------------+
|     5307 | 24/09/2015 20:32 | 00:00:00              |            -17 |               1443123174 |
|     5308 | 24/09/2015 20:33 | 00:00:07              |     1443123181 |               1443123181 |
|     5309 | 24/09/2015 20:33 | 00:00:08              |             -7 |               1443123182 |
|     5310 | 24/09/2015 20:33 | 00:00:09              |              2 |               1443123183 |
|     5311 | 24/09/2015 20:33 | 00:00:11              |              3 |               1443123185 |
|     5312 | 24/09/2015 20:33 | 00:00:12              |              3 |               1443123186 |
|     5313 | 24/09/2015 20:33 | 00:00:13              |              2 |               1443123187 |
|     5314 | 24/09/2015 20:33 | 00:00:14              |              2 |               1443123188 |
|     5315 | 24/09/2015 20:33 | 00:00:15              |              2 |               1443123189 |
|     5316 | 24/09/2015 20:33 | 00:00:15              |              1 |               1443123189 |
|     5317 | 24/09/2015 20:33 | 00:00:17              |              2 |               1443123191 |
|     5318 | 24/09/2015 20:33 | 00:00:18              |             18 |               1443123192 |
+----------+------------------+-----------------------+----------------+--------------------------+

时区不应该产生影响,因为这是从数据和存储过程运行的。我还尝试使用以下命令从时间戳更改为仅使用 datediff:

, ( TIME_TO_SEC(TIMEDIFF(event_time, @previous))) AS event_duration
, ( @previous := event_time ) AS previous_event_timestamp

进一步研究一下,问题似乎出在 ORDER BY 子句的某个地方,在该服务器上初始运行时填充 @previous 字段时,它似乎以未排序的顺序选择结果,就好像我删除了 ORDER BY 子句,然后将 @previous 设置为 event_time,然后该订单就是导致问题的订单。

如果我只运行一个没有 @previous 且没有 ORDER BY 子句的 select 语句,则 5.6 服务器返回的数据集似乎与第二个示例中显示的异常顺序相匹配,其中 @previous 似乎正在选择随机值。

我只能知道 SELECT 语句中运行数据并设置 @previous 值的部分首先运行,然后当它返回数据时运行 ORDER BY,这就是问题的根源。

浏览 MySQL 手册,我没有发现 5.5 和 5.6 之间的任何内容(至少没有找到我能够解释的原因)。我认为有一种方法可以让它发挥作用。

任何建议将不胜感激,因为我无法弄清楚为什么它按照 ORDER BY 子句之前的顺序进行选择。

最佳答案

MySQL明确警告不要在同一select中使用变量并在不同表达式中分配它们。我建议您使用子查询来执行此操作:

SELECT t.*,
       (unix_timestamp(event_time) - previous_event_timestamp) as event_duration
FROM (SELECT event_id, event_time,
             timediff(event_time, _first_event_time) AS event_time_from_start,
             if((@pp := @p) = null, null, -- never happens
                if(@p := unix_timestamp(event_time), @pp, @pp)
               ) AS previous_event_timestamp
      FROM session_events e CROSS JOIN
           (SELECT @p := 0, @pp := 0) c
      ORDER BY event_id ASC
      LIMIT 0, 400
     ) t;

if 逻辑只是将赋值放在同一语句中的一种方法。第一个将 @pp 指定为当前的先前值(在 @p 中)。第二个重新分配@p,然后返回@pp

然后外部查询对值进行计算。

documentation说:

As a general rule, other than in SET statements, you should never assign a value to a user variable and read the value within the same statement.

最好避免使用文档明确警告不要使用的“功能”。

关于mysql - 在 MySQL 5.5 中,用户变量可以从上一行中选择值,但在 5.6 中,它似乎以不同的顺序运行查询的 SELECT 部分,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32973350/

相关文章:

带有 LIKE 的 PHP MySql 搜索匹配太多行

mysql - 值不保存到 mysql 数据库中

mysql - 搜索嵌套集

php - 这是使用 PHP 将数据插入数据库的正确方法吗?

c# - 如何将列表框中列出的产品数量直接减去到我的产品数据库中? (MySQL)

PHP 未与服务器连接

mysql - 如何根据月份和年份列对 sql 表进行排序

mysql - 如何根据每个 id 更新所有行

php/mysql如何设置特定时间的功能

mysql - 如何在不检查数据库中是否存在的情况下生成 8 个字符的唯一字符串