2016-04-28 7 views
2

Ich fand einige verwandte Beispiele hier auf SO, aber nicht eine, die für diesen Fall speziell. Hier gehts:Parsen einer SQL-Zeichenfolge für Integer-Werte mit mehreren Delimitern

Ich muss SQL verwenden, um Daten zu analysieren, die aus einer flachen Datei stammen, die durch Pipe getrennt ist. Eines der Felder ist wie folgt unterformatiert. Mein Endzustand ist, die ganzen Zahlen innerhalb des Feldes zu summieren, aber meine Frage hier ist, Wege zu sehen, einen SQL SELECT zu verwenden, um JUST die ganzen Zahlen zu analysieren/zu extrahieren. Das Muster der Unterformatierung wird immer dort sein, wo den gewünschten Ganzzahlen eine Tilde (~) vorangestellt wird und ein Stern (*) folgt. Die Anzahl der Unterfelder kann auch variieren (mein Beispiel hat 5, aber es könnte mehr oder weniger). Der 4-Zeichen-TAG-Name spielt keine Rolle.

So, hier ist ein Beispiel:

|GADS~55.0*BILK~0.0*BOBB~81.0*HETT~32.0*IGGR~51.0| 

Aus diesem Beispiel alles, was ich für die Verarbeitung wollen würde, ist die endgültige Anzahl von 219. Auch hier kann ich auf der Summe Teil als weiteren Schritt arbeiten; nur daran interessiert, die Zahlen zu bekommen.

Ich weiß, wie man mit UNIX-Tools (dh AWK, sed, etc.) recht einfach umgehen kann, aber nicht in SQL. Irgendwelche Vorschläge helfen! Vielen Dank!

+0

Was ist mit den ganzen Zahlen, die in anderen Rohrabschnitten erscheinen? Müssen sie auch summiert werden, zusammen oder getrennt? – trincot

+0

Danke Trincot. Integer (eigentlich alle Werte) aus anderen Rohrabschnitten benötigen keine Logik oder Arithmetik für diese Änderung. Dies ist das einzige durch Pipe getrennte Feld, das dies benötigt. – sirabhorn

+0

Ich bekomme immer noch nicht, was mit einem Feld passieren soll, das diesen Wert hat '| GADS ~ 55.0 * BILK ~ 0.0 | BOBB ~ 81.0 * HETT ~ 32.0 | IGGR ~ 51.0 |' Sollte sich in eine oder mehrere Zahlen verwandeln? Welche? – trincot

Antwort

1

Das wird wieder mit 219:

  • In Cut1 lasse ich die Zeichenfolge mit der ersten Nummer starten.
  • In CUT2 schneide ich das Endrohr und fügen Sie ein Sternchen
  • In Splitted ersetze ich die ~ mit XML-Tags es so leicht macht aufsummiert zu spalten sind die Zeichenfolge
  • Im letzten Wählen Sie die reinen Zahlen ...

Jetzt ist der Code

DECLARE @str VARCHAR(100) = '|GADS~55.0*BILK~0.0*BOBB~81.0*HETT~32.0*IGGR~51.0|'; 

WITH Cut1 AS 
(
    SELECT SUBSTRING(@str,CHARINDEX('~',@str,1)+1,1000) AS c1 
) 
,Cut2 AS 
(
    SELECT SUBSTRING(Cut1.c1,1,LEN(Cut1.c1)-1) + '*' AS c2 FROM Cut1 
) 
,Splitted AS 
(
    SELECT CAST('<x>' + REPLACE(Cut2.c2,'~','</x><x>') + '</x>' AS XML) AS AsXML FROM Cut2 
) 
SELECT SUM(CAST(SUBSTRING(numbers.value('.','varchar(100)'),1,CHARINDEX('*',numbers.value('.','varchar(100)'),1)-1) AS FLOAT)) 
FROM Splitted 
CROSS APPLY AsXML.nodes('/x') AS the(numbers) 
+0

Wow, das ist das beste bisher. sah auch in meinen Tests gut aus. sehr skalierbar. – sirabhorn

+0

