2016-12-15 2 views
1

Ich möchte Datensätze einschließen, die nur einen Wert an einer bestimmten Position innerhalb einer Zeichenfolge mit Trennzeichen aufweisen. In den folgenden Strings möchte ich beispielsweise nur den Wert angeben, in dem sich Daten in Spalte 5 befinden. Also, nur Beispiel B qualifiziert:SQL: Filter für alle Daten in der Spalte mit Trennzeichen

Beispiel A: PV1|column1data||column3data|column4data||||column8data

Beispiel B: PV1|column1data||column3data||column5data|||column8data

+3

Welche DBMS verwenden Sie? Postgres? Orakel? –

+1

Warum werden diese Daten nicht zuerst in eine richtige Tabelle geladen? Warum eine Zeichenkette speichern? Haben Sie Einfluss darauf? –

+0

Nein, ich habe keinen Einfluss darauf. –

Antwort

1

Wenn Ihre Daten immer hat das gleiche Format des Artikels 9 und 8 Separatoren:

select * from tab where col not like '%|%|%|%|%||%|%|%' 

Das heißt, es könnte bessere Möglichkeiten in Ihrem RDBMS geben, wenn Sie uns wissen lassen, welche Sie verwenden. Außerdem ist das Speichern mehrerer Elemente in einer Spalte eines der schlimmsten Anti-Patterns in einer relationalen Datenbank.

Update für gezackte Daten

Für eine unbekannte Anzahl von Elementen in Ihren Daten, dies in SQL Server arbeiten könnte (je nach Leerzeichen und ANSI-Einstellungen):

select * 
from tab 
where col not like replicate('%|', 5) + replicate('|%', len(col) - len(replace(col, '|', '')) - 5) 

Hier wir sind Berechnen der Anzahl der Elemente für jeden Wert und dynamisches Erstellen des like-Musters zum Arbeiten wie oben. Andere Datenbanken sollten ähnliche Funktionen haben, die weitgehend dieselbe Logik ermöglichen. Ich bin nicht sicher, wie viel besser die Antworten erhalten können, ohne mehr Informationen zu haben.

+0

Dies ist nicht wirklich eine Datenbank. Es ist eine Abfrage zu Schnittstellentransaktionen. Ich versuche nur diejenigen Transaktionen herauszufiltern, die Daten in einem bestimmten Feld enthalten. Die Anzahl der Elemente und Trennzeichen ist nicht festgelegt. –

+0

@LeslieHarrison sollten Sie Ihre Frage erweitern, um einzuschließen, welche Systeme Sie verwenden, und weitere Beispiele dieser ungleich formatierten Daten einschließen. –

0

Der Operator LIKE ist langsam und wird etwas langsam sein, sobald Sie ein paar hunderttausend Datensätze getroffen haben. Diese Antwort ist nicht super schnell, aber wird die Aufgabe erledigen und erfordert keine SQL Server CLR. Wenn das gewählte System Oracle ist, benötigen Sie 11g, um die gleiche Syntax zu verwenden.

Sie haben eine Tabelle der Annahme ...

create table udata (
    ID int primary key identity(1,1) 
    , string varchar(2000) not null 
); 

Sie werden eine Variable für diese für zukünftige Änderungen wollen, sondern auch für Skript Flexibilität.

declare @delimiter varchar(1) = '|' 

Dann alles in einem CTE einrichten.

;with parser as (
    select 
      ID 
     , 0 as colNum 
     , substring(d.string, endPos + (2 * delimLen), len(d.string)) as string 
     , startPos 
     , endPos 
    from udata d 
     cross apply (
      select 
        len(@delimiter) as startPos 
       , case charindex(@delimiter,d.string) when 0 then len(d.string) + len(@delimiter) else charindex(@delimiter,d.string) end - len(@delimiter) as endPos 
       , len(@delimiter) as delimLen 
     ) p 
    where id between 2000 and 10000 
    union all 
    select 
      ID 
     , colNum + 1 as colNum 
     , substring(d.string, p.endPos + (2 * delimLen), len(d.string)) as string 
     , d.endPos + (2 * delimLen) as startPos 
     , d.endPos + (delimLen) + p.endPos as endPos 
    from parser d 
     cross apply (
      select 
        len(@delimiter) as startPos 
       , case charindex(@delimiter,d.string) when 0 then len(d.string) + len(@delimiter) else charindex(@delimiter,d.string) end - len(@delimiter) as endPos 
       , len(@delimiter) as delimLen 
     ) p 
    where string != '' 
), selector as (
    select u.id, p.colNum, substring(u.string, p.startPos, p.endPos - p.startPos + len(@delimiter)) as colVal--,u.string, p.startPos, p.endPos 
    from udata u 
     inner join parser p 
      on p.ID = u.ID 
) 

Was dies bedeutet ist, markieren Sie zunächst die Standorte für den Beginn und das Ende jeder Spalte Wert, dann werden die Wähler Scheiben es aus dem Quellstring. Beachten Sie die Where-Klausel im ersten Teil der rekursiven Abfrage: where id between 2000 and 10000 Sie möchten hier Ihre Datensätze begrenzen. Dies könnte sein, wo Sie es auf die Arten von Datensätzen beschränken, nach denen Sie suchen.

Schließlich wählen Sie Ihre Spalten in einer Pivot für eine einfache Lesung aus:

select * 
from selector 
    pivot (
     max(colVal) for colNum in ([1],[2],[3],[4],[5],[6],[7],[8]) 
    ) pv 

Die ursprünglichen Zeilen stattdessen zurückgegeben werden können Ihre ursprünglichen Kriterien wie folgt aus:

select * 
from udata u 
where exists (
     select top 1 1 
     from selector s 
     where s.colNum = 5 
      and s.colVal ='' 
      and s.ID = u.ID 
    ) 

Meine Testdaten enthält ~ 160.000 Zeilen, und die Abfrage existiert, ohne die ID-Grenzen in der CTE, dauerte 18 Sekunden auf Laptop-Hardware ausgeführt werden. Trotzdem sollte das den Trick machen.

Verwandte Themen