3

Je suis en train de passer d'une base de données héritée mal conçue à une nouvelle base de données. Dans l'ancienne base de données, il y a tableA avec les champs Id et Commodities. Id est la clé primaire et contient un int et Commodities contient une liste délimitée par des virgules.SQL: fractionner une ligne en plusieurs (normalisation)

TableA:

id | commodities 
1135 | fish,eggs,meat  
1127 | flour,oil 

Dans la nouvelle base de données, je veux tableB être dans l'identifiant de forme, produit où chaque produit est un élément unique dans la liste délimitée par des virgules dans tableA.

TableB:

id | commodity 
1135 | fish 
1135 | eggs 
1135 | meat 
1127 | flour 
1127 | oil  

J'ai une fonction, fonctionn, que lorsque donné une carte d'identité, une liste et un séparateur, retourne une table avec un champ identifiant et élément. Comment puis-je utiliser cette fonction pour transformer les deux champs de tableA en tableB?

(Note: J'ai eu du mal à trouver quoi titre cette question S'il vous plaît ne hésitez pas à modifier le titre pour qu'il reflète plus précisément la question.!)

Voici le code de fonction:

ALTER FUNCTION dbo.functionA 
(
@id int, 
@List VARCHAR(6000), 
@Delim varchar(5) 
) 
RETURNS 
@ParsedList TABLE 
(
id int, 
item VARCHAR(6000) 
) 
AS 
BEGIN 
DECLARE @item VARCHAR(6000), @Pos INT 
SET @List = LTRIM(RTRIM(@List))+ @Delim 
SET @Pos = CHARINDEX(@Delim, @List, 1) 
WHILE @Pos > 0 
BEGIN 
SET @item = LTRIM(RTRIM(LEFT(@List, @Pos - 1))) 
IF @item <> '' 
BEGIN 
INSERT INTO @ParsedList (id, item) 
VALUES (@id, CAST(@item AS VARCHAR(6000))) 
END 
SET @List = RIGHT(@List, LEN(@List) - @Pos) 
SET @Pos = CHARINDEX(@Delim, @List, 1) 
END 
RETURN 
END 
+0

Qu'est-ce que vous voulez dire est communément refered comme normaliztion (1. forme normale pour être précis). Peut-être que vous voulez ajouter cela à votre titre. –

+0

Base de données? Version? –

+0

SQl Server 2000 – dmr

Répondre

2

Vous écrivez un batch SQL qui parcourt la table A et insère dans la table b les résultats de votre appel de fonction.

+0

Il semble qu'il devrait y avoir un ensemble basé sur la façon de le faire ... Je détesterais pour la seule solution d'être un lot ... Il doit y avoir une autre façon ... – kralco626

+0

Eh bien, selon cet article, vous avez besoin d'une boucle ou cette table de pointage. Donc, je suppose qu'il suffit d'utiliser le lot ... L'ensemble de l'idiologie semble échouer ici ... – kralco626

+0

cela peut être fait sans boucle, dans un seul 'INSERT', voir ma réponse. –

3

Vous avez besoin d'un moyen de diviser et de traiter la chaîne dans TSQL, il y a plusieurs façons de le faire. Cet article porte sur les avantages et les inconvénients de chaque méthode à peu près:

Arrays and Lists in SQL Server 2000 and Earlier

Vous devez créer une fonction split. Voici comment une fonction split peut être utilisé:

SELECT 
    * 
    FROM YourTable        y 
    INNER JOIN dbo.yourSplitFunction(@Parameter) s ON y.ID=s.Value 

[Je préfère l'approche de la table numérique pour découper une chaîne en TSQL] (Arrays and Lists in SQL Server 2000 and Earlier), mais il existe de nombreuses façons de diviser les chaînes dans SQL Server, voir le lien précédent , ce qui explique les PROs et les CONs de chacun.

Pour la méthode de table de nombres au travail, vous devez faire une configuration de table de temps, ce qui va créer une table Numbers qui contient des lignes de 1 à 10 000:

SELECT TOP 10000 IDENTITY(int,1,1) AS Number 
    INTO Numbers 
    FROM sys.objects s1 
    CROSS JOIN sys.objects s2 
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number) 

Une fois la table des numéros est mis en place , créez cette fonction split:

CREATE FUNCTION inline_split_me (@SplitOn char(1),@param varchar(7998)) RETURNS TABLE AS 
    RETURN(SELECT substring(@SplitOn + @param + ',', Number + 1, 
        charindex(@SplitOn, @SplitOn + @param + @SplitOn, Number + 1) - Number - 1) 
       AS Value 
      FROM Numbers 
      WHERE Number <= len(@SplitOn + @param + @SplitOn) - 1 
      AND substring(@SplitOn + @param + @SplitOn, Number, 1) = @SplitOn) 

GO 

Vous pouvez maintenant facilement découper une chaîne CSV dans une table et se joindre à elle:

select * from dbo.inline_split_me(';','1;22;333;4444;;') where LEN(Value)>0 

SORTIE:

Value 
---------------------- 
1 
22 
333 
4444 

(4 row(s) affected) 

pour vous faire une nouvelle utilisation de la table ceci:

--set up tables: 
create table TableA (id int, commodities varchar(8000)) 
INSERT TableA VALUES (1135,'fish,eggs,meat') 
INSERT TableA VALUES (1127,'flour,oil') 

Create table TableB (id int, commodities varchar(8000)) 

--populate TableB 
INSERT TableB 
    (id, commodities) 
SELECT 
    a.id,c.value 
    FROM TableA a 
     CROSS APPLY dbo.inline_split_me(',',a.commodities) c 

--show tableB contents: 
select * from TableB 

SORTIE:

id   commodities 
----------- ------------- 
1135  fish 
1135  eggs 
1135  meat 
1127  flour 
1127  oil 

(5 row(s) affected) 

EDIT après Conrad Frix commentaire sur SQL Server 2000 ne supportant pas CROSS APPLY

cela va faire la même chose:

INSERT TableB 
     (id, commodities) 
    SELECT 
     a.id,NullIf(SubString(',' + a.commodities + ',' , number , CharIndex(',' , ',' + a.commodities + ',' , number) - number) , '') 
     FROM TableA   a 
      INNER JOIN Numbers n ON 1=1 
     WHERE SubString(',' + a.commodities + ',' , number - 1, 1) = ',' 
     AND CharIndex(',' , ',' + a.commodities + ',' , number) - number > 0 
     AND number <= Len(',' + a.commodities + ',') 

et est basé sur le code de the link in the answer by @Rup. Il supprime fondamentalement l'appel de la fonction et fait la division dans la requête principale (en utilisant une division de table Numbers similaire), donc pas besoin d'un CROSS APPLY.

+1

CROSS APPLY ne fonctionnera pas en 2000 –

+0

@Conrad Frix, j'ai mis à jour ma réponse avec une seule instruction 'INSERT' qui incorpore la division de table Numbers, donc pas' CROSS APPLY' est nécessaire (et certainement pas de boucle), qui est basé sur le code dans [le lien dans la réponse par @Rup] (http://stackoverflow.com/questions/3214972/sql-break-up-one-row-into-many-normalization/3215431#3215431) –

+0

+ 1 pour aucune boucle. –

2

Appelez-moi paresseux, mais je tirer les lignes combinées de la base de données, les diviser, puis réintroduisez les lignes de division. Ce genre de chose semble un peu anormal pour SQL ...

1

SSIS a une transformation Unpivot très pratique si elle est disponible pour vous.

+1

SSIS est une fonctionnalité de 2005. –

1
create table Project (ProjectId int, Description varchar(50)); 
insert into Project values (1, 'Chase tail, change directions'); 
insert into Project values (2, 'ping-pong ball in clothes dryer'); 

create table ProjectResource (ProjectId int, ResourceId int, Name varchar(15)); 
insert into ProjectResource values (1, 1, 'Adam'); 
insert into ProjectResource values (1, 2, 'Kerry'); 
insert into ProjectResource values (1, 3, 'Tom'); 
insert into ProjectResource values (2, 4, 'David'); 
insert into ProjectResource values (2, 5, 'Jeff'); 

-- a bit of SQL magic involving XML and voila 
SELECT *, 
    (SELECT Name + ' ' AS [text()] 
    FROM ProjectResource pr 
    WHERE pr.ProjectId = p.ProjectId 
    FOR XML PATH ('')) AS ResourceList 
FROM Project p 

La sortie de ce sera:

ProjectId Description      ResourceList 
1   Chase tail, change directions  Adam Kerry Tom 
2   ping-pong ball in clothes dryer David Jeff 
Questions connexes