我正在构建一个Web应用程序,它具有多种类型的对象以及这些对象之间的众多关系。每种类型的对象我给出了一个3位数的代码(即" TRA"," COM"," APR"," CRI&# 34;等等)。我有一个连接表,询问类型,然后是应该链接在一起的主要和次要对象的type_id。
CREATE TABLE `obj_rels` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`pri_type` varchar(3) DEFAULT NULL,
`pri_type_id` int(11) DEFAULT NULL,
`sec_type` varchar(3) DEFAULT NULL,
`sec_type_id` int(11) DEFAULT NULL,
`effective_on` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`trashed_by` int(11) DEFAULT NULL,
`trashed_on` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `types` (`pri_type`,`sec_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
我玩了很多不同类型的索引,但是这个表上的查询正在减慢应用程序的速度。
条目只添加一次,因此只用一条记录定义关系,例如pri_type = FIL,pri_type_id = 123,sec_type = TRA,sec_type_id = 456 ...如果我需要搜索与特定相关的任何内容因此,TRA需要搜索主要和次要类型/ type_id才能找到它们。我使用的查询是:
SELECT *
FROM (
SELECT pri_type, pri_type_id, sec_type, sec_type_id, effective_on, trashed_by, trashed_on
FROM obj_rels
UNION
SELECT sec_type AS pri_type, sec_type_id AS pri_type_id, pri_type AS sec_type, pri_type_id AS sec_type_id, effective_on, trashed_by, trashed_on
FROM obj_rels
) AS qry
WHERE pri_type = 'TRA' AND pri_type_id = 21375 AND (trashed_on = 0 OR ISNULL(trashed_on))
但是这个查询需要大约1.5秒才能在表中运行大约71,000条记录,而我所拥有的其他查询依赖于此查询,因此它们需要4-5秒才能运行。
如何最好地设置索引或调整查询以优化关系结果?
提前致谢!
答案 0 :(得分:1)
使用当前表设计获得的最快速度是完全消除子查询和联合,并使用IF语句获取动态列,如下所示:
SELECT
IF(pri_type = 'TRA' AND pri_type_id = 21375, pri_type, sec_type) AS pri_type,
IF(pri_type = 'TRA' AND pri_type_id = 21375, pri_type_id, sec_type_id) AS pri_type_id,
IF(pri_type = 'TRA' AND pri_type_id = 21375, sec_type, pri_type) AS sec_type,
IF(pri_type = 'TRA' AND pri_type_id = 21375, sec_type_id, pri_type_id) AS sec_type_id,
effective_on,
trashed_by,
trashed_on
FROM obj_rels
WHERE (trashed_on = 0 OR trashed_on IS NULL)
AND (
(pri_type = 'TRA' AND pri_type_id = 21375)
OR (sec_type = 'TRA' AND sec_type_id = 21375)
);
这将导致行与自身联合的行数的1/2,并且将为那些大量令人讨厌的数据避免令人讨厌的临时表。
当然,如果您为搜索列编制索引,您将会变得非常快:
ALTER TABLE obj_rels
ADD INDEX (pri_type),
ADD INDEX (pri_type_id),
ADD INDEX (sec_type),
ADD INDEX (sec_type_id),
ADD INDEX (trashed_on);
PS - 请注意,我已将ISNULL
函数调用更改为trashed_on IS NULL
。前者是COALESCE
别名(COALESCE是首选,顺便说一句,因为它适用于其他RDBMS),后者是比较。如果您想使用前者,可以说WHERE COALESCE(trashed_on, 0) = 0
来处理这两种情况。
答案 1 :(得分:0)
UNION可以在两个更简单的可索引查询之间进行组合。您可以消除表扫描,并在应用相应的WHERE子句后组合两个较小的中间结果,这些子句受益于两个不同的索引。
但是你误解了这种技巧。您执行此操作的方式会两次读取表中的每个行,创建一个包含142,000行的临时表,然后将您的条件应用于该临时表。
所以改为以这种方式编写查询:
(
SELECT pri_type, pri_type_id, sec_type, sec_type_id, effective_on, trashed_by, trashed_on
FROM obj_rels
WHERE pri_type = 'TRA' AND pri_type_id = 21375 AND trashed_on IS NULL
)
UNION ALL
(
SELECT sec_type, sec_type_id, pri_type, pri_type_id, effective_on, trashed_by, trashed_on
FROM obj_rels
WHERE sec_type = 'TRA' AND sec_type_id = 21375 AND trashed_on IS NULL
)
重复类似的WHERE子句而不是编写一个WHERE子句似乎是违反直觉的,但目的是使用相应的索引来减少匹配行的集合,然后使用UNION这些较小的行集。这比创建表中总行数的2倍的临时表要好得多,然后将WHERE子句应用于临时表。
要优化每个子查询,请创建以下索引:
ALTER TABLE obj_rels
ADD KEY (pri_type_id, pri_type, trashed_on),
ADD KEY (sec_type_id, sec_type, trashed_on);
UNION中的每个查询都使用相应的索引。
我首先放置type_id
列,因为我认为它们比type
列更具选择性。
我还会对应用程序进行更改,以确保trashed_on永远不会为0.如果没有有效的日期时间,请使用NULL。原因是允许索引包含trashed_on。我不确定它是否会使用OR
将索引应用于更复杂的表达式。
使用UNION ALL而不是UNION消除了UNION对总结果进行排序以消除重复的步骤。如果您想要消除重复,请忽略该更改。