Записки жертвы высоких технологий

    Здравствуйте! Мой блог посвящен в большей своей части моей профессиональной деятельности на поприще использования технологий ORACLE для разработки баз данных OLTP и OLAP хранилищ данных. В заметках я периодически размещаю разнообразные SQL, PL/SQL и Java скрипты написанные мной и не только мной, ссылки на интересные источники в сети либо другую полезную информацию, которая каким либо образом касается моей работы с замечательными продуктами компании ORACLE.
    Вы можете связаться со мной по контактному емейлу, если у вас есть какие-либо вопросы, связанные с разработкой баз данных на основе продуктов ORACLE, буду рад помочь вам, если это будет в моих силах.

23 мая 2019 г.

Разбиение CSV-значений в таблице на элементы

Много я ковырялся с разбиением строк на токены пока не натолкнулся на интересную задачку, связанную с разбиением CSV-значения на элементы и cross join полученных элементов с исходной записью. Для начала реализовал ее с помощью XMLTable, но такой способ требует подготовки исходного значения для использования в XMLTable, т.к. каждый элемент списка должен быть помещен в кавычки, например использование значения 'a,b,c,d,e' невозможно, но '"a","b","c","d","e"' вполне подходит. К сожалению, не всегда возможно изменять строку перед такой операцией, например из-за лимита длины буфера, как в моем случае. Я решил поискать альтернативные решения, которые позволяли бы выполнять cross join элементов без изменения исходной строки. В конце концов нашел на asktom.oracle.com прекрасный вариант, который меня полностью удовлетворил:


SQL> CREATE TABLE MY_TABLE(

  2  R_ID int,
  3  R_Site varchar(20),
  4  R_Name varchar(20),
  5  R_Role varchar(20)
  6  );

Table created.

SQL>
SQL> INSERT INTO MY_TABLE(R_ID , R_Site , R_Name , R_Role )
  2  VALUES ('1' , '123,-456,-789', 'qwer' , 'Owner');

1 row created.

SQL>
SQL> INSERT INTO MY_TABLE(R_ID , R_Site , R_Name , R_Role )
  2  VALUES ('2' , '56,-741,-852', 'qaz' , 'Manager');

1 row created.

SQL>
SQL>
SQL> select
  2    t.r_id,
  3    t.r_name,
  4    t.r_role,
  5    regexp_substr(t.R_Site, '[^,]+', 1, commas.column_value)  as site
  6  from
  7    MY_TABLE t,
  8    table(cast(multiset(select level from dual connect by  level <= regexp_count(t.R_Site, '[^,]+')) as sys.OdciNumberList)) commas;

      R_ID R_NAME                                                       R_ROLE                                                       SITE
---------- ------------------------------------------------------------ ------------------------------------------------------------ --------------------
         1 qwer                                                         Owner                                                        -456
         1 qwer                                                         Owner                                                        -789
         1 qwer                                                         Owner                                                        123
         2 qaz                                                          Manager                                                      -741
         2 qaz                                                          Manager                                                      -852
         2 qaz                                                          Manager                                                      56

6 rows selected.


Единственное, что я сделал с найденным примером, так это заменил невнятное выражение в CONNECT BY на приведенный в примере выше вызов regexp_count(...) и убрал секцию ORDER BY из-за зависания сессии.