Преобразование запроса UNION в MySQL - PullRequest
1 голос
/ 02 октября 2008

У меня есть очень большая таблица (8 ГБ) с информацией о файлах, и мне нужно создать для нее отчет, который бы выглядел примерно так:

(select * from fs_walk_scan where file_path like '\\\\server1\\groot$\\%' order by file_size desc limit 0,30)
UNION ALL
(select * from fs_walk_scan where file_path like '\\\\server1\\hroot$\\%' order by file_size desc limit 0,30)
UNION ALL
(select * from fs_walk_scan where file_path like '\\\\server1\\iroot$\\%' order by file_size desc limit 0,30)
UNION ALL
(select * from fs_walk_scan where file_path like '\\\\server2\\froot$\\%' order by file_size desc limit 0,30)
UNION ALL
(select * from fs_walk_scan where file_path like '\\\\server2\\groot$\\%' order by file_size desc limit 0,30)
UNION ALL
(select * from fs_walk_scan where file_path like '\\\\server3\\hroot$\\%' order by file_size desc limit 0,30)
UNION ALL
(select * from fs_walk_scan where file_path like '\\\\server4\\iroot$\\%' order by file_size desc limit 0,30)
UNION ALL
(select * from fs_walk_scan where file_path like '\\\\server5\\iroot$\\%' order by file_size desc limit 0,30)
[...]
order by substring_index(file_path,'\\',4), file_size desc

Этот метод выполняет то, что мне нужно сделать: получить список из 30 самых больших файлов для каждого тома. Тем не менее, это смертельно медленно, и «подобные» поиски жестко закодированы, даже если они находятся за другим столом и могут быть получены таким образом.

То, что я ищу, это способ сделать это, не проходя через огромный стол несколько раз. У кого-нибудь есть идеи?

Спасибо.

P.S. Я никак не могу изменить структуру огромной исходной таблицы.

Обновление: существуют индексы для file_path и file_size, но каждый из этих под (?) Запросов все еще занимает около 10 минут, и мне нужно сделать как минимум 22

Ответы [ 6 ]

2 голосов
/ 03 октября 2008

Вы можете использовать регулярное выражение:

select * from fs_walk_scan
  where file_path regexp '^\\\\server(1\\[ghi]|2\\[fg]|3\\h|[45]\\i)root$\\'

В противном случае, если вы можете изменить структуру таблицы, добавьте два столбца для хранения имени сервера и базового пути (и индексирования их), чтобы можно было создать более простой запрос:

select * from fs_walk_scan
  where server = 'server1' and base_path in ('groot$', 'hroot$', 'iroot$')
     or server = 'server2' and base_path in ('froot$', 'groot$')

Вы можете настроить триггер для инициализации полей при вставке записи или выполнить массовое обновление после этого, чтобы заполнить два дополнительных столбца.

2 голосов
/ 02 октября 2008

Какие у вас индексы на этой таблице? Этот индекс:

CREATE INDEX fs_search_idx ON fs_walk_scan (file_path, file_size desc)

значительно ускорит этот запрос ... если у вас его еще нет.

Обновление:

Вы сказали, что уже есть индексы для file_path и file_size ... это отдельные индексы? Или есть один индекс с обоими столбцами, проиндексированными вместе? Разница будет огромной для этого запроса. Даже с 22 подзапросами, если они проиндексированы правильно, это должно быть быстро.

1 голос
/ 03 октября 2008

Попробуйте это.
Вы хотите получить каждую запись, где есть менее 30 записей с большим размером файла и тем же путем к файлу.

SELECT * 
FROM   fs_walk_scan a
WHERE  ( SELECT COUNT(*) 
         FROM   fs_walk_scan b 
         WHERE  b.file_size  > a.file_size 
         AND    b.file_path  = a.file_path
       ) < 30

Edit:

Очевидно, это работает как собака. Итак ... Как насчет этого циклического синтаксиса?

