4
CREATE TABLE hostname_table
(
id INT NOT NULL AUTO_INCREMENT,
hostname CHAR(65) NOT NULL,
interval_avg INT,
last_update DATETIME NOT NULL,
numb_updates INT,
PRIMARY KEY (id)
)

このテーブルがあり、500〜600k行のデータをそのテーブルにインポートします。データベースに書き込むときに重複をチェックしません。これは、各ホストの重複がいくつあるかを知りたいためです。また、ホスト名が更新されるたびに間隔を確認したいためです。

hostname_tableの値の例:

id  hostname          interval_avg  last_update          numb_updates
1   www.host.com      60            2012-04-25 20:22:21  1
2   www.hostname.com  10            2012-04-25 20:22:21  5
3   www.name.com      NULL          2012-04-25 20:22:21  NULL
4   www.host.com      NULL          2012-04-25 20:22:26  NULL
5   www.host.com      NULL          2012-04-25 20:22:36  NULL

クリーンアップしたときの外観の例:

id  hostname          interval_avg  last_update          numb_updates
1   www.host.com      25            2012-04-25 20:22:36  3
2   www.hostname.com  10            2012-04-25 20:22:21  5
3   www.name.com      NULL          2012-04-25 20:22:21  NULL

このような巨大なデータベースでは、この目標を達成するためにあまり多くのクエリを送信したくありませんが、このような操作には3つのクエリが最小であると思います(間違っている場合は修正してください)。1時間ごとに約50万の新しい行があり、約50%以上が重複するため、重複が発生した回数と頻度を記録しながら、これらの重複を可能な限り効率的に削除することが重要です(interval_avgおよびnumb_updateupdate)。

これは3段階の問題であり、私はここのコミュニティが助けになることを望んでいました。

したがって、擬似コードで要約するには、これらのクエリを最適化するための支援が必要です。

  1. すべてのlast_update値とinterval_avg値を選択し、sum(numb_update)を取得し、各ホスト名のcount(duplicates)を取得します。
  2. min(id)のinterval_avgを更新し、min(id)のnumb_updatesを更新し、min(id)のlast_updateをmax(id)の値で更新します。
  3. min(id)を除くすべての重複を削除します

解決済み。数日間の調査の過程で、ある部分を94%最適化し、別の部分を約97%最適化しました。これが他の人が同じ解決策を探すのに役立つことを心から願っています。間違ったソリューションを選択すると、mySQLと大規模なデータベースが大きな問題になる可能性があります。(last_update列をDATETIMEからINT(10)に変更し、max(last_update)とmin(last_update)の値を取得できるように、最終的なソリューションの値としてフォーマットされた時刻からタイムスタンプに変更しました)

(問題の一部を支援してくれたGolezTrolに感謝します)

4

2 に答える 2

4

そのホスト名で集約する場合、そのホスト名のinterval_avgとnumb_updatesのそれぞれの異なる値を取得することはできません。あなたは彼らを意味しましたか、SUMそれとも多分AVG彼らですか?それとも、最小のIDの値を保持したいだけですか?

以下のクエリでは、それらを合計します。

SELECT 
  MIN(id) as id, 
  hostname, 
  SUM(interval_avg) as total_interval_avg,
  SUM(numb_updates) as total_numb_updates,
  COUNT(*) as hostname_count
FROM
  hostname_table
GROUP BY 
  hostname

この後、見つかった各IDをとの正しい値で更新する必要がありinterval_avgますnumb_updates

その後、このクエリで見つからない各IDを削除する必要があります。

DELETE FROM hostname_table
WHERE
  id NOT IN
    (SELECT 
      MIN(id)
    FROM
      hostname_table
    GROUP BY 
      hostname)
于 2012-04-26T07:10:13.173 に答える
0

私はこの解決策に行きました

--------------------------------

1.ホスト名ごとにすべての最小+最大のlast_update、sum(interval_avg)、sum( numb_update )、およびcount(duplicates)を選択します

//This will get the interval_avg value
//(summarize is ok, since all except min(id) will be zero), 
//give a count of how many duplicates there are per hostname, 
//and will also summarize numb_updates
SELECT 
  MIN(id) as id, 
  hostname, 
  SUM(numb_updates) as total_numb_updates,
  SUM(interval_avg) as total_interval_avg,
  MAX(last_update) as last_update_max,
  MIN(last_update) as last_update_min,
  COUNT(*) as hostname_count
