2013-11-19 9 views
1

У меня есть таблица со строкой, которая содержит несколько ограниченных значений, например. a;b;c.разделите строку на несколько строк

Мне нужно разбить эту строку и использовать ее значения в запросе. Например, у меня есть следующие таблицы:

str 
a;b;c 
b;c;d 
a;c;d 

Мне нужно сгруппировать по одному значению из str столбца, чтобы получить следующий результат:

str count(*) 
a 1 
b 2 
c 3 
d 2 

Можно ли реализовать с помощью одного запроса на выборку? Я не могу создать временные таблицы для извлечения там значений и запроса к этой временной таблице.

+0

[Вы FINDER ответ здесь] (https://blogs.oracle.com/aramamoo/entry/how_to_split_comma_separated_string_and_pass_to_in_clause_of_select_statement) –

+0

@PrzemyslawKruglej, я видел этот пример. Он работает только в том случае, если у меня есть одна строка, но у меня много строк. –

ответ

4

Ваш комментарий к @PrzemyslawKruglej answer

Главная проблема связана с внутренним запросом с connect by, его генерирует удивительное количество рядов

Количество сгенерированных строк может быть уменьшено с помощью следующего подхода:

/* test table populated with sample data from your question */ 
SQL> create table t1(str) as(
    2 select 'a;b;c' from dual union all 
    3 select 'b;c;d' from dual union all 
    4 select 'a;c;d' from dual 
    5 ); 
Table created 

-- number of rows generated will solely depend on the most longest 
-- string. 
-- If (say) the longest string contains 3 words (wont count separator `;`) 
-- and we have 100 rows in our table, then we will end up with 300 rows 
-- for further processing , no more. 
with occurrence(ocr) as( 
    select level 
    from (select max(regexp_count(str, '[^;]+')) as mx_t 
      from t1) t 
    connect by level <= mx_t 
) 
select count(regexp_substr(t1.str, '[^;]+', 1, o.ocr)) as generated_for_3_rows 
    from t1 
cross join occurrence o; 

Результат: Для три строки, в которых самый длинный один состоит из трех слов, мы будем генерировать 9 строк:

GENERATED_FOR_3_ROWS 
-------------------- 
        9 

Окончательный запрос:

with occurrence(ocr) as( 
    select level 
    from (select max(regexp_count(str, '[^;]+')) as mx_t 
      from t1) t 
    connect by level <= mx_t 
) 
select res 
    , count(res) as cnt 
    from (select regexp_substr(t1.str, '[^;]+', 1, o.ocr) as res 
      from t1 
     cross join occurrence o) 
where res is not null 
group by res 
order by res; 

Результат:

RES   CNT 
----- ---------- 
a    2 
b    2 
c    3 
d    2 

SQLFIddle Demo

Узнать больше о regexp_count() (11g и выше) и regexp_substr() функции регулярного выражения.

Примечание: Функции регулярного выражения относительно дороги для вычисления, и когда дело доходит до обработки очень большого количества данных, возможно, стоит рассмотреть возможность переключения на простой PL/SQL. Here is an example.

+0

спасибо, хороший подход –

1

Это некрасиво, но, похоже, работает. Проблема с расщеплением CONNECT BY заключается в том, что он возвращает повторяющиеся строки. Мне удалось избавиться от них, но вы должны проверить:

WITH 
    data AS (
    SELECT 'a;b;c' AS val FROM dual 
    UNION ALL SELECT 'b;c;d' AS val FROM dual 
    UNION ALL SELECT 'a;c;d' AS val FROM dual 
) 
SELECT token, COUNT(1) 
    FROM (
    SELECT DISTINCT token, lvl, val, p_val 
     FROM (
     SELECT 
      regexp_substr(val, '[^;]+', 1, level) AS token, 
      level AS lvl, 
      val, 
      NVL(prior val, val) p_val 
      FROM data 
     CONNECT BY regexp_substr(val, '[^;]+', 1, level) IS NOT NULL 
    ) 
    WHERE val = p_val 
) 
GROUP BY token; 
TOKEN     COUNT(1) 
-------------------- ---------- 
d        2 
b        2 
a        2 
c        3
+0

Этот подход работает для трех строк, которые не будут работать для 100 строк. Основная проблема заключается в внутреннем запросе с 'connect by', он генерирует поразительное количество строк, и разграничение займет много времени. Я выполнил этот запрос по реальным данным (val содержит до 7 конкатенированных значений), с тех пор он запускается 2 минуты, и он все еще работает –

+1

@michaelnesterenko Да, это проблема с подходом CONNECT BY.Не могли бы вы использовать хранимую функцию, которая создавала строки, и вы бы использовали функцию 'TABLE' для запроса этой функции? –

Смежные вопросы