SELECT DISTINCT file_path
INTO tmp1
FROM   fs_walk_scan a

DECLARE path VARCHAR(255);

SELECT MIN(file_path)
INTO   path
FROM   tmp1 

WHILE  path IS NOT NULL DO
    SELECT * 
    FROM   fs_walk_scan
    WHERE  file_path = path
    ORDER BY file_size DESC
    LIMIT 0,30

    SELECT MIN(file_path)
    INTO   path
    FROM   tmp1
    WHERE  file_path > path 
END WHILE

Идея в том, чтобы 1. получить список путей к файлам 2. цикл, выполняя запрос для каждого пути, который получит 30 самых больших размеров файлов.

(я посмотрел синтаксис, но я не очень разбираюсь в MySQL, поэтому прошу прощения, если его там нет. Не стесняйтесь редактировать / комментировать)

1 голос
/ 03 октября 2008

Вы можете сделать что-то вроде этого ... при условии, что fs_list имеет список ваших запросов "LIKE":

DELIMITER $$

DROP PROCEDURE IF EXISTS `test`.`proc_fs_search` $$
CREATE PROCEDURE `test`.`proc_fs_search` ()
BEGIN

DECLARE cur_path VARCHAR(255);
DECLARE done INT DEFAULT 0;


DECLARE list_cursor CURSOR FOR select file_path from fs_list;

DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

SET @sql_query = '';

OPEN list_cursor;

REPEAT
  FETCH list_cursor INTO cur_path;

  IF NOT done THEN
    IF @sql_query <> '' THEN
      SET @sql_query = CONCAT(@sql_query, ' UNION ALL ');
    END IF;

    SET @sql_query = CONCAT(@sql_query, ' (select * from fs_walk_scan where file_path like ''', cur_path , ''' order by file_size desc limit 0,30)');
  END IF;

UNTIL done END REPEAT;

SET @sql_query = CONCAT(@sql_query, ' order by file_path, file_size desc');

PREPARE stmt FROM @sql_query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

END $$

DELIMITER ;
0 голосов
/ 21 августа 2011

Для этого вы можете использовать группирование и самостоятельное объединение.

SELECT substring_index(file_path, '\\', 4), file_path
from fs_walk_scan as ws1
WHERE 30<= (
select count(*) from fs_Walk_scan as ws2
where substring_index(ws2.file_path, '\\', 4) = substring_index(ws1.file_path, '\\', 4)
and ws2.file_size > ws1.file_size
and ws2.file_path <> ws1.file_path)
group by substring_index(file_path, '\\', 4)

Это все еще запрос O (n) (n является числом групп), но он более гибкий и короткий.

Edit: Другой подход заключается в использовании переменных. Осуществимость вашей цели будет зависеть от того, как вы собираетесь выполнить этот запрос.

set @idx=0; set @cur_vol=0;                                                                      
SELECT file_volume, file_path, file_size FROM (
    SELECT file_volume, file_path, file_size,
    IF(@cur_vol != a.file_volume, @idx:=1, @idx:=@idx+1) AS row_index,
    IF(@cur_vol != a.file_volume, @cur_vol:=a.file_volume, 0) AS discard
    FROM (SELECT substring_index(file_path, '\\', 4) as file_volume, file_path, file_size 
        FROM fs_walk_scan
        ORDER BY substring_index(file_path,'\\',4), file_size DESC) AS a
    HAVING row_index <= 30) AS b;

Я еще не пробовал этот код, но концепцию переменных можно использовать для этой цели.

0 голосов
/ 02 октября 2008

Как насчет чего-то подобного (еще не проверял, но выглядит близко):

select * from fs_walk_scan where file_path like '\\\\server' and file_path like 'root$\\%' order by file_size desc 

Таким образом, вы проводите пару сравнений для отдельного поля, которые в общем соответствуют тому, что вы описали. Возможно, можно использовать регулярные выражения, но я этого не делал.

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