1

我有一个非常大的表(8gb),其中包含有关文件的信息,我需要针对它运行一个看起来像这样的报告:

(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 个最大文件的列表。但是,这非常慢,并且“喜欢”搜索是硬编码的,即使它们位于另一个表中并且可以通过这种方式获得。

我正在寻找的是一种无需多次通过巨大桌子即可做到这一点的方法。有人有想法么?

谢谢。

PS我不能以任何方式改变巨大源表的结构。

更新:file_path 和 file_size 上有索引,但是这些子(?)查询中的每一个仍然需要大约 10 分钟,我必须至少做 22 分钟。

4

6 回答 6

2

你在那张桌子上有什么样的索引?该指数:

创建索引 fs_search_idx ON fs_walk_scan(file_path, file_size desc)

会显着加快此查询的速度...如果您还没有这样的查询。

更新:

您说 file_path 和 file_size 上已经有索引...它们是单独的索引吗?或者是否有一个索引将两列索引在一起?对于此查询,差异将是巨大的。即使有 22 个子查询,如果索引正确,这应该很快。

于 2008-10-02T19:03:40.023 回答
2

您可以使用正则表达式:

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$')

您可以设置触发器以在插入记录时初始化字段,或者在之后进行批量更新以填充两个额外的列。

于 2008-10-02T20:10:08.787 回答
1

你可以做这样的事情......假设 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 ;
于 2008-10-02T22:36:47.593 回答
1

尝试这个。
您希望获取少于 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

编辑:

显然这表现得像一条狗。那么......这个循环语法怎么样?

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 不是很热,所以如果它不在那里,请随意。随意编辑/评论)

于 2008-10-03T11:27:15.737 回答
0

您可以使用分组和自加入来实现此目的。

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 是组数),但更灵活、更短。

编辑:另一种方法是使用变量。您的目的的可行性将取决于您将如何运行此查询。

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;

我还没有尝试过这段代码,但是变量的概念可以像这样用于你的目的。

于 2011-08-20T21:30:40.477 回答
0

像这样的东西怎么样(还没有测试过,但看起来很接近):

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

这样,您将在单个字段上进行一对比较,这通常与您所描述的相匹配。也可以使用正则表达式,但我没有这样做。

于 2008-10-02T19:04:06.310 回答