2016-04-28 5 views
2

Ich habe eine Spalte mit Zeichenfolge Wert mit Trennzeichen wie unten gezeigt, die ich im SELECT Teil der SQL-Abfrage verwenden.Umordnen eines Zeichenfolgenwerts mit Trennzeichen in einer SQL Select-Abfrage

0040~0040~0040~0040~0040^00~00~00~01~05^100~001~010~011~015^00~00~00~01~05 

einzelne Köpfe verwenden '^' getrennt, wie unten gezeigt.

Head1 = 0040~0040~0040~0040~0040 

Head2 = 00~00~00~01~05 

Head3 = 100~001~010~011~015 

Head4 = 00~00~00~01~05 

alle 4 Köpfe haben dieselbe Anzahl von Einträgen, die '~' begrenzt (Einträge nicht unbedingt 5) sind.

Was ich brauche, ist den ersten Eintrag von allen 4 Köpfen in einen zusammenführen. Wie nachfolgend dargestellt.

0040-00-100-00 von '-'

dann für den zweiten Eintrag begrenzt und so weiter für alle Einträge.

, wenn die Anzahl der Einträge 4 wie meine Zeichenfolge sind, sollte die formatierte Ausgabe wie folgt aussehen (die jeweils durch Komma getrennt Eintrag):

0040-00-100-00,0040-00-001-00,0040-00-010-00,0040-01-011-01, 
0040-05-015-05 

Ich möchte dies in der SELECT-Abfrage tun, wo ich diese holen Spaltenwert.

Beispiel ..

select x,y,z,(this is where I want this changes to be done.) from abc 
+0

Haben Ihre Zeilen eine ID-Spalte? – artm

+0

ja. Aber das ist nicht erforderlich, um dieses Problem zu lösen. – iamP

Antwort

1

Das Problem, das Sie vorgestellt haben, ist nicht leicht ein gelöst, sich an der Innenseite einer select-Anweisung zu begrenzen. Obwohl du gesagt hast, dass du es nicht getan hast, wenn du wüsstest, wie viele "Köpfe" es in jedem String geben würde, könntest du eine einzelne, komplizierte und sehr lange select-Anweisung (mit deinem "String-Wert mit Delimitern") aufrufen foobar):

