我有一个复杂的查询,我似乎无法使用正确的索引进行优化。有关如何修改查询或索引以进行优化的任何想法。 columndata
表有55000条记录,drows
d有4000条记录,其余表少于25条记录。你应该如何优化这样的问题:
SELECT DISTINCT a.id,
a.string_data,
b.grid_id,
c.data_type,
d.document_id
FROM `columndata` a,
`columngrid` b,
`dcolumns` c,
`drows` d
WHERE b.grid_id = 9
AND d.document_id = 17
AND d.id = a.row_number
AND b.column_id = a.column_id
AND c.id = a.column_id
AND 0 = (SELECT count(1) AS q
FROM `security` e,
`role_userlist` f,
`user_type_defaults`g
WHERE ((e.access_for = 1
AND e.access_for_id = 0)
OR (e.access_for = 2
AND e.access_for_id = f.role_id
AND f.userid = 0)
OR (e.access_for = 3
AND e.access_for_id = g.id
AND (g.usertype_name =""
OR (g.usertype_name = "Guest"
AND 0 = 0))))
AND e.access_level = 0
AND ((e.access_type = 2
AND e.access_subtype_grid_id = b.grid_id
AND e.access_subtype_column_id = a.column_id)
OR (e.access_type = 4
AND e.access_subtype_document_id = a.document_id
AND e.access_subtype_column_id = a.column_id)))
ORDER BY d.ordering, b.ordering LIMIT 0, 330
表
CREATE TABLE `columndata` (
`id` int(11) NOT NULL auto_increment,
`document_id` int (11) NOT NULL,
`column_id` int(11) NOT NULL,
`row_number` int(11) NOT NULL,
`string_data` varchar (5000),
PRIMARY KEY (`id`),
INDEX(`column_id`,`row_number`,`document_id`),
INDEX(`row_number`),
INDEX(`document_id`)
) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
CREATE TABLE `columngrid` (
`id` int(11) NOT NULL auto_increment,
`parent_id` int(11),
`column_id` int(11) NOT NULL,
`grid_id` int(11) NOT NULL,
`ordering` int(11) NOT NULL,
PRIMARY KEY (`id`),
INDEX (`parent_id`),
INDEX (`grid_id`,`column_id`,`ordering`)
) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
CREATE TABLE `dcolumns` (
`id` int(11) NOT NULL auto_increment,
`header` varchar(25) NOT NULL,
`data_type` varchar (25) NOT NULL default 'T',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
CREATE TABLE `drows` (
`id` int(11) NOT NULL auto_increment,
`parent_id` int(11),
`document_id` int (11) NOT NULL,
`grid_id` int(11) NOT NULL,
`ordering` int(11) NOT NULL,
PRIMARY KEY (`id`),
INDEX (`parent_id`),
INDEX (`document_id`,`id`,`ordering`)
) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
CREATE TABLE `security` (
`id` int(11) NOT NULL auto_increment,
`access_for` int(11) NOT NULL,
`access_for_id` int(11) NOT NULL,
`access_type` int(11) NOT NULL,
`access_type_id` varchar(11) NOT NULL,
`access_subtype_grid_id` int(11) NULL,
`access_subtype_column_id` int(11) NULL,
`access_subtype_document_id` int(11) NULL,
`access_level` int(4) default 0,
PRIMARY KEY (`id`),
INDEX `ind1` (`access_for`,`access_for_id`),
INDEX `ind2` (`access_type`,`access_type_id`),
INDEX `ind3` (`access_type`,`access_subtype_grid_id`,`access_subtype_column_id`),
INDEX `ind4` (`access_type`,`access_subtype_document_id`,`access_subtype_column_id`)
) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
CREATE TABLE `role_userlist` (
`id` int(11) NOT NULL auto_increment,
`userid` int(11) NOT NULL,
`role_id` int(11) NOT NULL,
`userid_assigning_role` int(11) NOT NULL,
PRIMARY KEY (`id`),
INDEX (`role_id`),
INDEX (`userid`)
) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
CREATE TABLE `#__jgrid_user_type_defaults` (
`id` int(11) NOT NULL auto_increment,
`usertype_name` varchar(25) NOT NULL,
`access_level` int(11),
PRIMARY KEY (`id`),
INDEX `ind1` (`usertype_name`)
) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
答案 0 :(得分:0)
您是否尝试使用表JOINS而不是编写无休止的where语句? 在单个查询中获取所有内容并不总是方便,因为它可以降低整体性能。
例如,我有一个复杂的搜索查询,它在7个不同的表中查找作为单个查询编写的值,搜索20000条记录大约需要5秒钟。将它分成7个较小的查询后,这些查询的总时间约为0.2秒。
答案 1 :(得分:0)
优化查询时,首先要查看数据库引擎生成的查询执行计划。
然后首先查看强制执行不良数据库的行数,即FROM子句:
FROM `columndata` a, `columngrid` b, `dcolumns` c, `drows` d
指定四个表的连接。比如说,a = 55,000行,b = 25行,c = 25行,d = 4,000行。算一算。这是展开集中的137 BILLION 行。如果您不是非常小心关于积极地修剪行和早期,那么您最终会得到一个运行速度非常慢的查询。
然后查看WHERE子句。在不查看最后一个子句(这是一个大的子查询)的情况下,您似乎将b限制为几行,并且d可能基于document_id限制为几百行。所以你看大约55,000 x 5(比如说),x 5(比方说)x 500(比如说)= 690 百万行。
显然你的查询没有返回那么多行,所以它们都在最后一个子句中被过滤掉了。但是,WHERE子句中的最后一个子句是一个子查询,它引用了另外三个不相关的表。这意味着您强制数据库引擎循环遍历690百万行,在EACH上执行一个子查询(并且它是COUNT查询,最慢的类型),然后丢弃大多数COUNT<> 0
现在你明白为什么它很慢吗?
我能想到优化的第一件事是将0=(SELECT ...
子查询替换为NOT EXISTS (SELECT...
,这会阻止子查询执行找到行,而不是强制数据库引擎计数所有这些,然后检查计数是否为零。你没有使用计数。您正在此处检查存在。使用EXISTS
。
其次,将第二个查询作为FROM子句的一部分(可能是一个WITH子句或只是放在那里)和 LEFT JOIN 它与columndata表(在左侧)一起,然后在NULL(右侧)上选择(WHERE)。这通过仅保留子查询中不存在的行来修剪columndata表(最大的一个)。数据库的查询优化器应该尝试使用JOIN重写子查询,但我不确定MySQL的优化器有多好。
答案 2 :(得分:0)
感谢您的回复。首先,我删除了递归调用,并在我的PHP代码中首先调用它,以找到一些不显示的column_numbers(整数)。然后我对结果数组进行“NOT IN”检查。见下面的电话。这个调用给出了这个解释结果。
id select_type表类型possible_keys键key_len ref行过滤了
1 SIMPLE b范围grid_id grid_id 8 NULL 6 1000.00使用where;额外使用临时;使用filesort
1 SIMPLE d ref PRIMARY document_id 4 const 4000 100.00
1 SIMPLE c eq_ref PRIMARY PRIMARY 4 b.column_id 1 100.00
1 SIMPLE a ref row_number column_id 8 c.id,d.id 1 100.00使用where
SELECT DISTINCT a.id,
a.string_data,
b.grid_id,
c.data_type,
d.document_id
来自columndata
a,
columngrid
b,
dcolumns
c,
drows
d
WHERE b.grid_id = 9
AND d.document_id = 17
AND d.id = a.row_number
AND b.column_id = a.column_id
AND c.id = a.column_id
AND a.column_id NOT IN(0,3,6)
订购,订购,订购LIMIT 0,330
我也尝试过加入,但速度要慢得多。有关如何使用正确的连接语法进行改进的评论将受到欢迎
首先选择行(4000),然后将列与行(50000)匹配,同时删除隐藏的列,然后从列和columgrid表添加额外数据
SELECT t1.row_id
t2.string_data,
2 AS grid_id,
t5.data_type,
t1.document_id
(选择DISTINCT a.id为row_id,
a.string_data,
a.ordering as row_ordering
来自drows
a
WHERE a.document_id = 7)t1
LEFT JOIN(SELECT DISTINCT b.row_id
b.column_id
b.string_data
来自dcolumns
WHERE b.column_id NOT IN(0,3,6))t2 ON t1.id = t2.row_id
LEFT JOIN(SELECT DISTINCT c.id,
c.data_type)t3 ON t2.column_id = t3.id
LEFT JOIN(SELECT DISTINCT d.column_id,
d.row_id,
d.ordering as column_ordering
FROM columngrid
)t4 ON(t1.id = t4.row_id AND t2.column_id = t4.column_id)
ORDER BY t1.row_ordering,t4.column_ordering