@sirabhorn, froh, das zu lesen! Ein Hinweis (der auch für die andere Seite gilt: Sie sollten sicher sein, dass Ihre Strings keine XML-verbotenen Zeichen wie '<, > und &' enthalten. Wenn ja, müssten Sie sie mit '<und> und & ' – Shnugo

1

Ist es das, was Sie wollen (es berechnet die Summe 219)? Sorry für die hässliche Formatierung, aber sicher kannst du es selbst "hübsch" machen.

declare @Input varchar(255), 
     @InputModified varchar(255) 

set @Input = '|GADS~55.0*BILK~0.0*BOBB~81.0*HETT~32.0*IGGR~51.0|' 


SELECT 
@InputModified = 
(
    CAST(CAST((
      SELECT SUBSTRING(@Input, Number, 1) 
      FROM master..spt_values 
      WHERE Type='p' AND Number <= LEN(@Input) AND 
      (
       SUBSTRING(@Input, Number, 1) LIKE '[0-9]' 
       or 
       SUBSTRING(@Input, Number, 1) LIKE '~' 
       or 
       SUBSTRING(@Input, Number, 1) LIKE '.' 
      ) 
      FOR XML Path('')) 
     AS xml) AS varchar(MAX)) 
) 


select sum(cast(Integers as float)) 
from 
(
    SELECT 
    LTRIM(RTRIM(m.n.value('.[1]','varchar(8000)'))) AS Integers 
    FROM 
    (
     SELECT CAST('<XMLRoot><RowData>' + REPLACE(@InputModified,'~','</RowData><RowData>') + '</RowData></XMLRoot>' AS XML) AS x 
    )t 
    CROSS APPLY x.nodes('/XMLRoot/RowData')m(n) 
) Integers 
where IsNumeric(Integers.Integers) = 1 
+0

Reboon - Danke! das macht den Trick. Und ich habe es mit einigen meiner anderen Testdatensätzen versucht und es hat sich wie erwartet verhalten. Ich kann daran arbeiten, es hübscher zu machen, danke nochmal – sirabhorn

+0

Ihre Lösung nutzt das XML-Splitting auch ... Ich mag es! Hallo @sirabhorn, sagen "Danke" ist sehr nett, aber es wäre noch netter, diese Antwort mit der Annahmeprüfung unter dem Stimmzähler zu akzeptieren, wenn dies dein Problem löst. [Bitte lesen Sie: jemand-Antworten] (http://stackoverflow.com/help/someone-answers). +1 von meiner Seite! – Shnugo

+0

@Shnugo - fertig, schätze den Zeiger – sirabhorn

0

zusätzlicher Ansatz rekursiven CTE mit:

DECLARE @Input VARCHAR(255) 
SET @Input = '|GADS~55.0*BILK~0.0*BOBB~81.0*HETT~32.0*IGGR~51.0|' 
;WITH cte (n) 
     AS (SELECT n = 1 
      UNION ALL 
      SELECT n + 1 
      FROM  cte 
      WHERE n <= LEN(@Input) 
      ) 

SELECT SUM(CONVERT(FLOAT, val)) 
FROM (SELECT SUBSTRING(STUFF(@Input, 1, t.n, ''), 1, 
           PATINDEX('%[*|]%', STUFF(@Input, 1, t.n, '')) - 1) AS Val 
     FROM cte AS t 
     WHERE SUBSTRING(@Input, t.n, 
         PATINDEX('%[*|]%', SUBSTRING(@Input, t.n, 99))) LIKE '~%[*|]' 
     ) AS T 

Test istHERE

+0

Interessant, cool zu sehen, eine andere Möglichkeit, es zu tun, aber mein lokaler Lauf dieser (und Ihre Test-Link) hat eine Ergebnismenge von 168, wenn es 219 sein sollte. Bin ich etwas fehlt? Sieht aus wie diese Lösung berücksichtigt nicht den letzten ganzzahligen Wert (51.0) dank – sirabhorn

+0

Ja, es ist 168 zurück, weil ich Kriterien für die Suche nach Wert genau wie in Ihrer Frage eingefügt habe ('den gewünschten Ganzzahlen wird * eine Tilde vorangestellt (~) und gefolgt durch einen Stern (*) * '), aber der letzte ganzzahlige Wert (51.0) folgt nicht auf das Sternchen' * ', deshalb wurde es ignoriert.Ich habe meine Antwort mit Suchwerten aktualisiert, nicht nur gefolgt von einem Sternchen '*' aber gefolgt von einer Pipe '|', so dass jetzt die gewünschte Ausgabe zurückgegeben wird. – Vasily

+0

Guter Punkt! Meine Spezifikationen benötigten diese Klarheit, da meine Anforderungen den letzten Wert benötigen ... und eine großartige Nachfolgelösung. – sirabhorn

0

ich versucht habe zu sein different.Also ich keine Trennzeichen bin nach. Also kein Begrenzer folgen kann Stärke dieser Technik sein.

Ich extrahiere nur die erste Zahl aus String und lösche String von 0 bis diese Nummer und so weiter.

So ist es sehr klar, dass wir es mit rekursiven CTE, while-Schleife und mit row_number tun können.

Hier habe ich versucht, rekursive CTE zu verwenden, die nicht optimiert werden kann, aber irgendwann macht es Spaß, anders zu sein.

DECLARE @Input VARCHAR(255) 
SET @Input = '|GADS~55.0*BILK~0.0*BOBB~81.0*HETT~32.0*IGGR~51.0|'; 

Diese Linie für mich den Trick,

select Left(SubString(@Input, PatIndex('%[0-9.]%', @Input),8000), 
PatIndex('%[^0-9.]%', SubString(@Input, 
PatIndex('%[0-9.]%', @Input), 8000))-1) 

komplettes Script,

;With CTE as 
(
Select @Input Input, Left(SubString(@Input, PatIndex('%[0-9.]%', @Input), 8000), PatIndex('%[^0-9.]%', SubString(@Input, PatIndex('%[0-9.]%', @Input), 8000))-1) XX 

union all 

select cast(stuff(Input,1, charindex(XX,Input)+len(XX)-1,'') as varchar(255)) 
,Left(SubString(cast(stuff(Input,1, charindex(XX,Input)+len(XX)-1,'') as varchar(255)), PatIndex('%[0-9.]%', cast(stuff(Input,1, charindex(XX,Input)+len(XX)-1,'') as varchar(255))), 8000), PatIndex('%[^0-9.]%', SubString(cast(stuff(Input,1, charindex(XX,Input)+len(XX)-1,'') as varchar(255)), PatIndex('%[0-9.]%', cast(stuff(Input,1, charindex(XX,Input)+len(XX)-1,'') as varchar(255))), 8000))-1) 
from cte where len(input)>0 

) 

select sum(cast(XX as float)) from cte 
where xx is not null or xx<>'' 
Verwandte Themen