Esta página fue traducida automáticamente. Lee el original en inglés. English

Biblioteca de IBSurgeon

Procedimiento almacenado de Firebird para dividir una línea con delimitadores en un conjunto de registros

Esta es una de las tareas que todo desarrollador en Firebird PSQL enfrenta tarde o temprano: ¿cómo dividir una línea que contiene valores con delimitadores en un array de valores separados? Algo como esto:

Code

select * from sp_split_into_words('foo rio bar firebird,mysql,postgresql;mssql;redis/rty');

Salida:
######

WORD
==========
foo
rio
bar
firebird
mysql
postgresql
mssql
redis
rty

Es posible hacerlo en SQL puro: utilice el siguiente código de procedimiento almacenado, cuyo autor es Pavel Zotov, ingeniero de QA de Firebird y DBA principal de IBSurgeon.

Tenga en cuenta que este procedimiento contiene una amplia lista de posibles delimitadores, incluido el espacio, por lo que si solo necesita una coma o un punto doble, puede eliminar otros delimitadores de la variable a_del.

También preste atención al tamaño del varchar devuelto (variable de retorno word): por defecto es varchar(50), puede ajustarlo según sus necesidades.

Code
set term ^;
create or alter procedure sp_split_into_words(
    a_text varchar(2048) character set utf8,
    a_dels varchar(20) default ',.<>/?;:''"[]{}`~!@#$%^&*()-_=+\|/',
    a_special char(1) default ' '
)
returns (
  word varchar(50)
) as
begin
-- SP auxiliar, utilizado solo en oltp_data_filling.sql para llenar la tabla PATTERNS
-- con combinaciones misceláneas de palabras para usar en pruebas SIMILAR TO.
for
    with recursive
    j as( -- bucle #1: transformar lista de delimitadores en filas
        select s,1 i, substring(s from 1 for 1) del
        from(
          select replace(:a_dels,:a_special,'') s
          from rdb$database
        )

        UNION ALL

        select s, i+1, substring(s from i+1 for 1)
        from j
        where substring(s from i+1 for 1)<>''
    )

    ,d as(
        select :a_text s, :a_special sp from rdb$database
    )
    ,e as( -- bucle #2: realizar reemplazo de cada delimitador por `espacio`
        select d.s, replace(d.s, j.del, :a_special) s1, j.i, j.del
        from d join j on j.i=1

        UNION ALL

        select e.s, replace(e.s1, j.del, :a_special) s1, j.i, j.del
        from e
        -- nb: aquí 'columna desconocida: e.i' error en versiones antiguas de 2.5,
        -- p. ej.: WI-V2.5.2.26540 (carta de Alexey Kovyazin, 24.08.2014 14:34)
        join j on j.i = e.i + 1
    )
    ,f as(
        select s1 from e order by i desc rows 1
    )

    ,r as ( -- bucle #3: realizar división del texto en palabras individuales
        select iif(t.k>0, substring(t.s from t.k+1 ), t.s) s,
             iif(t.k>0,position( del, substring(t.s from t.k+1 )),-1) k,
             t.i,
             t.del,
             iif(t.k>0,left(t.s, t.k-1),t.s) word
        from(
          select f.s1 s, d.sp del, position(d.sp, s1) k, 0 i from f cross join d
        )t

        UNION ALL

        select iif(r.k>0, substring(r.s from r.k+1 ), r.s) s,
             iif(r.k>0,position(r.del, substring(r.s from r.k+1 )),-1) k,
             r.i+1,
             r.del,
             iif(r.k>0,left(r.s, r.k-1),r.s) word
        from r
        where r.k>=0
    )
    select word from r where word>''
    into
        word
do
    suspend;
end
^
set term ;^
commit;