2016-06-17 17 views
0

lassen Sie sich ein super-vereinfachte Daten Beispiel annehmen:SQL Split Linie während des Imports - lief INSRERT INTO zweimal für bestimmte Linien

ID AKey AVal 
----------------- 
1  AB  94 
2  Q  48 
3  Z  56 
4  AB  12 
5  T  77 
... ... ... 

Ich mag "AB" in getrennte Linien "A" und "B" spalten in meinem Importskript, wo ich in der Regel tun:

INSERT INTO MyNewTable 
SELECT 
    SRC.ID as OldIDRef, 
    SRC.AKey as NewKey, 
    SRC.AVal as NewVal 
FROM OldTable as SRC 

Also im Grunde möchte ich die „AB“ Linien in der Auswahl duplizieren und einige Berechnungen spezifisch für diese Linie durchführen (lassen Sie uns divide Aval sagen von 2)

Die einzige Lösung, die ich wäre some so denken kann:

INSERT INTO MyNewTable 
SELECT 
    SRC.ID as OldIDRef, 
    CASE SRC.AKey = 'BA' THEN SUBSTRING(SRC.AKey,1,1) END as NewKey, 
    CASE SRC.AKey = 'BA' THEN SRC.AVal/2 END as NewVal 
FROM OldTable as SRC 

UNION ALL 

SELECT 
    SRC.ID as OldIDRef, 
    SUBSTRING(SRC.AKey,2,1) as NewKey, 
    SRC.AVal/4 + 10 as NewVal 
FROM OldTable as SRC 
WHERE SRC.AKey = 'BA' 

ich einen solchen Prozess dann einige Male in meinem Importe benötigen, so frage ich mich, wenn ich nicht etwas einfachere Lösung fehlt?

+0

Sind Sie OK, die alle Nicht-AB wird NULL sein? –

+0

Gibt es eine maximale Länge für Akey? – Matt

+0

Alle nonAB - Ich fürchte nein, aber sie können leicht verbunden werden ... Ich schaue auf Ihre Antwort unter. –

Antwort

0

Angenommen, Sie wahrscheinlich eine Antwort suchen, der auf N # skaliert werden kann Zeichen in der Schlüssellänge und eine, die den neuen Wert auf die Anzahl der Schlüssel aufteilt, die davon getrennt werden. Ich würde mit einer rekursiven Cte gehen, um es abzuziehen. Fügen Sie mit Ihren Beispieldaten eine weitere Zeile mit 3 Zeichen wie "GHI" hinzu, und führen Sie diesen Code aus, und sehen Sie die Ergebnisskala nach nur 2 Zeichen.

;WITH cteRecursive AS (
    SELECT 
     Id 
     ,AKey 
     ,LEFT(AKey,1) AS NewAKey 
     ,RIGHT(Akey,LEN(AKey) - 1) AS RemainingKey 
     ,AVal 
     ,1 AS [Level] 
    FROM 
     @Table 

    UNION ALL 

    SELECT 
     t.Id 
     ,t.AKey 
     ,LEFT(c.RemainingKey,1) AS NewAKey 
     ,RIGHT(RemainingKey,LEN(RemainingKey) - 1) AS RemainingKey 
     ,t.AVal 
     ,c.[Level] + 1 AS [Level] 
    FROM 
     @Table t 
     INNER JOIN cteRecursive c 
     ON t.Id = c.Id 
     AND LEN(c.RemainingKey) > 0 
) 

SELECT 
    Id 
    ,AKey AS OriginalAKey 
    ,NewAKey 
    ,AVal AS OriginalAVal 
    ,AVal/2.00 AS NewVal 
    ,AVal/CAST(MAX([Level]) OVER (PARTITION BY Id) AS DECIMAL(4,2)) AS NewValAsPortionOfLevel 
    ,AVal/CAST(LEN(AKey) AS DECIMAL(4,2)) AS NewValAsPortionOfKeyLength 
FROM 
    cteRecursive 

Hier ist die Tabelle Variable I, wenn Sie es

DECLARE @Table AS TABLE (Id INT IDENTITY(1,1), AKey VARCHAR(100), AVal INT) 
INSERT INTO @Table (AKey, AVal) 
VALUES ('AB',94),('Q',48),('Z',56),('AB',12),('T',77),('ghi',100) 

Wenn nicht den Schlüssel aufteilen wollen Sie mit Hilfe des rekursiven CTE tatsächlich vereinfachen und diesen Weg gehen. Durch die Verwendung von Level < LEN(AKey) stoppt die Rekursion an der richtigen Stelle und Sie brauchen keine der anderen String-Manipulationen.

