MySQL间隔变量和子查询表示法
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL间隔变量和子查询表示法相关的知识,希望对你有一定的参考价值。
我继承了这个mysql查询作为一些遗留代码:
SELECT
HardwareAddress, CONV(SUBSTRING(EventValue,3,2), 16, 10) AS 'Algorithm'
FROM ( SELECT @prev := '') init
JOIN
( SELECT HardwareAddress != @prev AS first,
@prev := HardwareAddress,
HardwareAddress, EventValue, ID
FROM Events
WHERE Time > {unixtime}
AND EventType = 104
AND HardwareAddress IN ({disps})
ORDER BY
HardwareAddress,
ID DESC
) x
WHERE first;
{unixtime}
和{disps}
是填充Python String.format()方法的变量。
我很难从这个查询中创建新功能,因为没有其他人理解它是如何工作的,而且我还没有找到足够的文档。我所知道的是,查询从IoT设备发送的长十六进制字符串中提取一个名为“algorithm”的值列表。
我大致了解子选择和区间变量是如何工作的,但有很多我不明白。 FROM (SELECT @prev := '') init
线如何工作?查询的最后两行也让我感到困惑。当没有任何东西引用时,为什么子查询别名为x
,WHERE first
究竟是什么意思?
如果有人能够指导我完成这段代码的工作,我将非常感激。
让我们将SQL分解成几部分。整体的胆量是JOINed子查询:
SELECT
HardwareAddress != @prev AS first,
@prev := HardwareAddress,
HardwareAddress,
EventValue,
ID
FROM Events
WHERE
Time > {unixtime}
AND EventType = 104
AND HardwareAddress IN ({disps})
ORDER BY HardwareAddress, ID DESC
- 第1列:不知道(还)是什么
@prev
,我们看到运营商是!=
。这意味着无论其操作数是什么,第1列都将是二进制值。在MySQL中,1
或0
。 - 第2列:将SQL变量
@prev
设置为当前匹配行的值。讨论如下,但查询的结果将始终是NULL
。 - 第3,4,5栏:我假设自我解释。
- 约束1,2,3:我假设自我解释。
- ORDER BY:值得注意的是,查询按第3列
HardwareAddress
排序结果,然后ID
降序。
然后第一列是布尔值,表示此行的HardwareAddress
列是否与前一行的HardwareAddress
列相同。在上下文中,1意味着这是ORDER BY
的第一行+-------+--------------------------+-------------------+------------+-----+
| first | @prev := HardwareAddress | HardwareAddress | EventValue | ID |
+-------+--------------------------+-------------------+------------+-----+
| 1 | NULL | ff:ff:9d:5f:f5:01 | ... | 10 |
| 0 | NULL | ff:ff:9d:5f:f5:01 | ... | 9 |
| 0 | NULL | ff:ff:9d:5f:f5:01 | ... | 8 |
| 0 | NULL | ff:ff:9d:5f:f5:01 | ... | 7 |
| 1 | NULL | ff:ff:9d:5f:f5:02 | ... | 200 |
| 0 | NULL | ff:ff:9d:5f:f5:02 | ... | 37 |
| 0 | NULL | ff:ff:9d:5f:f5:02 | ... | 24 |
| 0 | NULL | ff:ff:9d:5f:f5:02 | ... | 23 |
| 0 | NULL | ff:ff:9d:5f:f5:02 | ... | 22 |
| 1 | NULL | ff:ff:9d:5f:f5:03 | ... | 152 |
| ... | NULL | ff:ff:9d:..:..:.. | ... | ... |
| ... | NULL | ff:ff:9d:..:..:.. | ... | ... |
| ... | NULL | ff:ff:9d:..:..:.. | ... | ... |
+-----+----------------------------+-------------------+------------+-----+
。
因此,查询将返回如下结果:
WHERE first
将它与外部查询的约束放在一起,+-------+--------------------------+-------------------+------------+-----+
| first | @prev := HardwareAddress | HardwareAddress | EventValue | ID |
+-------+--------------------------+-------------------+------------+-----+
| 1 | NULL | ff:ff:9d:5f:f5:01 | ... | 10 |
| 1 | NULL | ff:ff:9d:5f:f5:02 | ... | 200 |
| 1 | NULL | ff:ff:9d:5f:f5:03 | ... | 152 |
+-----+----------------------------+-------------------+------------+-----+
和最终结果将是:
FROM (SELECT @prev := '') init
换句话说,整个查询尝试以给定的顺序获取每个HardwareAddress的第一个。神奇的@prev
?这只是初始化SQL变量... ID DESC) x
以便在随后的子查询中使用。尾部x
部分将内部查询别名为SELECT
HardwareAddress,
CONV(SUBSTRING(EventValue,3,2), 16, 10) AS 'Algorithm',
MAX(ID) AS ID
FROM Events
WHERE
Time > {unixtime}
AND EventType = 104
AND HardwareAddress IN ({disps})
GROUP BY 1, 2;
。有人可能会利用这些别名在更复杂的查询上进行进一步的连接,但在这种情况下,它们是出于MySQL语法的原因。你可以忽略它们。
总而言之,这是一种获取与每个HardwareAddress关联的最大ID的非常低效的方法。如果查询需要每列的最大值,那么只需直接询问MAX。考虑:
SELECT HardwareAddress, CONV(...) AS 'Algorithm'
FROM (SELECT ...) x
您的输出中将有一个额外的ID;如果代码太脆弱而无法处理新列,您可以像原始查询使用子选择一样掩盖它:
AND EventType
尽管AND HardwareAddress
和ID
具有选择性,但这应该是一个更有效的查询。如果init
列有一个索引,那就更好了。
所有子查询必须是别名。所有SET @prev := '';
子查询都会初始化session / @变量(它相当于在运行查询之前执行FROM ( SELECT ... ) init
JOIN ( SELECT ... ) x
)。
FROM ( SELECT ... ) AS init
JOIN ( SELECT ... ) AS x
可以写
init
后一种语法有助于暗示x
和ON
是子查询的“别名”。这些别名通常在查询的其他地方需要,但在这种情况下不需要,尤其是当存在SELECT @prev := ''
子句时。 (仍然是强制性的。)实际使用的名称并不重要。
@prev
只返回一行;它并没有真正使用。它具有将“用户变量”FROM
分配给空字符串的副作用。外部查询依赖于在另一个子查询之前执行的子查询。
顺便说一下,这两个子查询称为“派生”表,因为它们位于JOIN
或SELECT HardwareAddress != @prev AS first, -- Outputs 1 or 0
@prev := HardwareAddress, -- Outputs HA, and sets @prev
...
ORDER BY
HardwareAddress, -- to go thru in order
ID DESC
之后。
1
当first
有“变化”时,HardwareAddress
会显示为1
。目的是让0
在第一行,然后是1
的一堆行,然后返回HardwareAddress, EventValue, ID
以获得不同的地址。这两个表达式构成了实现该目标的“模式”。
HardwareAddress
最后,你可以看到@prev
(以及其他一些东西)。
在您的应用程序代码中格式化输出真的会更好,而不是尝试在SQL中执行WHERE first
游戏。
first
注意,0表示FALSE,其他任何表示TRUE。 1
是上面讨论的专栏的别名。所以,当它是GROUP BY
时,它显示;否则它会被跳过。
效果是显示每个组的第1个,按HardwareAddress分组。顺便说一下,简单的groupwise max是不可能的;需要某种形式的诡计。 (参见我对SELECT
HardwareAddress,
的讨论。)
HardwareAddress
第二个派生表必须有一些额外的列。拥有这个外部查询可以让你扔掉那些额外的东西并专注于所需的输出 - 即first
,而不是HardwareAddress
,而不是SELECT ...
CONV(SUBSTRING(EventValue,3,2), 16, 10) AS 'Algorithm'
的额外副本。
EventValue
这将, ID
的一部分从十六进制转换为十进制。表达式可以在派生表中完成;它并没有太大的区别。
WHERE Time > {unixtime}
AND EventType = 104
AND HardwareAddress IN ({disps})
这似乎是虚假信息,后来被扔掉了。
WHERE
(我假设你理解INDEX(EventType, Time),
INDEX(EventType, HardwareAddress)
条款。)我推荐以下内容来帮助提高性能,特别是如果表格很大:
qazxswpoi
以上是关于MySQL间隔变量和子查询表示法的主要内容,如果未能解决你的问题,请参考以下文章