Как выбрать серию дат (выберите с зависимым от даты подзапросом)? - PullRequest
0 голосов
/ 26 января 2019

Предположим, в моей базе данных PostgreSQL есть таблица

 -----------+-----------------------------
 IP        | inet                        
 timestamp | timestamp                   
 ports     | integer REFERENCES other_table_does_not_matter                    
 hostname  | character varying           

каждый IP проверяется каждый час.

Я хочу сгруппировать количество (IP), то есть суммарную ежедневную недоступность по дням, как показано ниже:

SELECT timestamp::date AS date, COUNT (DISTINCT IP) AS count 
FROM ip_check WHERE ports=9 AND ip NOT IN (SELECT IP FROM ip_check WHERE hostname <> '')
GROUP BY timestamp::date
ORDER BY timestamp::date ASC;

но есть дополнительное условие. Мне нужно исключить IP, который был доступен / доступен даже в течение короткого времени (в моем случае имя хоста <> '' означает, что IP был доступен):

... AND IP NOT IN (SELECT ... WHERE hostname <> '' AND DATE(timestamp)='2019-01-25')

Другими словами. Я хотел бы запустить один SELECT (показанный ниже) для ряда дат и группировать результаты по дням.

SELECT COUNT(DISTINCT IP) 
FROM ip_check 
WHERE ports=9 AND DATE(timestamp)='2019-01-25' 
AND IP NOT IN 
    (SELECT IP 
     FROM ip_check 
     WHERE hostname <> '' AND DATE(timestamp)='2019-01-25');

Пример данных:

 (ip;timestamp;ports;hostname)
 (1.1.1.1;2019-01-24 10:11;9;'')
 (1.1.1.1;2019-01-24 11:11;9;'hostA')
 (1.1.1.1;2019-01-24 1:11;9;'')
 (1.1.1.1;2019-01-24 2:11;9;'')
 (1.1.1.1;2019-01-24 3:11;9;'')
 (1.1.1.1;2019-01-24 4:11;9;'')
 (2.1.1.1;2019-01-24 10:11;9;'')
 (3.1.1.1;2019-01-24 10:11;9;'hostC')
 (1.1.1.1;2019-01-25 10:11;9;'')
 (1.1.1.1;2019-01-25 11:11;9;'')
 (1.1.1.1;2019-01-25 1:11;9;'hostA')
 (2.1.1.1;2019-01-25 10:11;9;'')
 (3.1.1.1;2019-01-25 10:11;9;'')

Желаемый вывод:


     data   | count
------------+-------
 2019-01-24 |  1
 2019-01-25 |  2
(2 rows)

Объяснение:

  • 2019-01-24 - только 1 IP (2.1.1.1) полностью недоступен
  • 2019-01-25 - 2 IP (2.1.1.1, 3.1.1.1) полностью недоступны

1 Ответ

0 голосов
/ 26 января 2019

Чтобы показать ip, которые были полностью недоступны для каждого дня, вы можете использовать следующий запрос:

SELECT ips.ip, dts.dt
FROM
(SELECT DISTINCT ip FROM mytable) ips
CROSS JOIN (SELECT DISTINCT timestamp::date dt FROM mytable) dts
WHERE NOT EXISTS (
  SELECT 1 FROM mytable WHERE ip = ips.ip AND timestamp::date = dts.dt AND hostname <> ''
)

Запрос работает, генерируя декартово объединение между IP-адресами и днями, и использует коррелированный подзапрос, а затем фильтрует кортежи, которые следует считать полностью недоступными.

С вашими примерами данных запрос возвращает:

ip        dt
2.1.1.1   2019-01-24
2.1.1.1   2019-01-25
3.1.1.1   2019-01-25

Чтобы получить количество полностью недоступных ips в день, мы просто превратим это в агрегированный запрос:

SELECT dts.dt, COUNT(*)
FROM
(SELECT DISTINCT ip FROM mytable) ips
CROSS JOIN (SELECT DISTINCT timestamp::date dt FROM mytable) dts
WHERE NOT EXISTS (
  SELECT 1 FROM mytable WHERE ip = ips.ip AND timestamp::date = dts.dt AND hostname <> ''
)
GROUP BY dts.dt

Возвращает:

dt            count
2019-01-24    1
2019-01-25    2

Демонстрация на скрипке БД

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...