Mysql ГРУППИРУЕТ ПО и УЧИТЫВАЕТ несколько предложений WHERE
-
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)
Я хочу, чтобы оба числа:(статус!= "ОК" И статус != "Исправлено")
И наоборот...количество:(статус = "ОК" ИЛИ статус = "Исправлено")
И первый ответ, разделенный на второй, для 'percentage_dead' (вероятно, так же быстро, как и при последующей обработке)
ЗА самый последний день или смещение ( - 86400 за вчерашний день и т.д.)
Таблица содержит около 500 тысяч записей и растет примерно на 5000 в день, так что один SQL-запрос в отличие от цикла был бы действительно хорош..
Я представляю, какие креативные "ЕСЛИ" могли бы это сделать.Мы ценим ваш опыт.
Редактировать:Я открыт для использования другого SQL-запроса либо для сегодняшних данных, либо для данных из смещения.
Редактировать:Запрос работает достаточно быстро, но в настоящее время я не могу разрешить пользователям сортировать по столбцу процентов (тот, который получен из плохих и хороших показателей).Это не ограничитель показа, но я позволяю им сортировать все остальное.ПОРЯДОК СЛЕДОВАНИЯ этого:
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 по сравнению снеправильные имена хостов на дату последнего действия.Верно?И затем это должно быть сгруппировано по ядру.
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;
Это вариация проблемы "наибольшее число пользователей на группу", которую я часто вижу в вопросах SQL в StackOverflow.
Сначала мы хотим выбрать только те строки, которые имеют последнюю дату активности для каждого имени хоста, что мы можем сделать, выполнив внешнее объединение для строк с тем же именем хоста и большим значением 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;
Что касается соотношения между нормальными и неработающими именами хостов, я бы рекомендовал просто вычислить это в вашем 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;