1

ファイルに関する情報を含む非常に大きなテーブル (8 GB) があり、それに対して次のようなレポートを実行する必要があります。

(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

そのテーブルにはどのような種類のインデックスがありますか? このインデックス:

CREATE INDEX 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$\\'

それ以外の場合は、テーブル構造を変更できる場合は、2 つの列を追加してサーバー名とベース パスを保持し (そしてそれらにインデックスを付けます)、より単純なクエリを作成できるようにします。

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 つの余分な列を埋めることができます。

于 2008-10-02T20:10:08.787 に答える
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 に答える
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 に答える
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 に答える
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 に答える