2016-06-17 2 views
0

supposons une donnée super-simplifiée exemple:SQL ligne de séparation lors de l'importation - exécuter INSRERT EN deux fois pour des lignes spécifiques

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

Je voudrais partager « AB » en plusieurs lignes « A » et « B » dans mon script d'importation, où je fais habituellement:

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

donc, fondamentalement, je voudrais dupliquer les lignes « AB » dans la sélection et effectuer des calculs spécifiques pour cette ligne (disons diviser AVAL par 2)

La seule solution que je peux penser serait someting comme ceci:

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' 

je besoin d'un tel processus plus de quelques reprises dans mes importations, alors je me demande, si je ne manque pas une solution plus simple?

+0

Êtes-vous OK tous les Non-AB seront NULL? –

+0

Y a-t-il une longueur maximale pour Akey? – Matt

+0

Tous nonAB - Je crains que non, mais ils peuvent être facilement rejoints ... Je regarde votre aval. –

Répondre

0

Disons que vous êtes probablement une réponse qui peut être mise à l'échelle N Nombre de caractères dans la longueur de la clé et un qui ations la nouvelle valeur au nombre de clés qui en sont séparées. J'irais avec un cte récursif pour le retirer. Avec vos données d'échantillon, ajoutez une autre ligne avec 3 caractères comme 'GHI' et exécutez ce code et voyez l'échelle des résultats au-delà de seulement 2 caractères.

;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 

Voici la variable tableau I utilisé si vous le voulez

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) 

Si ne pas diviser la clé que vous pouvez réellement simplifier la cte récursive et aller dans cette voie. En utilisant Level < LEN(AKey) la récursion s'arrêtera au bon endroit et vous n'avez besoin d'aucune des autres manipulations de chaînes.

;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 

Et une autre technique si vous avez un ensemble de données très volumineux et ne souhaitez pas utiliser la récursivité, est que vous pouvez construire une table Tally Pour Inscrivez-vous sur. Je serais curieux de savoir qui fonctionne le mieux. J'ai en fait une table de pointage permanente à utiliser dans certaines manipulations d'enregistrements pour l'ETL de l'entrepôt de données que j'utilise, mais vous devriez probablement utiliser une table temporaire plutôt qu'une variable de table. Quoi qu'il en soit, voici cette méthode.

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 

Notez toutes ces méthodes supposent AKey ne sera jamais NULL ou 0 longueur, mais tous sont facilement adaptés pour gérer cela devrait-il être nécessaire.

+0

Je vous ai peut-être trompé avec SUBSTRING unnessesary un peu, mais dans l'ensemble la logique CTE peut être appliquée et semble être plus faisable. Je vais tester les deux réponses. –

+0

Si vous n'avez pas besoin de renverser la clé actuelle et que vous avez juste besoin de répéter les lignes, je le ferais différemment sans le ct récursif je construirais une table de pointage basée sur la longueur maximale de la clé et la rejoindrait sur LEN de la clé. Si j'ai une chance entre prendre soin de mon fils aujourd'hui, je vais essayer d'écrire quelque chose comme ça ici. – Matt

+0

J'ai ajouté deux méthodes qui ne reflètent pas la nécessité de changer la valeur AKey. – Matt

0

Quoi qu'il en soit, voici le script, qui travaille pour des valeurs et devrait être deux fois plus vite que UNION ALL:

;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

Je pense que je vous ai trompé un peu, la clé ne doit pas être littéralement divisée. Les données sur certaines lignes spécifiques mélangent simplement deux choses ensemble et je dois effectuer quelques calculs sur des champs particuliers pour séparer les deux parties logiques et les mettre dans une nouvelle base de données séparément. –