mysql 排除行,MYSQL:如果一个连接的行匹配条件,则排除多个行

我有两张桌子.一个是用户,其中包含字段uid和名称.另一个表是users_roles,其中包含字段uid和rid(role id).

我想要检索没有任何一组提供的角色的用户列表.

例如:

uid | name

----------

1 | BOB

2 | DAVE

3 | JOHN

USERS_ROLES:

uid | rid

---------

1 | 1

1 | 2

1 | 3

2 | 1

3 | 1

我希望能够只查询没有一组rids的用户.例如,排除rid 2和3的查询应该返回DAVE和JOHN,或者排除rids(1,3)的查询应该返回nobody.

解决方法:

使用反连接模式的查询有时是最有效的:

SELECT u.uid

, u.name

FROM users u

LEFT

JOIN users_roles r

ON r.uid = u.uid

AND r.rid IN (2,3)

WHERE r.uid IS NULL

反连接模式是LEFT [outer] JOIN user_roles表以拉回所有匹配的行,AND来从没有匹配行的用户获取行. “技巧”是在WHERE子句中使用谓词排除所有匹配行,从而消除具有匹配项的用户的所有行.

使用NOT EXISTS相关子查询可以获得等效的结果集:

SELECT u.uid

, u.name

FROM users u

WHERE NOT EXISTS

( SELECT 1

FROM users_roles r

WHERE r.uid = u.uid

AND r.rid IN (2,3)

)

另一种方法是使用NOT IN,尽管有时效率较低.性能取决于许多因素.优化器可以为每个查询生成不同的执行计划.

在任何情况下,为了获得最佳性能,您需要一个索引… ON users_roles(uid)或ON users_roles(uid,rid).

跟进:

我的MySQL 5.1.34服务器上的性能测试表明,反连接查询的速度几乎是等效的NOT EXISTS和NOT IN查询的两倍. (1.091秒对2.066秒和2.020秒)

-- setup and populate test tables

CREATE TABLE t_users

( uid INT UNSIGNED NOT NULL PRIMARY KEY

, `name` VARCHAR(50)

) ENGINE=INNODB DEFAULT CHARSET=latin1 ;

CREATE TABLE t_users_roles

( uid INT UNSIGNED NOT NULL

, rid INT UNSIGNED NOT NULL

, PRIMARY KEY (uid,rid)

) ENGINE=INNODB DEFAULT CHARSET=latin1;

ALTER TABLE t_users_roles ADD CONSTRAINT FK_t_users_roles_t_users FOREIGN KEY (uid) REFERENCES t_users (uid);

CREATE INDEX t_users_ix1 ON t_users (uid,`name`);

CREATE INDEX t_users_roles_ix1 ON t_users_roles (rid,uid);

INSERT INTO t_users (uid,`name`)

SELECT d.d*100000+e.d*10000+u.d*1000+h.d*100+t.d*10+o.d+1 AS uid

, CONCAT('NAME',LPAD(d.d*100000+e.d*10000+u.d*1000+h.d*100+t.d*10+o.d+1,8,'-')) AS `name`

FROM (SELECT 0 AS d UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) o

