Postgres Рекурсия по 2 чередующимся полям - PullRequest
0 голосов
/ 23 января 2012

Вот что я пытаюсь сделать.У меня есть простая таблица:

table USAGE_LOG:

USAGE_ID
PC_ID
MEMORY_STICK_ID

Таким образом, каждая запись об использовании представляет ПК, используемый с определенной картой памяти.

И идентификатор ПК, и карта памяти связаны с таблицей устройства, где device_type - это1 для ПК и 2 для карт памяти.

Мой сценарий состоит в том, что я обнаружил вирус на некоторых ПК, и теперь мне нужно отследить все возможные зараженные машины, используя мою таблицу USAGE_LOG.

Мой компьютер помечен как имеющий вирус в отдельной таблице:

table VIRUS:

PC_ID
VIRUS_ID

Мне нужно создать CTE (или представление), который содержит список всех возможных зараженных устройств путем рекурсивного поискадля каждой карты памяти, подключенной к моему зараженному компьютеру, мне нужно найти каждый компьютер, подключенный к каждой из моих подозрительных карт памяти, а затем к каждому компьютеру и т. д. и т. д.

Ограничения: я не могу изменить свою структуру данных, и я не могу создавать какие-либо функции или хранимые процедуры.

Возможно ли это, и если да, то как это делается?

1 Ответ

1 голос
/ 23 января 2012

Тестовая настройка

Я включаю мою настройку теста для тех, кто хочет проверить ее самостоятельно:

CREATE TEMP TABLE usage_log (
  usage_id int primary key
, pc_id  int
, memory_stick_id int);
INSERT INTO usage_log VALUES
  (1,1,2)
, (2,3,2)
, (3,3,4)
, (4,5,4)
, (5,5,6)
, (6,7,6)
, (7,9,8)
, (8,9,10)
, (9,3,12)
, (10,11,12)
, (11,11,13);

CREATE TEMP TABLE virus(
  pc_id int
, virus_id int);
INSERT INTO virus values
  (3, 4)
, (3, 5)
, (5, 6)

/* Alternative test:
TRUNCATE VIRUS;
INSERT INTO virus values
  (9, 4)
, (9, 5);
*/

Запрос с двумя рекурсивными CTE

Этот запрос должен найти все компьютеры и флешки, возможно, рекурсивно зараженные внутренним использованием:

WITH RECURSIVE v_start AS (
    SELECT pc_id, max(u.usage_id) AS usage_id
    FROM   virus v
    JOIN   usage_log u USING (pc_id)
    GROUP  BY 1
    ),

    v_down AS (
    SELECT u.*
    FROM   usage_log u
    JOIN   v_start v USING (usage_id)

    UNION
    SELECT DISTINCT u.*
    FROM   usage_log u 
    JOIN   v_down v ON u.pc_id = v.pc_id OR u.memory_stick_id = v.memory_stick_id
    WHERE  u.usage_id < v.usage_id
    ),

    v_up AS (
    SELECT u.*
    FROM   usage_log u
    JOIN   v_down v USING (usage_id)

    UNION
    SELECT DISTINCT u.*
    FROM   usage_log u 
    JOIN   v_up v ON u.pc_id = v.pc_id OR u.memory_stick_id = v.memory_stick_id
    WHERE  u.usage_id > v.usage_id
    )

SELECT pc_id, 1 AS device_type            -- PCs
FROM   v_up
GROUP  BY 1

UNION ALL
SELECT memory_stick_id, 2 AS device_type  -- USB-sticks
FROM   v_up
GROUP  BY 1
ORDER  BY 2, 1

Объяснить запрос

  • Я полагаю, usage_id - это последовательный столбец, который отражает временную шкалу.

  • Редактировать: в первом CTE v_start Я нахожу последнюю запись в журнале для каждого зараженного ПК. Вид вируса полностью игнорируется. В моей первой версии у меня было самое раннее использование min(usage_id), но это было до того, как я улучшил запрос, включив в него все прекурсоры, и , а затем позволил ему распространиться по временной шкале. Мы должны начать с последнего использования сейчас, которое будет включать еще больше подозреваемых. Мы могли бы сузить поиск, если бы у нас было больше информации для работы.

  • Первый CTE также RECURSIVE, потому что технически вы можете использовать только один тип CTE одновременно. Без части UNION это фактически обычный CTE . Подробнее в руководстве о предложении WITH (CTE) .

  • В следующих двух CTE я возвращаюсь назад во времени, а затем снова иду вперед, чтобы найти любой USB-флешку или ПК, который мог соприкоснуться. Хронологический порядок актуален. См. Объяснение ниже.
    Обратите внимание, что я использую UNION - вместо UNION ALL в моем первом наброске. Должно быть намного эффективнее устранять дубликаты с каждым шагом.

  • Финальная SELECT - это функция сводной таблицы бедного человека, в то же время удаляя любые дубликаты среди ПК и USB-флешек. В этом случае оба списка объединяются с UNION ALL. Окончательный результат упорядочен по типу устройства и идентификатору устройства.

Объяснить стратегию

Наихудший сценарий - это то, что мы должны предположить, и это означает, что ПК мог быть заражен во время любого использования.

Нам нужно вырастить назад (v_down) из последнего использования, чтобы включить любые устройства, с которых мог появиться вирус.

Затем, из всех подозрительных рядов, мы прыгаем вперед (v_up) через раз, чтобы найти любое и все устройства, которые могли быть инфицированы дополнительно.

Вуаля: все компьютеры и USB-накопители, которые могут быть задействованы.


Объясните примером

Рассмотрим эту настройку:

INSERT INTO usage_log VALUES
  (1,1,2)  -- PC 1 connects to stick 3
, (3,3,4)  -- PC 3 connects to stick 4
, (2,3,2)  -- PC 3 connects to stick 2
, (2,3,6); -- PC 3 connects to stick 6

Здесь ручка 4 никогда не соприкасается с USB 2, прокси или нет. Но они были бы связаны, если бы мы игнорировали график. Стик 6 подключается к флешке 2 через прокси, потому что он подключается к ПК 3 после того, как установил контакт с флешкой 2.

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