Как вы подходите к оптимизации этого сложного SQL-запроса, а затем выбираете правильный индекс таблицы - PullRequest
0 голосов
/ 28 марта 2011

У меня сложный запрос, который я не могу оптимизировать с помощью правильных индексов. Любые идеи о том, как изменить запрос или индексы для оптимизации. 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 голосов
/ 28 марта 2011

При оптимизации запроса сначала посмотрите на план выполнения запроса, сгенерированный вашим механизмом базы данных.

Затем посмотрите сначала, сколько строк вы заставляете выполнять плохую базу данных, предложение FROM:

FROM  `columndata` a, `columngrid` b, `dcolumns` c, `drows` d

указывает соединение четырех таблиц.Скажем, a = 55 000 строк, b = 25 строк, c = 25 строк, d = 4000 строк.Делай математику.Это 137 МИЛЛИАРДОВ строк в расширенном наборе.Если вы не крайне осторожны в отношении агрессивного сокращения строк и рано , вы получите очень медленный запрос.

Затем посмотрите на ГДЕпункт.Не глядя на последнее предложение (которое является большим подзапросом), кажется, что вы ограничиваете b несколькими строками, а d, возможно, несколькими сотнями, основываясь на document_id.Итак, вы смотрите примерно 55 000 x 5 (скажем), x 5 (скажем) x 500 (скажем) = 690 МИЛЛИОНОВ строк.

Очевидно, ваш запрос не возвращает столько строктак что все они были отфильтрованы в последнем предложении.Однако последнее предложение в предложении WHERE является подзапросом, который ссылается еще на три несвязанных таблицы.Это означает, что вы заставляете механизм базы данных циклически проходить по 690 МИЛЛИОНАМ строк, выполняете один подзапрос в КАЖДОМ (это запрос COUNT, самый медленный вид), а затем отбрасываете большинство из них с COUNT <> 0.

Теперь вы понимаете, почему это медленно?

Первое, что я могу подумать об оптимизации, это заменить подзапрос 0=(SELECT ... на NOT EXISTS (SELECT..., который останавливает выполнение подзапроса, когда найдена строка.вместо того, чтобы принудительно заставить ядро ​​базы данных считать всех из них, и затем проверить, равен ли счетчик нулю.Вы не используете счет.Вы проверяете существование здесь.Используйте EXISTS.

Во-вторых, сделайте второй запрос частью предложения FROM (возможно, предложение WITH или просто вставьте его туда) и LEFT JOIN вместе с таблицей columndata (на левой стороне), затем выберите (ГДЕ) в NULL (справа).Это сокращает таблицу columndata (самую большую), сохраняя только те строки, которые не существуют в подзапросе.Оптимизатор запросов к базе данных должен попытаться переписать подзапрос с помощью JOIN, но я не уверен, насколько хорош оптимизатор MySQL.

0 голосов
/ 06 апреля 2011

Спасибо за ваши ответы.Сначала я удалил рекурсивный вызов и сделал его первым в своем PHP-коде, чтобы найти несколько column_numbers (целые числа), которые не должны быть показаны.Затем я делаю проверку NOT IN массива результатов.Смотри звонок ниже.Этот вызов дает результат объяснения.

id select_type тип таблицы возможный_ключ Ключ key_len ref строки, отфильтрованные
1 ПРОСТОЙ диапазон b grid_id grid_id 8 NULL 6 1000.00 Использование где;Дополнительное использование временное;Использование файловой сортировки 1 ПРОСТО 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 ПРОСТО a ref row_number column_id 8 c.id, d.id 1 100.00 Использование где

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
И a.column_id NOT IN (0,3,6) ORDER BY d.ordering, b.ordering LIMIT 0, 330

Я также пытался сделать соединение, но это было намного медленнее.Приветствуются комментарии по улучшению с правильным синтаксисом объединения.

Сначала выберите строки (4000), затем сопоставьте столбцы со строками (50000), удаляя скрытые столбцы, затем добавьте дополнительные данные из столбцов и таблиц столбцов
ВЫБРАТЬ 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.упорядочить как row_ordering FROM drows a ГДЕ a.document_id = 7) t1 левое соединение (SELECT DISTINCT b.row_id b.column_id b.string_data FROM dcolumns
ГДЕ b.column_id НЕ ВХОД (0,3,6)) t2 ON t1.id = t2.row_id ВЛЕВОJOIN (ВЫБЕРИТЕ DISTINCT c.id, c.data_type) t3 ON t2.column_id = t3.id LEFT JOIN (ВЫБЕРИТЕ DISTINCT d.column_id, d.row_id, d.упорядочить как 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

0 голосов
/ 28 марта 2011

Вы пытались использовать таблицу JOINS вместо того, чтобы писать бесконечные операторы where? Не всегда удобно извлекать все в одном запросе, поскольку это может снизить общую производительность.

например, у меня был сложный поисковый запрос, который искал значение в 7 разных таблицах, написанных как один запрос, для поиска среди 20000 записей потребовалось около 5 секунд. После разделения на 7 небольших запросов общее время таких запросов составило около 0,2 секунды.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...