Как объединить таблицы на регулярное выражение - PullRequest
7 голосов
/ 30 декабря 2011

Скажем, у меня есть две таблицы MSG для сообщений и MNC для кодов мобильной сети. Они не имеют никаких отношений. Но я хочу присоединиться к ним

SELECT msg.message,
    msg.src_addr,
    msg.dst_addr,
    mnc.name,
FROM "msg"
JOIN "mnc"
ON array_to_string(regexp_matches(msg.src_addr || '+' || msg.dst_addr, '38(...)'), '') = mnc.code

Но запрос завершается с ошибкой:

psql:marketing.sql:28: ERROR:  argument of JOIN/ON must not return a set
LINE 12: ON array_to_string(regexp_matches(msg.src_addr || '+' || msg...

Есть ли способ сделать такое соединение? Или я двигаюсь не в ту сторону?

Ответы [ 3 ]

14 голосов
/ 30 декабря 2011

Как уже упоминалось @Milen regexp_matches(), вероятно, не та функция, которая вам нужна.Вы хотите простое совпадение регулярного выражения (~) .На самом деле, оператор LIKE (~~) будет быстрее :

Предположительно быстрее всего с LIKE

SELECT msg.message
      ,msg.src_addr
      ,msg.dst_addr
      ,mnc.name
FROM   mnc
JOIN   msg ON msg.src_addr ~~ ('%38' || mnc.code || '%')
           OR msg.dst_addr ~~ ('%38' || mnc.code || '%')
WHERE  length(mnc.code) = 3

Кроме того, вы толькохотите mnc.code точно из 3 символов.


С помощью regexp

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

SELECT msg.message
      ,msg.src_addr
      ,msg.dst_addr
      ,mnc.name
FROM   mnc
JOIN   msg ON (msg.src_addr || '+' || msg.dst_addr) ~ (38 || mnc.code)
           AND length(mnc.code) = 3

Для этого также необходимо, чтобы msg.src_addr и msg.dst_addr были NOT NULL.

Второй запрос демонстрирует, как дополнительная проверка length(mnc.code) = 3 может перейти в условие JOIN или предложение WHERE.Тот же эффект здесь.


С regexp_matches ()

Вы могли бы сделать эту работу с regexp_matches():

SELECT msg.message
      ,msg.src_addr
      ,msg.dst_addr
      ,mnc.name
FROM   mnc
JOIN   msg ON EXISTS (
    SELECT * 
    FROM   regexp_matches(msg.src_addr ||'+'|| msg.dst_addr, '38(...)', 'g') x(y)
    WHERE  y[1] = mnc.code
    )

Но это будет медленное сравнение - или я так полагаю.

Объяснение:
Ваше выражение regexp_matches () просто возвращает массив всех захваченных подстрок первый матч.Поскольку вы захватываете только одну подстроку (одну пару скобок в шаблоне), вы получите исключительно массивы с одним элементом .

Вы получите все совпадения с дополнительнымипереключатель «глобально» 'g' - но в несколько рядов.Таким образом, вам нужен дополнительный выбор, чтобы проверить их все (или совокупность).Поместите это в EXISTS - полусоединение, и вы получите то, что хотели.

Может быть, вы можете сообщить о результатах теста из всех трех?Для этого используйте EXPLAIN ANALYZE .

0 голосов
/ 30 декабря 2011

Попробуйте вместо этого использовать «подстроку», которая извлекает подстроку с использованием шаблона регулярного выражения.

SELECT msg.message,
    msg.src_addr,
    msg.dst_addr,
    mnc.name
FROM "msg"
JOIN "mnc"
ON substring(msg.src_addr || '+' || msg.dst_addr from '38(...)') = mnc.code
0 голосов
/ 30 декабря 2011

Ваша непосредственная проблема в том, что regexp_matches может вернуть одну или несколько строк.

...