你如何优化这个复杂的SQL查询,然后选择适当的表索引

时间:2011-03-28 02:52:50

标签: mysql sql optimization join indexing

我有一个复杂的查询,我似乎无法使用正确的索引进行优化。有关如何修改查询或索引以进行优化的任何想法。 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;  

3 个答案:

答案 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