副标题[/!--empirenews.page--]
上下文
我有一个应用程序从表中选择加权随机条目,其中前缀总和(权重)是关键部分.简化的表定义如下所示:
CREATE TABLE entries (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,weight DECIMAL(9,3),fenwick DECIMAL(9,3)
) ENGINE=MEMORY;
其中`fenwick`将值存储在`weights`的Fenwick树表示中.
让每个条目的“范围”跨越其前缀和与其前缀相加的权重.应用程序必须在0和SUM(权重)之间生成一个随机数@r,并找到其范围包含@r的条目,如下所示:
Fenwick树结合MEMORY引擎和二进制搜索,应该允许我在O(lg ^ 2(n))时间内找到适当的条目,而不是使用朴素查询的O(n)时间:
SELECT a.id-1 FROM (SELECT *,(@x:=@x+weight) AS counter FROM entries
CROSS JOIN (SELECT @x:=0) a
HAVING counter>@r LIMIT 1) a;
研究
由于多个查询的开销,我一直在尝试将前缀sum操作压缩成一个查询(而不是脚本语言中的几个数组访问).在这个过程中,我意识到传统的求和方法,即涉及按降序键顺序访问元素,只会求和第一个元素.我怀疑当WHERE子句中存在变量时,MySQL会线性地运行表.这是查询:
SELECT
SUM(1) INTO @garbage
FROM entries
CROSS JOIN (
SELECT @sum:=0,@n:=@entryid
) a
WHERE id=@n AND @n>0 AND (@n:=@n-(@n&(-@n))) AND (@sum:=@sum+entries.fenwick);
/*SELECT @sum*/
其中@entryid是我们正在计算的前缀和的条目的ID.我确实创建了一个有效的查询(以及返回整数最左边位的函数lft):
SET @n:=lft(@entryid);
SET @sum:=0;
SELECT
SUM(1) INTO @garbage
FROM entries
WHERE id=@n
AND @n<=@entryid
AND (@n:=@n+lft(@entryid^@n))
AND (@sum:=@sum+entries.fenwick);
/*SELECT @sum*/
但它只证实了我对线性搜索的怀疑. EXPLAIN查询也是如此:
+------+-------------+---------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+------+---------------+------+---------+------+--------+-------------+
| 1 | SIMPLE | entries | ALL | NULL | NULL | NULL | NULL | 752544 | Using where |
+------+-------------+---------+------+---------------+------+---------+------+--------+-------------+
1 row in set (0.00 sec)
指数:
SHOW INDEXES FROM entries;
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| entries | 0 | PRIMARY | 1 | id | NULL | 752544 | NULL | NULL | | HASH | | |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)
现在,我已经看到很多问题,询问如何消除WHERE子句中的变量,以便优化器可以处理查询.但是,如果没有id = @n,我无法想到这个查询的方式.我已经考虑将我想要求的条目的关键值放入一个表并使用连接,但我相信我会得到不良影响:要么过多的表,要么通过评估@entryid来进行线性搜索.
题
有没有办法强制MySQL使用此查询的索引?如果他们提供此功能,我甚至会尝试不同的DBMS.
最佳答案
放弃
芬威克树对我来说是新的,我发现这篇文章时才发现它们. 这里给出的结果是基于我的理解和一些研究, 但我绝不是一个芬威克树专家,我可能错过了一些东西.
参考资料
说明fenwick树是如何工作的
https://stackoverflow.com/a/15444954/1157540转载自 https://cs.stackexchange.com/a/10541/38148
https://cs.stackexchange.com/a/42816/38148
使用芬威克树
https://en.wikipedia.org/wiki/Fenwick_tree
https://en.wikipedia.org/wiki/Prefix_sum
问题1,找到给定条目的权重
鉴于下表
CREATE TABLE `entries` (
`id` int(11) NOT NULL AUTO_INCREMENT,`weight` decimal(9,3) DEFAULT NULL,`fenwick` decimal(9,3) NOT NULL DEFAULT '0.000',PRIMARY KEY (`id`)
) ENGINE=INNODB;
并且已经填充了数据(参见concat提供的http://sqlfiddle.com/#!9/be1f2/1), 如何计算给定条目@entryid的权重?
这里要理解的关键概念是,fenwick索引的结构基于id值本身的数学和按位运算.
查询通常应仅使用主键查找(WHERE ID = value).
使用排序(ORDER BY)或范围(WHERE(value1< ID)AND(ID< value2))的任何查询都会错过该点,并且不会按预期的顺序遍历树.
例如,使用密钥60:
SET @entryid := 60;
让我们用二进制分解值60
mysql> SELECT (@entryid & 0x0080) as b8,-> (@entryid & 0x0040) as b7,-> (@entryid & 0x0020) as b6,-> (@entryid & 0x0010) as b5,-> (@entryid & 0x0008) as b4,-> (@entryid & 0x0004) as b3,-> (@entryid & 0x0002) as b2,-> (@entryid & 0x0001) as b1;
+------+------+------+------+------+------+------+------+
| b8 | b7 | b6 | b5 | b4 | b3 | b2 | b1 |
+------+------+------+------+------+------+------+------+
| 0 | 0 | 32 | 16 | 8 | 4 | 0 | 0 |
+------+------+------+------+------+------+------+------+
1 row in set (0.00 sec)
换句话说,只保留位设置,我们有
32 + 16 + 8 + 4 = 60
现在,逐个删除设置的最低位以导航树:
32 + 16 + 8 + 4 = 60
32 + 16 + 8 = 56
32 + 16 = 48
32
这给出了访问元件60的路径(32,48,56,60).
注意,将60转换为(32,60)仅需要对ID值本身进行位计算:不需要访问表或数据库,并且可以在发出查询的客户端中完成此计算.
然后是元素60的分数权重
(编辑:瑞安网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|