SELECT x,y,z, SUBSTR(foobar, 1, INSTR(foobar, '~') - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^') + 1, INSTR(foobar, '~', INSTR(foobar, '^')) - INSTR(foobar, '^') - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 2) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 2)) - INSTR(foobar, '^', 1, 2) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 3) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 3)) - INSTR(foobar, '^', 1, 3) - 1) || ',' || SUBSTR(foobar, INSTR(foobar, '~') + 1, INSTR(foobar, '~', 1, 2) - INSTR(foobar, '~', 1, 1) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '~', INSTR(foobar, '^', 1, 1)) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 1)) - INSTR(foobar, '^', 1, 1) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '~', INSTR(foobar, '^', 1, 2)) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 2)) - INSTR(foobar, '^', 1, 2) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '~', INSTR(foobar, '^', 1, 3)) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 3)) - INSTR(foobar, '^', 1, 3) - 1) 
FROM abc 

und das wird nur die ersten beiden „Einträge“, aber es wird jeden „Kopf“ zu bekommen, unabhängig von der Länge und kann erweitert werden, um mehr Einträge enthalten, aber es wäre, wenn es nicht funktioniert Es gab nur zwei Einträge und die Select-Anweisung wurde um fünf Einträge erweitert, was unpraktisch ist, da Sie angegeben haben, dass eine Zeile beliebig viele Einträge enthalten kann. Es gibt eine Möglichkeit, dies mit einer CASE Anweisung zu erreichen, aber es ist sehr lang und sehr komplex. Hier ist eine Beispielabfrage, die können, was Sie suchen:

SELECT x, y, z, 
    CASE (LENGTH(SUBSTR(foobar, 1, INSTR(foobar, '^')))-LENGTH(REPLACE(SUBSTR(foobar, 1, INSTR(foobar, '^')), '~', '')) + 1) 
    WHEN 1 THEN SUBSTR(foobar, 1, INSTR(foobar, '^') - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 1) + 1, INSTR(foobar, '^', 1, 2) - INSTR(foobar, '^', 1, 1) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 2) + 1, INSTR(foobar, '^', 1, 3) - INSTR(foobar, '^', 1, 2) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 3) + 1, LENGTH(foobar) - INSTR(foobar, '^', 1, 3)) 
    WHEN 2 THEN SUBSTR(foobar, 1, INSTR(foobar, '~') - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^') + 1, INSTR(foobar, '~', INSTR(foobar, '^')) - INSTR(foobar, '^') - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 2) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 2)) - INSTR(foobar, '^', 1, 2) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 3) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 3)) - INSTR(foobar, '^', 1, 3) - 1) || ',' || SUBSTR(foobar, INSTR(foobar, '~') + 1, INSTR(foobar, '^', 1, 1) - INSTR(foobar, '~', 1, 1) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '~', INSTR(foobar, '^', 1, 1)) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 1), 1) - INSTR(foobar, '^', 1, 1) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '~', INSTR(foobar, '^', 1, 2)) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 2)) - INSTR(foobar, '^', 1, 2) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '~', INSTR(foobar, '^', 1, 3)) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 3)) - INSTR(foobar, '^', 1, 3) - 1) 
    ELSE 'Too many entries: ' || (LENGTH(SUBSTR(foobar, 1, INSTR(foobar, '^')))-LENGTH(REPLACE(SUBSTR(foobar, 1, INSTR(foobar, '^')), '~', '')) + 1) || ' entries given of 2 maximum' 
    END AS SAMPLE_OUTPUT 
FROM abc; 

Die Abfrage oben (wenn es über seine aktuelle Kapazität von zwei Einträgen erweitert) organisiert Ihre „Köpfe“. Es ist sehr lang und komplex, aber wenn Sie wissen, dass die Länge jedes Kopfes konstant ist (wie immer eine Länge von 4 pro Eintrag in "Head1"), dann kann der Code verkürzt werden, mit dem zusätzlichen Vorteil einer schnelleren Ausführung , wenn die Werte fest codiert sind ohne INSTR. Das Beispiel geht davon unter, dass die Kopflängen 4, 2, 3 und 2, wie in der Datenprobe:

SELECT x, y, z, 
    CASE (LENGTH(SUBSTR(foobar, 1, INSTR(foobar, '^')))-LENGTH(REPLACE(SUBSTR(foobar, 1, INSTR(foobar, '^')), '~', '')) + 1) 
    WHEN 1 THEN REPLACE(foobar, '^', '-') 
    WHEN 2 THEN SUBSTR(foobar, 1, 4) || '-' || SUBSTR(foobar, 11, 2) || '-' || SUBSTR(foobar, 17, 3) || '-' || SUBSTR(foobar, 25, 2) || ',' || SUBSTR(foobar, 6, 4) || '-' || SUBSTR(foobar, 14, 2) || '-' || SUBSTR(foobar, 21, 3) || '-' || SUBSTR(foobar, 28, 2) 
    WHEN 3 THEN SUBSTR(foobar, 1, 4) || '-' || SUBSTR(foobar, 16, 2) || '-' || SUBSTR(foobar, 25, 3) || '-' || SUBSTR(foobar, 37, 2) || ',' || SUBSTR(foobar, 6, 4) || '-' || SUBSTR(foobar, 19, 2) || '-' || SUBSTR(foobar, 29, 3) || '-' || SUBSTR(foobar, 40, 2) || ',' || SUBSTR(foobar, 11, 4) || '-' || SUBSTR(foobar, 22, 2) || '-' || SUBSTR(foobar, 33, 3) || '-' || SUBSTR(foobar, 43, 2) 
    ELSE 'Too many entries: ' || (LENGTH(SUBSTR(foobar, 1, INSTR(foobar, '^')))-LENGTH(REPLACE(SUBSTR(foobar, 1, INSTR(foobar, '^')), '~', '')) + 1) || ' entries given of 3 maximum' 
    END AS SAMPLE_OUTPUT 
FROM abc; 

Eine letzte Anmerkung: Ich empfehle eine Spalte Alias ​​für die Case-Anweisung aus Gründen verwenden, die sehr werden Offensichtlich sollten Sie versuchen, den Code ohne einen zu testen.

+0

Ja, das funktioniert gut, wenn es 2 Einträge gibt. Da meine Eintragslänge nicht festgelegt ist, muss ich einen anderen Weg finden, um das zu lösen. Danke für deine Antwort. :) – iamP

