2009-08-20 14 views
7

Mam kolumnę VARCHAR, która zawiera 5 informacji (2 i 3 TIMESTAMP) oddzielonych "$".Dzielenie VARCHAR w DB2 w celu pobrania wartości wewnątrz

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') 
; 

chciałbym wyodrębnić pole 4th ...

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

... mieć coś podobnego

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

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

szukam w tej kolejności:

  1. Funkcja ciągów wbudowanych DB2
  2. zabudowany oświadczenie takie jak SUBSTR(COL, POSSTR(COL)+1)...
  3. użytkownik definiuje funkcję, która zachowuje się jak SPLIT

Precyzja: Tak, robię wiedzieć, że to nie jest dobry pomysł, aby mieć takie kolumny ...

Odpowiedz

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! 

Zastosowanie:

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

Zaakceptowanie go, ponieważ jest to dokładnie to, czego szukałem (trzecia opcja). Czy możesz po prostu dodać modyfikator 'DETERMINISTIC' i' NO EXTERNAL ACTION', aby móc go używać w klauzuli "GROUP BY"? –

+0

Dobra uwaga. Zmieniono, aby zwrócić NULL, gdy element nie został znaleziony. Żądanie pierwszego elementu, z nieistniejącym-deliemetrem, zwróci oryginalny ciąg. – Toni

4

Jestem pewien, że istnieje lepszy sposób, aby to napisać, ale tutaj jest 1 (SQL) rozwiązanie dla tego prostego przypadku. Można go przepisać jako procedurę składowaną, aby wyszukać dowolne ciągi znaków. Nie mogą być również niektóre 3rd strona narzędzia/rozszerzenia, aby pomóc w/split chcesz ...

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 za wysiłek :-). Również skończyłem robić coś podobnego, ale szukam czegoś bardziej * ogólnego *. –

+0

To jest okropne, ale to było właśnie to, co było mi potrzebne, aby mi pomóc z jakimś parsowaniem w zapytaniu. Dzięki! –

+0

Dzięki za funkcję lokalizacji. Pomagając w innym analizowaniu łańcuchów, musiałem przekonwertować VARCHAR na: USD na końcu na DECMIAL. –

1

spróbować, to działa!

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

jaka jest różnica między twoją odpowiedzią a poprzednimi? –

0

Jeśli można to zrobić w wersji DB2, można użyć funkcji LOCATE_IN_STRING do znalezienia pozycji separatora. Funkcja LOCATE_IN_STRING zwraca pozycję początkową ciągu znaków i umożliwia wybór N-tej instancji. można znaleźć w dokumentacji tej funkcji here

Dla Twojej przykład, można użyć tego kodu:

select 
substring(col, LOCATE_IN_STRING(col, '$', 1, 3), LOCATE_IN_STRING(col, '$', 1, 4) - LOCATE_IN_STRING(col, '$', 1, 3))      
from MYTABLE            
Powiązane problemy