SQL последовательное исключение - PullRequest
0 голосов
/ 10 марта 2019

У меня есть таблица, которая выглядит следующим образом:

id_a, id_b, statistic

Эта таблица содержит 1M ~ 1024 * 1024 записей, содержащих все комбинации id_a и id_b.Ранее я вычислял статистику (число с плавающей запятой) на основе id_a и id_b, и теперь хотел бы собрать список всех пар id_a и id_b, чтобы каждая из них имела наименьшую возможную статистику, но также эти id_a и id_bпоявляются только один раз в обоих столбцах.

Хороший результат будет выглядеть следующим образом:

[1,2, 0.0]
[5,3, 0.1]
[7,9, 0.3]
...

Как видите, каждое число в первом и втором столбцах появляется только один раз на обоих.Я не могу расширить этот набор, добавив [6,7, _] или [5,6, _]

Мое решение пока выглядит как последовательное расширение запроса SQL:

exclude_abs = []
while s < maxSize:
    a, b, stat = selectBestSystem(exclude_abs)
    exclude_abs.extend([a,b])

def selectBestSystem(exclude_abs):
    exclude_req = " AND ".join( map(lambda x: ("id_a!=%(1) and id_b!=%(1)" % x), exclude_abs) )
    req = ("SELECT id_a, id_b, statistic FROM table WHERE"+ \
          exclude_req+" ORDER BY statistic ASC LIMIT 1"
    return db.process(req)

Thisсоздает запрос, который выглядит ужасающим после первых 100 извлеченных пар:

SELECT id_a, id_b, statistic FROM table WHERE
id_a!=1 and id_b!=1 and
id_a!=2 and id_b!=2 and
id_a!=5 and id_b!=5 and
id_a!=3 and id_b!=3 and
id_a!=7 and id_b!=7 and
id_a!=9 and id_b!=9 and
[...skipped 200 conditions...]
ORDER BY statistic ASC LIMIT 1

В результате на обработку этого запроса после 100+ выбранных пар уходит более 15 секунд.Есть ли лучший способ сделать этот последовательный процесс исключения на MySQL?Может быть, моя структура данных - полный мусор, и мне не следует использовать реляционные БД для начала?

БД - это AWS RDS Aurora 5.6.10a

1 Ответ

1 голос
/ 10 марта 2019

Я думаю, что лучше всего позволить БД обрабатывать это в одном запросе, а не генерировать новый запрос для каждой строки, которую вы хотите, хотя моя попытка сделать это не очень приятна.

I 'm пытаюсь отсортировать полную таблицу в нужном вам порядке, а затем использовать NOT EXISTS против той же отсортированной таблицы, чтобы удалить те, которые не соответствуют вашим критериям.

SELECT id_a, id_b, statistic
FROM
 (SELECT @row_number:=@row_number+1 AS row_number, mytable.*
 FROM mytable, (SELECT @row_number:=0) AS t
 ORDER BY statistic, id_a, id_b -- Include all columns in ORDER BY to make this predictable when we repeat the logic below
 ) a
WHERE NOT EXISTS
 (SELECT *
  FROM
   (SELECT @row_number2:=@row_number2+1 AS row_number, mytable.*
   FROM mytable, (SELECT @row_number2:=0) AS t
   ORDER BY statistic, id_a, id_b) b
 WHERE b.row_number < a.row_number AND (a.id_a = b.id_a OR a.id_a = b.id_b OR a.id_b = b.id_a OR a.id_b = b.id_b)
  )

Если у вас был MySQL 8(который поддерживает CTE), это, вероятно, будет красивее, поскольку вы можете избежать повторения логики sort / rownumber.

Я не уверен, как будет масштабироваться NOT EXISTS, поэтому, возможно, следующее будет более производительным.

SELECT a.id_a, a.id_b, a.statistic
FROM
 (SELECT @row_number:=@row_number+1 AS row_number, mytable.*
 FROM mytable, (SELECT @row_number:=0) AS t
 ORDER BY statistic, id_a, id_b DESC -- Include all columns in ORDER BY to make this predictable when we repeat it
 ) a
 LEFT OUTER JOIN
 (SELECT @row_number2:=@row_number2+1 AS row_number, mytable.*
 FROM mytable, (SELECT @row_number2:=0) AS t
 ORDER BY statistic, id_a, id_b DESC) b
  ON b.row_number < a.row_number AND (a.id_a = b.id_a OR a.id_a = b.id_b OR a.id_b = b.id_a OR a.id_b = b.id_b)
WHERE b.id_a is null;
...