+0

@iamP Die zweite Abfrage kann um weitere Einträge erweitert werden. Das obige Beispiel unterstützt nur bis zu zwei Einträge, aber es können weitere hinzugefügt werden. Wenn Sie möchten, werde ich die Abfrage erweitern, damit sie mehr Einträge unterstützt. –

+0

Die Einträge können beliebig nummeriert werden. Ich denke, es wird viel länger dauern, wenn die Zahl 5+ oder sogar 4+ geht. Ihr Code ist einfacher zu verstehen. Gibt es eine Möglichkeit, Fall 1 in irgendeiner Weise für eine beliebige Anzahl von Einträgen zu wiederholen, um es kurz zu machen? – iamP

1

Erste CTE ist nur eine Auswahl, um Dummy-Datensätze zu erstellen, können Sie das ignorieren. Der zweite CTE ist rekursiv, um die Spaltenwerte nach^pro ID zu teilen. Der dritte CTE besteht darin, Abschnitte mit - unter Verwendung von XML pro ID zu kombinieren. Letztes CTE soll alle zu einer einzigen Zeichenfolge kombinieren, die durch , getrennt ist. Musste eine ID-Spalte haben, um sie zu gruppieren, bevor alles kombiniert wurde.Es wird mit n Anzahl von Zeilen arbeiten.

;WITH myTable (id, MyColumn) 
AS (SELECT 1, '0040~0040~0040~0040^00~00~00~01~05^100~001~010~011~015^00~00~00~01~05' 
union ALL SELECT 2, 'b040~0040~0040~0040^b0~00~00~01~05^b00~001~010~011~015^b02~00~00~01~05' 
union ALL SELECT 3, 'c040~0040~0040~0040^c0~00~00~01~05^c00~001~010~011~015^c02~00~00~01~05' 
union ALL SELECT 4, 'd040~0040~0040~0040^d0~00~00~01~05^d00~001~010~011~015^d02~00~00~01~05' 
), 
RecursiveCTE (id, sub, startInd, endInd) 
AS 
(
    SELECT 
     id, 
     convert(varchar, SUBSTRING(MyColumn, 1, CHARINDEX('^', MyColumn))) as sub 
     , 0 AS startInd 
     , CHARINDEX('^', MyColumn) AS endInd 
     from mytable 
    UNION ALL 
     select id, 
     convert(varchar, SUBSTRING((SELECT MyColumn FROM myTable WHERE id = dr.id), endInd + 1, endInd - startInd)), 
     endInd, 
     CHARINDEX('^', (SELECT MyColumn FROM myTable WHERE id = dr.id), endInd + 1) as endInd 
    from RecursiveCTE dr 
     where dr.endInd <> 0 
), 
PerIDSeperatedByDash AS (
select id, 
STUFF((
select '-' + SUBSTRING(drinner.sub, 1, CHARINDEX('~', drinner.sub) - 1) 
from RecursiveCTE drinner 
WHERE (drinner.id = drouter.id) 
FOR XML PATH ('')),1,1,'') AS DashSeperated 
from RecursiveCTE drouter 
group by id) 
SELECT DISTINCT STUFF((
SELECT ',' + ds.DashSeperated 
FROM PerIDSeperatedByDash ds 
ORDER BY id 
FOR XML PATH ('')),1,1,'') 
from PerIDSeperatedByDash 
+0

Danke für die Antwort. Kann ich es im ausgewählten Format verwenden, das ich erwähnt habe, indem ich nur den letzten Auswahlparameter durch den Code ersetzt habe? – iamP

+0

@iamP Ich glaube nicht, dass Sie CTEs als innere Auswahl verwenden können. Warum muss es im 'select * from (query here)' Format sein? – artm

Verwandte Themen