CREATE TABLE userrole (
userid INT,
roleid INT,
PRIMARY KEY (userid, roleid)
);
CREATE INDEX ON userrole (roleid);
字符串 运行以下命令:
<?php
ini_set('max_execution_time', 120); // takes over a minute to insert 500k+ records
$start = microtime(true);
echo "<pre>\n";
mysql_connect('localhost', 'scratch', 'scratch');
if (mysql_error()) {
echo "Connect error: " . mysql_error() . "\n";
}
mysql_select_db('scratch');
if (mysql_error()) {
echo "Selct DB error: " . mysql_error() . "\n";
}
$users = 200000;
$count = 0;
for ($i=1; $i<=$users; $i++) {
$roles = rand(1, 4);
$available = range(1, 5);
for ($j=0; $j<$roles; $j++) {
$extract = array_splice($available, rand(0, sizeof($available)-1), 1);
$id = $extract[0];
query("INSERT INTO userrole (userid, roleid) VALUES ($i, $id)");
$count++;
}
}
$stop = microtime(true);
$duration = $stop - $start;
$insert = $duration / $count;
echo "$count users added.\n";
echo "Program ran for $duration seconds.\n";
echo "Insert time $insert seconds.\n";
echo "</pre>\n";
function query($str) {
mysql_query($str);
if (mysql_error()) {
echo "$str: " . mysql_error() . "\n";
}
}
?>
型 输出量:
499872 users added.
Program ran for 56.5513510704 seconds.
Insert time 0.000113131663847 seconds.
型 这增加了500,000个随机用户角色组合,并且大约有25,000个符合所选标准。 第一个查询:
SELECT userid
FROM userrole
WHERE roleid IN (1, 2, 3)
GROUP by userid
HAVING COUNT(1) = 3
型 查询时间:0.312s
SELECT t1.userid
FROM userrole t1
JOIN userrole t2 ON t1.userid = t2.userid AND t2.roleid = 2
JOIN userrole t3 ON t2.userid = t3.userid AND t3.roleid = 3
AND t1.roleid = 1
create table RoleGroup(
roleid int not null,
primary key(roleid)
)
insert into RoleGroup values (1);
insert into RoleGroup values (2);
insert into RoleGroup values (3);
select userid from Users as U
where not exists (
select * from RoleGroup as G
where not exists (
select R.roleid from UserRole as R
where R.roleid = G.roleid
and R.userid = U.userid
)
);
型 另一种写法是这样的
select userid from Users as U
where not exists (
select * from RoleGroup as G
where G.roleid not in (
select R.roleid from UserRole as R
where R.userid = U.userid
)
);
select userid from userrole where userid = 1
intersect
select userid from userrole where userid = 2
intersect
select userid from userrole where userid = 3
6条答案
按热度按时间1qczuiv01#
好吧,我在这个问题上被否决了,所以我决定测试一下:
字符串
运行以下命令:
型
输出量:
型
这增加了500,000个随机用户角色组合,并且大约有25,000个符合所选标准。
第一个查询:
型
查询时间:0.312s
型
查询时间:0.016s
是的。我提出的联接版本比聚合版本快20倍。
对不起,但我这样做是为了在真实的世界中谋生和工作,在现实世界中,我们测试SQL,结果不言自明。
原因应该很清楚。聚合查询的开销将随表的大小而变化。每一行都通过
HAVING
子句进行处理、聚合和筛选(或不进行筛选)。联接版本将(使用索引)基于给定的角色选择用户的子集,然后根据第二个角色检查该子集,最后根据第三个角色检查该子集。每一个selection(用relational algebra术语)都在一个越来越小的子集上工作。由此可以得出结论:join版本的性能更好,匹配的发生率更低。
如果只有500个用户(在上面的500k样本中)具有三个指定的角色,则join版本将明显更快。聚合版本不会(任何性能改进都是传输500个用户而不是25k的结果,加入版本显然也得到了)。
我也很想知道一个真实的的数据库(即Oracle)将如何处理这一点。因此,我基本上在OracleXE上重复了相同的练习(在与前面示例中的MySQL相同的Windows XP桌面计算机上运行),结果几乎相同。
联接似乎不受欢迎,但正如我所演示的,聚合查询可能会慢一个数量级。
**更新:**经过广泛的测试,情况会更加复杂,答案将取决于您的数据,数据库和其他因素。这个故事的寓意是测试,测试,测试。
whitzsjs2#
字符串
只是大声地想一想,Cletus描述的自连接的另一种写法是:
型
这对你来说可能更容易阅读,MySQL支持这样的元组比较。MySQL还知道如何为这个查询智能地使用覆盖索引。只需通过
EXPLAIN
运行它,并在所有三个表的注解中看到“使用索引”,这意味着它正在阅读索引,甚至不必接触数据行。我在我的MacBook上使用MySQL 5.1.48运行了这个查询超过210万行(Stack Overflow July数据转储 PostTags),它在1.08秒内返回了结果。在一个有足够内存分配给
innodb_buffer_pool_size
的体面服务器上,它应该更快。任何阅读这篇文章的人:我的回答简单明了,得到了‘接受’的状态,但请一定去读一下克莱图斯给出的答案。它有更好的性能。
abithluo3#
经典的方法是将其视为关系除法问题。
在英语中:选择所需roleid值均未缺失的用户。
我假设你有一个UserRole表引用的Users表,并且我假设所需的roleid值在一个表中:
字符串
我还将假设所有相关列都不可为NULL,因此IN或NOT EXISTS不会有什么意外。下面是一个SQL查询,它表达了上面的英语:
型
另一种写法是这样的
型
这最终可能是有效的,也可能不是有效的,这取决于索引、平台、数据等。在网上搜索“关系部门”,你会发现很多。
ttisahbt4#
假设userid、roleid包含在唯一索引中(这意味着不能有2条记录,其中userid = x,roleid = 1
字符串
xqkwcwgp5#
字符串
这不是解决问题吗?在典型的关系型数据库中,这是一个多么好的解决方案?查询优化器会自动优化这个吗?
zte4gxcn6#
如果你需要任何类型的通用性(不同的3角色组合或不同的n角色组合)...我建议你为你的角色使用一个位掩码系统,并使用位运算符来执行你的查询...