我有一个非常大的表(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个最大文件的列表。然而,这是非常缓慢的,并且“喜欢”搜索是硬编码的,即使他们坐在另一张桌子上并且可以这样做。
我正在寻找的是一种方法,无需多次通过巨大的桌子就可以做到这一点。有人有什么想法吗?
感谢。
P.S。我无法以任何方式改变巨大的源表的结构。
更新:file_path和file_size上有索引,但这些子(?)查询中的每一个仍然需要大约10分钟,而且我必须做最少22分钟。
答案 0 :(得分:2)
你在那张桌子上有什么样的索引?这个指数:
CREATE INDEX fs_search_idx ON fs_walk_scan(file_path,file_size desc)
会显着提高这个查询...如果你还没有这样的话。
更新
你说file_path和file_size上已有索引......它们是个别索引吗?或者是否有一个索引,两列都被索引在一起?这个查询的差异很大。即使有22个子查询,如果索引正确,这应该是快速的。
答案 1 :(得分: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$')
您可以设置触发器以在插入记录时初始化字段,或者之后执行批量更新以填充两个额外的列。
答案 2 :(得分: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 ;
答案 3 :(得分: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不是很热,所以如果它不是那么适用。可以随意编辑/评论)
答案 4 :(得分:0)
这样的事情怎么样(没有测试过,但看起来很近):
select * from fs_walk_scan where file_path like '\\\\server' and file_path like 'root$\\%' order by file_size desc
通过这种方式,您可以对单个字段进行一对比较,这些比较通常与您描述的内容相匹配。也可以使用正则表达式,但我还没有这样做。
答案 5 :(得分: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;
我还没有尝试过这段代码,但变量的概念可以像这样用于你的目的。