select符合不同行的不同条件的值?
这是一个非常基本的查询,我无法弄清楚….
比方说,我有这样的两列表:
userid | roleid --------|-------- 1 | 1 1 | 2 1 | 3 2 | 1
我想获得所有不同的roleids
有roleids
1,2和3.使用上面的例子,我想要返回的唯一结果是userid
1.我该怎么做?
SELECT userid FROM UserRole WHERE roleid IN (1, 2, 3) GROUP BY userid HAVING COUNT(DISTINCT roleid) = 3;
对任何人来说:我的回答很简单直接,并得到了“接受”的地位,但是请阅读@cletus给出的答案。 它有更好的performance。
只要大声想想,另一种写@cletus描述的自连接的方法是:
SELECT t1.userid FROM userrole t1 JOIN userrole t2 ON t1.userid = t2.userid JOIN userrole t3 ON t2.userid = t3.userid WHERE (t1.roleid, t2.roleid, t3.roleid) = (1, 2, 3);
这可能会更容易阅读,MySQL支持比较这样的元组。 MySQL也知道如何为这个查询智能地使用覆盖索引。 通过EXPLAIN
运行它,查看所有三个表的注释中的“使用索引”,这意味着它正在读取索引,甚至不必触摸数据行。
我使用MySQL 5.1.48在我的Macbook上运行了210万行查询(Stack Overflow July数据转储的PostTags),并在1.08秒内返回了结果。 在一个体面的服务器上有足够的内存分配给innodb_buffer_pool_size
,它应该更快。
好吧,我对此感到低落,所以我决定testing一下:
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个随机的用户angular色组合,大约有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
查询时间:0.016s
那就对了。 我提出的连接版本比汇总版本快20倍。
对不起,我只是为了生活,在真实的世界中工作,在现实世界中testingSQL,结果就说明了一切。
这个原因应该很清楚。 聚合查询将按照表的大小进行扩展。 每行都通过HAVING
子句进行处理,汇总和过滤(或不)。 连接版本将(使用索引)根据给定angular色select用户的子集,然后根据第二个angular色检查该子集,最后针对第三个angular色检查该子集。 每个select (在关系代数术语中)都在一个越来越小的子集上工作。 从这里你可以得出结论:
连接版本的性能更好,匹配率更低。
如果只有500个用户(在上面的500k样本中)有三个angular色,那么join版本将会显着加快。 汇总版本不会(并且任何性能改进都是由500个用户而不是25K来完成的,而连接版本显然也是这样)。
我也很好奇看到一个真正的数据库(如Oracle)将如何处理这个问题。 所以我基本上重复了在Oracle XE上的相同练习(与上一个示例中的MySQL相同的Windows XP桌面机器上运行),结果几乎相同。
join似乎不被赞成,但正如我已经certificate,聚合查询可以慢一个数量级。
更新:经过一些广泛的testing ,图片更复杂,答案将取决于您的数据,数据库和其他因素。 故事的寓意是testing,testing,testing。
假设userid,roleid包含在唯一的索引中(意思是不能有2个logging,其中userid = x和roleid = 1
select count(*), userid from t where roleid in (1,2,3) group by userid having count(*) = 3
经典的做法是将其视为关系分割问题。
用英语:select那些没有缺less所需angular色值的用户。
我假设你有一个UserRole表所引用的Users表,我假设所需的roleid值在一个表中:
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);
我也会假设所有相关的列都是不可空的,所以IN或NOT EXISTS都没有意外。 以下是表示上述英文的SQL查询:
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 ) );
根据索引,平台,数据等的不同,这可能会也可能不会有效。在Web上search“关系部门”,您会发现很多。
select userid from userrole where userid = 1 intersect select userid from userrole where userid = 2 intersect select userid from userrole where userid = 3
这不会解决问题吗? 在典型的关系数据库上,这个解决scheme有多好? 查询优化器会自动优化这个?
如果你在这里需要任何一种普遍性(不同的3angular色组合或不同的Nangular色组合)…我build议你使用一个位掩码系统为您的angular色,并使用按位运算符来执行您的查询…