Вопрос по postgresql, sql, math – Комбинации PostgreSQL без повторов

5

Как сделать функцию в postgres, которая будет принимать строку или массив и возвращать все комбинации некоторой длины?

Например, у вас есть ABC, и вы хотите получить комбинации с 2 символами, результат должен быть:

AB переменный ток До нашей эры

Спасибо заранее за вашу помощь.

Я бы использовал это в нескольких языках программирования и только в одной базе данных PostgreSQL, поэтому я подумал, что это будет самый простой способ, просто иметь это в одном месте. Эту строку не нужно соединять, я просто предлагаю, как она должна работать. ffox003
Я мог бы быть полезен в решении судоку ;-) wildplasser
Зачем вам это делать в СУБД? Можете ли вы сделать это на уровне приложения? Mosty Mostacho

Ваш Ответ

3   ответа
10
set search_path='tmp';

WITH ztab AS (
SELECT idx as idx
, substring ( 'WTF!' FROM idx FOR 1) as str
FROM generate_series(1, char_length( 'WTF!' )) idx
)
SELECT t1.str, t2.str
FROM ztab t1
JOIN ztab t2 ON t2.idx > t1.idx
        ;

 str | str 
-----+-----
 W   | T
 W   | F
 W   | !
 T   | F
 T   | !
 F   | !
(6 rows)

К сожалению, я не могу найти способ избежать двойной строковой константы. (но все это можно упаковать в функцию). Если нет повторяющихся символов (или вы хотите их убрать), вы можете сделать анти-объединение на str вместо idx.

ОБНОВЛЕНИЕ (подсказка от ypercube) Похоже, что ОП хочет, чтобы строки были объединены. Быть по сему::

WITH ztab AS (
SELECT idx as idx
, substring ( 'WTF!' FROM idx FOR 1) as str
FROM generate_series(1, char_length( 'WTF!' )) idx
)
SELECT t1.str || t2.str AS results
FROM ztab t1
JOIN ztab t2 ON t2.idx > t1.idx
        ;

Результаты:

 results 
---------
 WT
 WF
 W!
 TF
 T!
 F!
(6 rows)

ОБНОВЛЕНИЕ 2: (здесь прибывает рекурсивная штука ...)

WITH RECURSIVE xtab AS (
        WITH no_cte AS (
        SELECT
        1::int AS len
        , idx as idx
        , substring ( 'WTF!' FROM idx FOR 1) as str
        FROM generate_series(1, char_length( 'WTF!' )) idx
        )
        SELECT t0.len as len
                , t0.idx
                , t0.str
        FROM no_cte t0
        UNION SELECT 1+t1.len
                , tc.idx
                , t1.str || tc.str AS str
        FROM xtab t1
        JOIN no_cte tc ON tc.idx > t1.idx
        )
SELECT * FROM xtab
ORDER BY len, str
-- WHERE len=2
        ;

Результаты 3:

 len | idx | str  
-----+-----+------
   1 |   4 | !
   1 |   3 | F
   1 |   2 | T
   1 |   1 | W
   2 |   4 | F!
   2 |   4 | T!
   2 |   3 | TF
   2 |   4 | W!
   2 |   3 | WF
   2 |   2 | WT
   3 |   4 | TF!
   3 |   4 | WF!
   3 |   4 | WT!
   3 |   3 | WTF
   4 |   4 | WTF!
(15 rows)
Что за уродливый хак ?! (о, а выбор должен быть что-то вродеSELECT t1.str || t2.str AS result или жеSELECT t1.str AS str1, t2.str AS str2)
Хорошо, тогда задача состоит в том, чтобы обобщить запрос, чтобы принять параметр (n) для длины количества используемых символов. Вместо константы 2 у тебя :)
Отличное решение!
Tnx! Я не думаю, что функция generate_series () принимает текстовые аргументы (я даже не смотрел это вверх), так что этот уродливый хак действительно способ обойти это.
Да вы правы конечно Мне не нравятся строковые операции в СУБД, я предпочитаю видеть вещи в свете реляционной модели ...
0

того источникаИнформация

WITH RECURSIVE xtab AS (
    WITH no_cte AS (
    SELECT
    1::int AS len
    , idx as idx
    , unnest(ARRAY['MY','POSTGRESQL','VERSION','9.6']) as str
    FROM generate_series(1, array_length(ARRAY['MY','POSTGRESQL','VERSION','9.6'],1)) idx
    )
    SELECT t0.len as len
            , t0.idx
            , t0.str
    FROM no_cte t0
    UNION SELECT 1+t1.len
            , tc.idx
            , t1.str ||','|| tc.str AS str
    FROM xtab t1
    JOIN no_cte tc ON tc.idx > t1.idx
    )
    SELECT distinct
    array_to_string(ARRAY(SELECT DISTINCT trim(x) FROM unnest(string_to_array(str,',')) x),', ') FROM xtab
1
with chars as (
   select unnest(regexp_split_to_array('ABC','')) as c
)
select c1.c||c2.c
from chars c1
  cross join chars c2

with chars as (
   select unnest(regexp_split_to_array('ABC','')) as c
)
select c1.c||c2.c
from chars c1
  cross join chars c2
where c1.c < c2.c
@wildplasser: я думаю, что дубликаты можно легко обработать, добавивdistinct на внутренний запрос выбора. Так что CTE возвращает каждый символ только один раз
@wildplasser: вы имеете в виду лечить AB и BA одинаково? Я отредактировал свой ответ
Ага. Но c1.c & lt; Разрушитель связей c2.c уже обрабатывает (частично) это. КСТАТИ Мой "ABBA" пример был неверным, я должен был сказать "barabas"
Это выглядит намного элегантнее, чем у меня ;-( как я уже сказал: мне не нравятся строки. Кстати, это подавляет перестановки?
Вот что я имел в виду. Аналогичная проблема связана с дублированием символов в строке, например "ABBA". (это не было учтено в ОП)

Похожие вопросы