;WITH cteRecursive AS (
    SELECT 
     Id 
     ,AKey 
     ,AVal 
     ,1 AS [Level] 
    FROM 
     @Table 

    UNION ALL 

    SELECT 
     t.Id 
     ,t.AKey 
     ,t.AVal 
     ,c.[Level] + 1 AS [Level] 
    FROM 
     @Table t 
     INNER JOIN cteRecursive c 
     ON t.Id = c.Id 
     AND c.[Level] < LEN(t.Akey) 
) 

SELECT 
    Id 
    ,AKey AS OriginalAKey 
    ,AVal AS OriginalAVal 
    ,AVal/2.00 AS NewVal 
    ,AVal/CAST(MAX([Level]) OVER (PARTITION BY Id) AS DECIMAL(4,2)) AS NewValAsPortionOfLevel 
    ,AVal/CAST(LEN(AKey) AS DECIMAL(4,2)) AS NewValAsPortionOfKeyLength 
FROM 
    cteRecursive 

Und eine andere Technik, wenn Sie eine sehr große Datenmenge haben und wollen keine Rekursion verwenden, ist, dass Sie eine Tally Tabelle erstellen können, um auf Join. Ich wäre neugierig zu wissen, was besser funktioniert. Ich habe tatsächlich eine permanente Tally-Tabelle zur Verwendung in einigen der Datensatzmanipulation für ETL von Data Warehouse, die ich verwende, aber Sie sollten wahrscheinlich eine temporäre Tabelle anstelle einer Tabellenvariablen auch verwenden. Wie auch immer, hier ist diese Methode.

DECLARE @TallyTable AS TABLE (I INT) 

DECLARE @MaxLen INT 
SELECT @MaxLen = MAX(LEN(AKey)) FROM @Table 

IF (@MaxLen > 0) 
BEGIN 
    WHILE @MaxLen > 0 
    BEGIN 
     INSERT INTO @TallyTable (I) VALUES (@MaxLen) 
     SET @MaxLen -= 1 
    END 
END 

SELECT 
    * 
    ,NewValueApportionedByLengthOfKey = CAST(AVal AS DECIMAL)/ISNULL(NULLIF(LEN(AKey),0),1) 
FROM 
    @Table t 
    INNER JOIN @TallyTable tt 
    ON LEN(t.AKey) >= tt.I 

Hinweis all diese Methoden übernimmt AKey nie NULL oder 0 Länge sein, aber alle sind leicht zu handhaben, dass angepasst sollte es eine Notwendigkeit sein.

+0

Ich habe dich vielleicht mit dem nichtssagenden SUBSTRING ein bisschen in die Irre geführt, aber insgesamt kann die CTE-Logik angewendet werden und scheint machbarer zu sein. Ich werde beide Antworten testen. –

+0

Wenn Sie den eigentlichen Schlüssel nicht verschütten müssen und Sie nur die Zeilen wiederholen müssen, würde ich das ohne die rekursive Cte ​​anders machen. Ich würde eine Tally-Tabelle basierend auf der maximalen Länge des Schlüssels erstellen und dann auf LEN von verbinden der Schlüssel. Wenn ich heute die Chance habe, auf meinen Sohn aufzupassen, werde ich versuchen, so etwas hier zu schreiben. – Matt

+0

Ich habe zwei Methoden hinzugefügt, die keine Notwendigkeit zeigen, den AKey-Wert zu ändern. – Matt

0

, Auf jeden Fall hier ist die Schrift, die für gegebene Werte arbeitet und sollte doppelt so schnell wie UNION ALL sein:

;WITH s1 as (
    SELECT ID, LEFT(AKey,1) as NewKey1, AVal/2 as NewVal1 
     , RIGHT(AKey,1) as NewKey2, AVal/4 + 10 as NewVal2 
     , AKey, AVal 
    FROM Split) 
SELECT ID as OldIDRef, 
    CASE NKey.AKey WHEN 'A' THEN NewKey1 WHEN 'B' THEN NewKey2 ELSE s1.AKey END as NewKey, 
    CASE NKey.AKey WHEN 'A' THEN NewVal1 WHEN 'B' THEN NewVal2 ELSE s1.AVal END as NewVal 
FROM s1 
INNER JOIN (SELECT 'A' as AKey UNION ALL SELECT 'B' UNION ALL SELECT NULL) as NKey 
ON NKey.AKey = NewKey1 or NKey.AKey = NewKey2 or (NKey.AKey is Null and not (NewKey1 = 'A' and NewKey2 = 'B')) 
+0

Ich denke, ich habe dich ein bisschen in die Irre geführt, der Schlüssel muss nicht buchstäblich gespalten werden. Die Daten einiger spezifischer Linien mischen einfach zwei Dinge miteinander und ich muss einige Berechnungen über bestimmte Felder durchführen, um die zwei logischen Teile aufzuteilen und sie getrennt in eine neue Datenbank zu bringen. –