Манипулирование строками SQL для возврата нескольких строк - PullRequest
1 голос
/ 07 мая 2010

Я опытный программист, но относительно новый для SQL. Мы используем Oracle 10 и 11. У меня есть система, использующая SQL, которая объединяет фактические строки с виртуальными строками (например, «SELECT 1 from DUAL»), объединяя их и пересекая при необходимости, что, похоже, все работает.

Моя проблема в том, что мне нужно объединить эту систему, которая ожидает строки данных, с новыми данными, которые будут содержать данные (скажем, для упрощения) в разделенных запятыми строках.

Так что я думаю, что мне нужен способ конвертировать строку типа: «5,6,7,8» в 4 строки с одним столбцом в каждой, с «5» в первом ряду, «6» во втором и т. д. На других языках я бы делал разделение с запятой в качестве разделителя. Конечно, данные не всегда будут иметь 4 записи.

Есть второй вопрос, но я задам его отдельно. Но я подозреваю, что это упростит вещи, если это возможно, если решение вышеупомянутого может быть использовано в качестве таблицы в другом операторе SQL (т.е. для работы с моей существующей системой). Спасибо за любую помощь.

Ответы [ 3 ]

2 голосов
/ 07 мая 2010

выглядит некрасиво, но работает:

select r
      ,substr(','||csv||',',
              instr(','||csv||',',',',1,r)+1,
              instr(','||csv||',',',',1,r+1)-instr(','||csv||',',',',1,r)-1) v
from   (select '5,6,7a,8b,,bob' csv from dual)
      ,(select rownum r from dual connect by level <= 4000)
where  instr(csv||',',',',1,r) > 0;

R   V
=   =
1   5
2   6
3   7a
4   8b
5
6   bob
1 голос
/ 07 мая 2010

Для использования SQL вы можете сделать следующее ...

CREATE OR REPLACE TYPE tab_varchar2 AS TABLE OF VARCHAR2( 4000 );
/

CREATE OR REPLACE FUNCTION string_to_rows
   ( pv_string   IN   VARCHAR2
   , pv_delimiter   IN   VARCHAR2   DEFAULT   '_'
   )
   RETURN tab_varchar2
   PIPELINED
AS
   lv_string   VARCHAR2( 32767 )   DEFAULT   pv_string || pv_delimiter;
   lv_num   PLS_INTEGER;
BEGIN
   LOOP
      lv_num := INSTR( lv_string, pv_delimiter );
      EXIT WHEN ( NVL( lv_num, 0 ) = 0 );

      PIPE ROW( LTRIM( RTRIM( SUBSTR( lv_string, 1, lv_num - 1 ) ) ) );

      lv_string := LTRIM( SUBSTR( lv_string, lv_num + 1 ) );

    END LOOP;

    RETURN;
END;
/

Тогда вы можете запустить что-то вроде этого ...

SELECT c.owner, c.table_name, c.column_name, c.data_type
     , t.column_value   column_token
  FROM dba_tab_columns c
     , TABLE( string_to_rows( c.column_name ) ) t
 WHERE c.owner = 'SYS'
   AND c.table_name = 'DBA_INDEXES'
   AND c.column_name = 'AVG_LEAF_BLOCKS_PER_KEY'

Что бы вернуть это ...

Row# OWNER TABLE_NAME  COLUMN_NAME             DATA_TYPE COLUMN_TOKEN
---- ----- ----------- ----------------------- --------- ------------
1    SYS   DBA_INDEXES AVG_LEAF_BLOCKS_PER_KEY NUMBER    AVG
2    SYS   DBA_INDEXES AVG_LEAF_BLOCKS_PER_KEY NUMBER    LEAF
3    SYS   DBA_INDEXES AVG_LEAF_BLOCKS_PER_KEY NUMBER    BLOCKS
4    SYS   DBA_INDEXES AVG_LEAF_BLOCKS_PER_KEY NUMBER    PER
5    SYS   DBA_INDEXES AVG_LEAF_BLOCKS_PER_KEY NUMBER    KEY
1 голос
/ 07 мая 2010

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

Если вам абсолютно НЕОБХОДИМО сделать это в SQL, эта статья покажет вам, как это сделать:

http://www.oracle.com/technology/oramag/code/tips2007/070907.html

...