FROM
  hostname_table
GROUP BY 
  hostname
HAVING 
  COUNT(*)>1
//Get all last_update from each duplicate hostname(including the original)
//Dont do this in a seperate query, you only need first+last+rowcount to figure
//out the interval average. It took me a while to realize this, so I tried many
//varieties with little success(took too long with +600k rows) 
//
// --- I will include the solution I didn't go for, ---
// --- so others wont do the same mistake ---
//
// START DONT USE THIS
// 2.63sec @ 10000 rows
$sql = "SELECT
  id, 
  ".$db_table.".hostname, 
  last_update 
FROM 
  ".$db_table." 
INNER JOIN (
  SELECT 
    hostname, 
    COUNT(*) 
  FROM 
    ".$db_table." 
  GROUP BY 
    hostname 
  HAVING 
    COUNT(*)>1
) as t2
ON 
  ".$db_table.".hostname = t2.hostname";

$resource = mysql_query($sql,$con);
// END DONT USE THIS (below is a 94% improvement)
//
// START THIS IS BETTER, BUT DONT USE THIS
// 0.16 sec @ 10000 rows
//Select everything from the table
$sql = "SELECT id 
    FROM ".$db_table;
$resource = mysql_query($sql,$con);
$array_id_all = array();
while($assoc = mysql_fetch_assoc($resource)){
    array_push($array_id_all, $assoc['id']);
}

//This will select the ID of all the hosts without duplicates
$sql = "SELECT 
  MIN(id) as id, 
  hostname
FROM
  ".$db_table."
GROUP BY 
  hostname
HAVING 
  COUNT(*)=1";

$resource = mysql_query($sql,$con);

$array_id_unique = array();
while($assoc = mysql_fetch_assoc($resource)){
    array_push($array_id_unique, $assoc['id']);
}

$array_id_non_unique = array_diff($array_id_all, $array_id_unique);
$id_list_non_unique = implode(", ", $array_id_non_unique);

//Select everything from the table when the IDs are IN $id_list_non_unique
$sql = "SELECT * 
    FROM ".$db_table." 
    WHERE id IN (".$id_list_non_unique.")";
$resource = mysql_query($sql,$con);

$array_duplicates = array();
$i=0;
while($assoc = mysql_fetch_assoc($resource)){
    $array_duplicates[$i] = array($assoc['id'], $assoc['hostname'], $assoc['interval_avg'], $assoc['last_update'], $assoc['numb_updates']);
    $i++;
}
// END THIS IS BETTER, BUT DONT USE THIS

(Nick Fortescue @ https://stackoverflow.com/a/877051/1248273に感謝)

2. min(id) の interval_avg を更新し、min(id) のnumb_updatesを更新し、min(id) の last_update を max(id) の値で更新します。

//update the interval_avg, last_update and numb_update value of the min(id)
//of each duplicate hostname.
// --- I will include the solution I didn't go for, ---
// --- so others wont do the same mistake ---
//
// START DONT USE THIS
// 167 secs @ 500k rows
UPDATE hostname_table
  SET interval_avg = CASE id
    WHEN 1 THEN 25
    //etc
  END,
  last_update = CASE id
    WHEN 1 THEN "2012-04-25 20:22:36"
    //etc
  END,
  numb_update = CASE id
    WHEN 1 THEN 3
    //etc
  END
WHERE id IN (1)
// END DONT USE THIS
//
// START USE THIS
// 5.75 secs @ 500k rows (96.6% improvement)
INSERT INTO hostname_table (id,interval_avg,last_update,numb_updates)
  VALUES 
    ('1','25','2012-04-25 20:22:36','3'), 
    //etc
ON DUPLICATE KEY UPDATE 
  interval_avg=VALUES(interval_avg), 
  last_update=VALUES(last_update), 
  numb_updates=VALUES(numb_updates)
// END USE THIS

(Michiel de Mare @ https://stackoverflow.com/a/3466/1248273に感謝)

3. min(id) を除くすべての重複を削除します

//delete all duplicates except min(id)
ALTER IGNORE TABLE hostname_table ADD UNIQUE (hostname)
ALTER TABLE hostname_table DROP INDEX hostname

(必要な最初の情報を選択する際に正しい方向に進んでくれた GolezTrol に感謝します)

于 2012-04-26T17:56:04.900 に答える