Mysql ГРУППИРУЕТ ПО и УЧИТЫВАЕТ несколько предложений WHERE

StackOverflow https://stackoverflow.com/questions/1632821

Вопрос

Упрощенная структура таблицы:

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; 
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top