[英]MySQL optimize OR with multiple tables
我试图找到一个更好的解决方案,因为实际查询花费的时间太长。
假设您有 3 个表:任务、地址、人员 现在我们有一个像“tobias london”这样的字符串 - 我们将按空格拆分字符串并得到 ["tobias","london"](当然我们可以有更复杂的包含更多单词的字符串)
现在我们想要所有的任务,在地址或人员中有匹配并且所有单词都必须匹配
实际查询示例:
SELECT
tasks.task_id
FROM
(
tasks,
(SELECT
adress_id AS id,
'adress_id' AS typ
FROM
adresses
WHERE adresses.city LIKE 'tobias%'
OR adresses.street LIKE 'tobias%'
UNION
SELECT
person_id AS id,
'person_id' AS typ
FROM
persons
WHERE persons.name LIKE 'tobias%') f0,
(SELECT
adress_id AS id,
'adress_id' AS typ
FROM
adresses
WHERE adresses.city LIKE 'london%'
OR adresses.street LIKE 'london%'
UNION
SELECT
person_id AS id,
'person_id' AS typ
FROM
persons
WHERE persons.name LIKE 'london%') f1
)
WHERE (
f0.typ = 'adress_id'
AND tasks.adress_id = f0.id
OR f0.typ = 'person_id'
AND tasks.person_id = f0.id
)
AND (
f1.typ = 'adress_id'
AND tasks.adress_id = f1.id
OR f1.typ = 'person_id'
AND tasks.person_id = f1.id
)
这是非常慢的,因为 WHERE 子句中的 OR (这只是一个有 2 个单词的例子)
有人知道如何让它比这更好吗?
编辑:根据要求一个小的 db-fiddle:
CREATE TABLE `adresses` ( `adress_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `street` varchar(200) NOT NULL, `house_number` varchar(6) NOT NULL, `postal_code` smallint(5) unsigned NOT NULL, `city` varchar(100) NOT NULL, PRIMARY KEY (`adress_id`), KEY `street` (`street`), KEY `postal_code` (`postal_code`), KEY `city` (`city`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; /*Data for the table `adresses` */ insert into `adresses`(`adress_id`,`street`,`house_number`,`postal_code`,`city`) values (1,'first','1',12345,'London'), (2,'second','2',23456,'Paris'), (3,'third','3',34567,'Okawa'), (4,'fourth','4',45678,'Berlin'), (5,'fiveth','5',56789,'ABCDE'), (6,'sixth','6',65535,'DFGHJH'); /*Table structure for table `persons` */ CREATE TABLE `persons` ( `person_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL, PRIMARY KEY (`person_id`), KEY `name` (`name`) ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; /*Data for the table `persons` */ insert into `persons`(`person_id`,`name`) values (1,'Anna'), (2,'John'), (3,'Michel'), (4,'Olivia'), (5,'Pavel'), (6,'Peter'), (7,'Sarah'), (8,'Tobias'); /*Table structure for table `tasks` */ CREATE TABLE `tasks` ( `task_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `person_id` int(10) unsigned NOT NULL, `adress_id` int(10) unsigned NOT NULL, `description` varchar(200) NOT NULL, PRIMARY KEY (`task_id`), KEY `person_id` (`person_id`), KEY `adress_id` (`adress_id`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8; /*Data for the table `tasks` */ insert into `tasks`(`task_id`,`person_id`,`adress_id`,`description`) values (1,1,2,''), (2,2,3,''), (3,3,4,''), (4,5,6,''), (5,4,1,''), (6,1,4,''), (7,3,3,''), (8,8,1,''); SELECT tasks.task_id FROM ( tasks, (SELECT adress_id AS id, 'adress_id' AS typ FROM adresses WHERE adresses.city LIKE 'tobias%' OR adresses.street LIKE 'tobias%' UNION SELECT person_id AS id, 'person_id' AS typ FROM persons WHERE persons.name LIKE 'tobias%') f0, (SELECT adress_id AS id, 'adress_id' AS typ FROM adresses WHERE adresses.city LIKE 'london%' OR adresses.street LIKE 'london%' UNION SELECT person_id AS id, 'person_id' AS typ FROM persons WHERE persons.name LIKE 'london%') f1 ) WHERE ( f0.typ = 'adress_id' AND tasks.adress_id = f0.id OR f0.typ = 'person_id' AND tasks.person_id = f0.id ) AND ( f1.typ = 'adress_id' AND tasks.adress_id = f1.id OR f1.typ = 'person_id' AND tasks.person_id = f1.id )
✓ ✓ ✓ ✓ ✓ ✓ | 任务编号 | | ------: | | 8 |
db<> 在这里摆弄
重新设计架构。 在关系数据库中使用typ
在两个表之间进行选择是一个非常糟糕的主意。 (正如你所发现的那样。)
在那之后,在可能具有“tobias%”或“london%”的列上创建FULLTEXT
索引可能会很有用。 但是问题来了,因为地址和人名在不同的表中。
如果你在同一张桌子上都有:
SELECT ...
WHERE MATCH(firstname, last name, address, city)
AGAINST("+tobias* +london*" IN BOOLEAN MODE)
并将这个索引放在桌子上
FULLTEXT(firstname, lastname, address, city)
(注意字长等方面的限制)
另一种方法
考虑制作一个可能包含两列的单独表格( Search
):
words
- 用户可能搜索的所有内容的混搭(名称、城市等)task_id
(或任何必要的键)——用于连接到其他表然后使用它(例如)这样:
SELECT ...
FROM Search
JOIN ... ON Search.task_id = ...
WHERE MATCH(Search.text) AGAINST ("+word1 +word2" IN BOOLEAN MODE)
...
它有助于在构建查询时消除任何“短”词。
插入行时,此技术需要额外的努力。 并且数据库中存在冗余数据。 (这是一个禁忌,但通常是性能所必需的。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.