複数のWHERE句のMysql GROUP BYおよびCOUNT
-
06-07-2019 - |
質問
簡略化されたテーブル構造:
CREATE TABLE IF NOT EXISTS `hpa` (
`id` bigint(15) NOT NULL auto_increment,
`core` varchar(50) NOT NULL,
`hostname` varchar(50) NOT NULL,
`status` varchar(255) NOT NULL,
`entered_date` int(11) NOT NULL,
`active_date` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `hostname` (`hostname`),
KEY `status` (`status`),
KEY `entered_date` (`entered_date`),
KEY `core` (`core`),
KEY `active_date` (`active_date`)
)
このために、定義済みのステータスを持つすべてのレコードを単純に合計する次のSQLクエリがあります。
SELECT core,COUNT(hostname) AS hostname_count, MAX(active_date) AS last_active
FROM `hpa`
WHERE
status != 'OK' AND status != 'Repaired'
GROUP BY core
ORDER BY core
このクエリは単純化され、無関係なデータや、質問に影響を与えない余分な列への内部結合を削除しました。
MAX(active_date)は特定の日のすべてのレコードで同じであり、常に最新の日を選択するか、NOW()からのオフセットを許可する必要があります。 (UNIXTIMEフィールドです)
次の両方のカウントが必要です:(status!= 'OK' AND status!= 'Repaired')
およびその逆...カウント:(ステータス= 'OK'またはステータス= '修復済み')
「percentage_dead」の最初の答えを2番目の答えで割ったもの(おそらく、後処理で行うのと同じくらい速い)
最新の日またはオフセット(昨日など-86400など)
テーブルには約50万件のレコードが含まれており、1日に約5,000件増加するため、ループとは対照的に1つのSQLクエリは本当に便利です。
これを行うことができる創造的なIFがあると思います。あなたの専門知識は高く評価されています。
編集:今日のデータ、またはオフセットからのデータのいずれかに対して、異なるSQLクエリを使用できます。
編集:クエリは機能しますが、十分に高速ですが、ユーザーにパーセンテージ列(悪いカウントと良いカウントから派生した列)でソートさせることはできません。これはショーストッパーではありませんが、他のすべてに基づいて並べ替えることができます。このORDER BY:
SELECT h1.core, MAX(h1.entered_date) AS last_active,
SUM(CASE WHEN h1.status IN ('OK', 'Repaired') THEN 1 ELSE 0 END) AS good_host_count,
SUM(CASE WHEN h1.status IN ('OK', 'Repaired') THEN 0 ELSE 1 END) AS bad_host_count
FROM `hpa` h1
LEFT OUTER JOIN `hpa` h2 ON (h1.hostname = h2.hostname AND h1.active_date < h2.active_date)
WHERE h2.hostname IS NULL
GROUP BY h1.core
ORDER BY ( bad_host_count / ( bad_host_count + good_host_count ) ) DESC,h1.core
教えてください: #1247-参照 'bad_host_count'はサポートされていません(グループ関数への参照)
編集:別のセクションで解決しました。以下が動作し、 ORDER BY percentage_dead
SELECT c.core, c.last_active,
SUM(CASE WHEN d.dead = 1 THEN 0 ELSE 1 END) AS good_host_count,
SUM(CASE WHEN d.dead = 1 THEN 1 ELSE 0 END) AS bad_host_count,
( SUM(CASE WHEN d.dead = 1 THEN 1 ELSE 0 END) * 100/
( (SUM(CASE WHEN d.dead = 1 THEN 0 ELSE 1 END) )+(SUM(CASE WHEN d.dead = 1 THEN 1 ELSE 0 END) ) ) ) AS percentage_dead
FROM `agent_cores` c
LEFT JOIN `dead_agents` d ON c.core = d.core
WHERE d.active = 1
GROUP BY c.core
ORDER BY percentage_dead
解決
わかりましたら、最後のアクティビティの日付で、OKホスト名とOKホスト名ではないステータスのカウントを取得します。右?そして、それをコアごとにグループ化する必要があります。
SELECT core, MAX(active_date)
SUM(CASE WHEN status IN ('OK', 'Repaired') THEN 1 ELSE 0 END) AS OK_host_count,
SUM(CASE WHEN status IN ('OK', 'Repaired') THEN 0 ELSE 1 END) AS broken_host_count
FROM `hpa` h1 LEFT OUTER JOIN `hpa` h2
ON (h1.hostname = h2.hostname AND h1.active_date < h2.active_date)
WHERE h2.hostname IS NULL
GROUP BY core
ORDER BY core;
これは、「グループごとの最大n」のバリエーションです。 StackOverflowのSQLに関する質問でよく見られる問題。
最初に、ホスト名ごとに最新のアクティビティ日付を持つ行のみを選択します。これは、同じホスト名とより大きいactive_dateを持つ行に対して外部結合を行うことで実行できます。そのような一致が見つからない場合、指定されたホスト名ごとにすでに最新の行があります。
コアごとにグループ化し、ステータスごとに行をカウントします。
これが今日の日付の解決策です(将来、active_dateの行がないと仮定します)。結果をN日前の行に制限するには、両方のテーブルを制限する必要があります。
SELECT core, MAX(active_date)
SUM(CASE WHEN status IN ('OK', 'Repaired') THEN 1 ELSE 0 END) AS OK_host_count,
SUM(CASE WHEN status IN ('OK', 'Repaired') THEN 0 ELSE 1 END) AS broken_host_count
FROM `hpa` h1 LEFT OUTER JOIN `hpa` h2
ON (h1.hostname = h2.hostname AND h1.active_date < h2.active_date
AND h2.active_date <= CURDATE() - INTERVAL 1 DAY)
WHERE h1.active_date <= CURDATE() - INTERVAL 1 DAY AND h2.hostname IS NULL
GROUP BY core
ORDER BY core;
OKと破損したホスト名の比率については、PHPコードで計算することをお勧めします。 SQLでは、他の選択リスト式で列エイリアスを参照することは許可されていないため、上記をサブクエリとしてラップする必要があり、この場合よりも複雑になります。
UNIXタイムスタンプを使用していることを忘れていました。このようなことをしてください:
SELECT core, MAX(active_date)
SUM(CASE WHEN status IN ('OK', 'Repaired') THEN 1 ELSE 0 END) AS OK_host_count,
SUM(CASE WHEN status IN ('OK', 'Repaired') THEN 0 ELSE 1 END) AS broken_host_count
FROM `hpa` h1 LEFT OUTER JOIN `hpa` h2
ON (h1.hostname = h2.hostname AND h1.active_date < h2.active_date
AND h2.active_date <= UNIX_TIMESTAMP() - 86400)
WHERE h1.active_date <= UNIX_TIMESTAMP() - 86400 AND h2.hostname IS NULL
GROUP BY core
ORDER BY core;