2009-08-20 17 views
7

tengo una columna VARCHAR que contiene 5 informaciones (2 CHAR(3) y 3 TIMESTAMP) separados con '$'.Dividir un VARCHAR en DB2 para recuperar un valor dentro

CREATE TABLE MYTABLE (
    COL VARCHAR(256) NOT NULL 
); 

INSERT INTO MYTABLE 
VALUES 
    ('AAA$000$2009-10-10 10:50:00$null$null$null'), 
    ('AAB$020$2007-04-10 10:50:00$null$null$null'), 
    ('AAC$780$null$2007-04-10 10:50:00$2009-04-10 10:50:00$null') 
; 

me gustaría extraer el cuarto campo ...

'AAA$000$2009-10-10 10:50:00$null$null$null' 
          ^^^^ this field 

... para tener algo como

SELECT SPLIT(COL, '$', 4) FROM MYTABLE 

1 
----- 
'null' 
'null' 
'2009-04-10 10:50:00' 

que estoy buscando, en este orden:

  1. Una función de cadena incorporada de DB2
  2. Un integrable declaración como SUBSTR(COL, POSSTR(COL)+1)...
  3. Una función definida por el usuario que se comporta como SPLIT

Precisión: Sí, lo hago saben que no es una buena idea tener este tipo de columnas ...

Respuesta

6
CREATE FUNCTION split(pos INT, delimeter CHAR, string VARCHAR(255)) 
LANGUAGE SQL 
RETURNS VARCHAR(255) 
DETERMINISTIC NO EXTERNAL ACTION 
BEGIN ATOMIC 
    DECLARE x INT; 
    DECLARE s INT; 
    DECLARE e INT; 

    SET x = 0; 
    SET s = 0; 
    SET e = 0; 

    WHILE (x < pos) DO 
     SET s = locate(delimeter, string, s + 1); 
     IF s = 0 THEN 
      RETURN NULL; 
     END IF; 
     SET x = x + 1; 
    END WHILE; 

    SET e = locate(delimeter, string, s + 1); 
    IF s >= e THEN 
     SET e = LENGTH(string) + 1; 
    END IF; 
    RETURN SUBSTR(string, s + 1, e - s -1); 
END! 

Uso:

SELECT split(3,'$',col) from mytable; -- or 
SELECT split(0,'-', 'first-second-third') from sysibm.sysdummy1; 
SELECT split(0,'-', 'returns this') from sysibm.sysdummy1; 
SELECT split(1,'-', 'returns null') from sysibm.sysdummy1; 
+0

Al aceptarlo, ya que es exactamente lo que busqué (tercera opción). ¿Puedes agregar el modificador 'DETERMINISTIC' y' NO EXTERNAL ACTION', para poder usarlo en una cláusula 'GROUP BY'? –

+0

Buen punto. Modificado para devolver NULL cuando el elemento no se encuentra. Al solicitar el primer elemento, con deliemeter no existente se devolverá la cadena original. – Toni

4

Estoy seguro de que hay una mejor manera de escribir esto, pero aquí hay 1 (SQL) solución para el caso simple dado. Podría reescribirse como un procedimiento almacenado para buscar cualquier cadena arbitraria. También puede haber algunas herramientas 3ª parte/extensiones para ayudar a cabo w/la división que desee ...

select 
locate('$', col, (locate('$',col, (locate('$',col) +1))) + 1) as poss3rdDollarSign, -- position of 3rd dollar sign 
locate('$', col, (locate('$', col, (locate('$',col, (locate('$',col) +1))) + 1)) + 1) as poss4thDollarSign, -- position of 4th dollar sign 
    (locate('$', col, (locate('$', col, (locate('$',col, (locate('$',col) +1))) + 1)) + 1)) - 
    (locate('$', col, (locate('$',col, (locate('$',col) +1))) + 1)) - 1 as stringLength,-- length of string between 3rd and 4th dollar sign 
    substr(col, locate('$', col, (locate('$',col, (locate('$',col) +1))) + 1) + 1, (locate('$', col, (locate('$', col, (locate('$',col, (locate('$',col) +1))) + 1)) + 1)) - 
    (locate('$', col, (locate('$',col, (locate('$',col) +1))) + 1)) - 1) as string 
    from mytable 
+0

+1 por el esfuerzo :-). También terminé haciendo algo similar, pero estoy buscando algo más * genérico *. –

+0

Esto es horrible, pero era justo lo que necesitaba para ayudarme con el análisis de cadenas en una consulta. ¡Gracias! –

+0

Gracias por la función localizar. Ayudado con algún otro análisis sintáctico de cadenas, necesité convertir un VARCHAR con: USD al final de él en un DECMIAL. –

1

probar esto, funciona!

CREATE FUNCTION SPLIT(P_1 VARCHAR(3200), 
         P_2 VARCHAR(200)) 
    RETURNS TABLE(P_LIST VARCHAR(3200)) 
    SPECIFIC SPLIT 
    LANGUAGE SQL 
    MODIFIES SQL DATA 
    NO EXTERNAL ACTION 
F1: BEGIN 
    return 
    with source(str, del) as 
     (select p_1, p_2 from sysibm.sysdummy1), 
      target(str, del) as 
      (select source.str, source.del from source 
       where length(source.str) > 0 
     union all 
      select 
      (case when (instr(target.str, target.del) > 0) 
            then substr(target.str, 
               instr(target.str, target.del)+1, 
                length(target.str)-instr(target.str, target.del))         else null end), 
       (case when (instr(target.str, target.del) > 0) 
               then target.del else null end) 
       from target 
       where length(target.str) > 0 
       ) 
     select str from target 
     where str is not null; 
END 
+0

¿Cuál es la diferencia entre su respuesta y las anteriores? –

0

Si la versión de DB2 puede hacerlo, puede utilizar entonces la función de LOCATE_IN_STRING a la posición encontrada de su separación. La función LOCATE_IN_STRING devuelve la posición inicial de una cadena y le permite elegir la enésima instancia. Puede encontrado documentación de esta función here

Para su ejemplo, puede utilizar este código:

select 
substring(col, LOCATE_IN_STRING(col, '$', 1, 3), LOCATE_IN_STRING(col, '$', 1, 4) - LOCATE_IN_STRING(col, '$', 1, 3))      
from MYTABLE            
Cuestiones relacionadas