2010-08-26 9 views
4

J'ai besoin de prendre les résultats d'une requête par élément de ligne et de créer une liste délimitée par des points-virgules des matériaux qui composent cet élément.SQL Server 2008 - Chaîne de concaténation

Schéma Maquillage:

Tables: LineItems (article unique Listing) LineItems_Materials (Many to Many) Matériaux (matériel unique Annonce)

Articles de ligne: ID | LineItem 1 | '1A .1'

LineItems_Materials: ID | LineItemID | MaterialID 1 | 1 | 1 2 | 1 | 2 3 | 1 | 3

Matériaux: ID | Matériel 1 | béton 2 | Acier 3 | Dirt

Donc, pour la ligne 1 article (1A.1) Je veux montrer béton, acier, Dirt

Je sais que je peux écrire une fonction pour le faire. J'ai utilisé CTE dans la fonction .... Je pourrais aussi utiliser une boucle while. Y a-t-il une autre méthode qui serait meilleure?

Voici ce que je (script construire des objets, des données de charge et fonction de création):

SCRIPT: 
    IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[test].[UFN_LineItem_Materials]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) 
    DROP FUNCTION [test].[UFN_LineItem_Materials] 
    GO 
    IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[test].[LineItems]') AND type in (N'U')) 
    DROP TABLE [test].[LineItems] 
    GO 
    IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[test].[Materials]') AND type in (N'U')) 
    DROP TABLE [test].[Materials] 
    GO 
    IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[test].[LineItems_Materials]') AND type in (N'U')) 
    DROP TABLE [test].[LineItems_Materials] 
    GO 
    IF EXISTS (SELECT * FROM sys.schemas WHERE name = N'test') 
    DROP SCHEMA [test] 
    GO 
    CREATE SCHEMA [test] AUTHORIZATION [dbo] 
    GO 

    Create Table test.Materials(
    MaterialID INT IDENTITY(1,1), 
    Material varchar(100)); 

    Insert Into test.Materials 
    Values('Concrete'); 


    Insert Into test.Materials 
    Values('Steel'); 


    Insert Into test.Materials 
    Values('Dirt'); 

    GO 
    Create Table test.LineItems_Materials(
    LineItemMaterialID INT IDENTITY(1,1), 
    LineItemID   INT, 
    MaterialID   INT) 

    GO 

    Insert Into test.LineItems_Materials 
    Select 1,1 
    UNION 
    Select 1,2 
    UNION 
    Select 1,3 


    GO 


    CREATE TABLE [test].[LineItems](
     [LineItemID] [int] IDENTITY(1,1) NOT NULL, 
     [ItemNumber] [varchar](25) NULL 
    ) ON [PRIMARY] 

    GO 

    Insert Into [test].[LineItems] 
    Select '1A.1' 


    GO 
    ------------------------------------------------------------- 
    --Build Material Strings (;) example: List of Materials 
    ------------------------------------------------------------ 

    CREATE FUNCTION test.UFN_LineItem_Materials(@LineItemID INT) 
     RETURNS VARCHAR(100) 
    AS 

    BEGIN 

     DECLARE @Materials Varchar(100) = '' 
     ;with CTE 
     AS(
     Select lm.LineItemID,m.MaterialID,m.Material 
     from test.LineItems_Materials lm 
     inner join test.Materials m on lm.MaterialID = m.MaterialID 
     Where lm.LineItemID = @LineItemID 
     ) 
     Select @Materials += ';' + c.Material 
     from CTE c; 

     SET @Materials = substring(@Materials,2,LEN(@Materials)-1); 

     RETURN @Materials; 

    END 
    GO 

    Select lm.LineItemID,test.UFN_LineItem_Materials(lm.LineItemID) Materials 
    From test.Materials m 
    inner join test.LineItems_Materials lm on m.MaterialID = lm.MaterialID 
    Where m.Material = 'Concrete' 

D'autres idées?

apprécient toujours les évaluations

-S-

Répondre

6

Si vous souhaitez concaténer des valeurs à travers les lignes, utilisez le Trick XML, par exemple:

SELECT Name + ',' 
FROM Project 
FOR XML PATH('') 

Voici un exemple plus complet:

select LineItemID, (
     Select m.Material + ',' 
     From test.Materials m 
     inner join test.LineItems_Materials lm1 on m.MaterialID = lm1.MaterialID 
     Where m.MaterialID in (select MaterialID from test.LineItems_Materials where LineItemID = lm2.LineItemID) 
     FOR XML PATH('') 
    ) as Materials 
from test.LineItems_Materials lm2 
group by LineItemID 
+0

qui est de l'argent ... merci! – scarpacci

+0

C'est vraiment proche de ce que j'ai construit – scarpacci

+0

+1 pour la présentation la plus simple de 'for xml' – DonBecker

0

Ce qui suit TSQL fera l'affaire, il vous suffit de remplacer les noms de colonnes et des tables appropriées

DECLARE @vString NVARCHAR(500) 
SET @vString = '' 
SELECT @vString = @vString + ColumnNameToConcatenate + ',' 
FROM TableToPickColumnFrom 

SELECT SUBSTRING(@vString, 0, LEN(@vString) -1) AS ConcatenatedText 

Il a également retirez le , à la fin de la chaîne pour obtenir:

Valeur1, Valeur2, Value3

Rathe que:

Valeur1, Valeur2, Value3,

1

essayer quelque chose comme ceci:

declare @x table (x varchar(5)) 
insert @x values ('AAAA') 
insert @x values ('BBBB') 
insert @x values ('CCCC') 

SELECT 
    STUFF(
      (
       SELECT ','+x 
        FROM @x 
        FOR XML PATH('') 

      ), 1, 1, '' 
     ) AS ColName 

SORTIE:

ColName 
------------------- 
AAAA,BBBB,CCCC 

(1 row(s) affected) 

ou comme ceci:

declare @x table (RowID int, x varchar(5)) 
insert @x values (1,'AAAA') 
insert @x values (1,'BBBB') 
insert @x values (1,'CCCC') 
insert @x values (2,'aa') 
insert @x values (3,'abc') 
insert @x values (3,'123') 

SELECT 
    a.RowID, 
    STUFF(
      (
       SELECT ', '+b.x 
        FROM @x b 
        WHERE a.RowID=b.RowID 
        order by b.x 
        FOR XML PATH('') 
      ), 1, 2, '' 
     ) AS ColName 
    FROM @x a 
    GROUP BY a.RowID 

SORTIE:

RowID  ColName 
----------- ----------------- 
1   AAAA, BBBB, CCCC 
2   aa 
3   123, abc 

(3 row(s) affected)