JOIN (SELECT 0 AS d UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t

JOIN (SELECT 0 AS d UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) h

JOIN (SELECT 0 AS d UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) u

JOIN (SELECT 0 AS d UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) e

JOIN (SELECT 0 AS d UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) d

;

-- 1000000 row(s) affected.

INSERT INTO t_users_roles (uid,rid)

SELECT d.d*100000+e.d*10000+u.d*1000+h.d*100+t.d*10+o.d+1 AS uid

, r.rid

FROM (SELECT 1 AS rid UNION ALL SELECT 2 UNION ALL SELECT 3) r

CROSS

JOIN (SELECT 0 AS d UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) o

JOIN (SELECT 0 AS d UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t

JOIN (SELECT 0 AS d UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) h

JOIN (SELECT 0 AS d UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) u

JOIN (SELECT 0 AS d UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) e

JOIN (SELECT 0 AS d UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) d

WHERE (d.d*100000+e.d*10000+u.d*1000+h.d*100+t.d*10+o.d+1) % 100000 <> 0

;

-- 2999970 row(s) affected

OPTIMIZE TABLE t_users;

OPTIMIZE TABLE t_users_roles;

SHOW STATUS LIKE 'Qcache_hits' ;

-- Variable_name Value

-- ------------- ---------

-- Qcache_hits 1117342

SHOW VARIABLES LIKE 'version' ;

-- Variable_name Value

-- ------------- ------------

-- version 5.1.53-log

-- table size from the file system

$du -sh DATA/test/t_users*.ibd

72M DATA/test/t_users.ibd

133M DATA/test/t_users_roles.ibd

-- anti-join query

SELECT SQL_NO_CACHE u.uid

, u.name

FROM t_users u

LEFT

JOIN t_users_roles r

ON r.uid = u.uid

AND r.rid IN (2,3)

WHERE r.uid IS NULL ;

-- Exec: 1.095 sec

-- Exec: 1.090 sec

-- Exec: 1.091 sec

-- Exec: 1.087 sec

-- Exec: 1.090 sec

-- avg 5 executions: 1.091 sec

-- not exists query

SELECT SQL_NO_CACHE u.uid

, u.name

FROM t_users u

WHERE NOT EXISTS

( SELECT 1

FROM t_users_roles r

WHERE r.uid = u.uid

AND r.rid IN (2,3)

) ;

-- Exec: 2.071 sec

-- Exec: 2.066 sec

-- Exec: 2.059 sec

-- Exec: 2.065 sec

-- Exec: 2.070 sec

-- avg 5 executions: 2.066 sec

-- not in query

SELECT SQL_NO_CACHE u.uid

, u.name

FROM t_users u

WHERE u.uid NOT IN

( SELECT r.uid

FROM t_users_roles r

WHERE r.uid IS NOT NULL

AND r.rid IN (2,3)

) ;

-- Exec: 2.022 sec

-- Exec: 2.023 sec

-- Exec: 2.014 sec

-- Exec: 2.026 sec

-- Exec: 2.016 sec

-- avg 5 executions: 2.020 sec

SHOW STATUS LIKE 'Qcache_hits' ;

-- Variable_name Value

-- ------------- ---------

-- Qcache_hits 1117342

EXPLAIN output for three statements:

-- ANTI JOIN

id select_type table type possible_keys key key_len ref rows filtered Extra

-- ------------------ ------ -------------- ------------------------- ----------- ------- ----- ------- -------- ------------------------------------

1 SIMPLE u index t_users_ix1 57 1000423 100.00 Using index

1 SIMPLE r ref PRIMARY,t_users_roles_ix1 PRIMARY 4 u.uid 1 100.00 Using where; Using index; Not exists

-- NOT EXISTS

id select_type table type possible_keys key key_len ref rows filtered Extra

-- ------------------ ------ -------------- ------------------------- ----------- ------- ----- ------- -------- --------------------------

1 PRIMARY u index t_users_ix1 57 1000423 100.00 Using where; Using index

2 DEPENDENT SUBQUERY r ref PRIMARY,t_users_roles_ix1 PRIMARY 4 u.uid 1 100.00 Using where; Using index

-- NOT IN

id select_type table type possible_keys key key_len ref rows filtered Extra

-- ------------------ ------ -------------- ------------------------- ----------- ------- ------ ------- -------- --------------------------

1 PRIMARY u index t_users_ix1 57 1000423 100.00 Using where; Using index

2 DEPENDENT SUBQUERY r index_subquery PRIMARY,t_users_roles_ix1 PRIMARY 4 func 1 100.00 Using index; Using where

标签:sql,mysql,drupal

来源: https://codeday.me/bug/20190725/1535731.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值