2011-04-01 6 views
4

Dans ma base de données SQL 2005, j'ai une table avec des valeurs stockées en tant qu'ID avec des relations avec d'autres tables. Donc dans mon MyDBO.warranty tableau, je stocke product_id au lieu de product_name afin d'économiser de l'espace. Le nom_produit est stocké dans MyDBO.products.SQL: imbriqué SELECT avec plusieurs valeurs dans un seul champ

Lorsque le service marketing tire les informations démographiques, la requête sélectionne le nom correspondant pour chaque ID de tableaux connexes (dégrossi par souci de concision):

SELECT w1.warranty_id AS "No.", 
     w1.created AS "Register Date" 
     w1.full_name AS "Name", 
     w1.purchase_date AS "Purchased", 
     (
      SELECT p1.product_name 
      FROM WarrDBO.products p1 WITH(NOLOCK) 
      WHERE p1.product_id = i1.product_id 
     ) AS "Product Purchased", 
     i1.accessories 
FROM WarrDBO.warranty w1 
LEFT OUTER JOIN WarrDBO.warranty_info i1 
    ON i1.warranty_id = w1.warranty_id 
ORDER BY w1.warranty_id ASC 

Maintenant, mon problème est que la colonne « accessoires » sur les magasins de table de warranty_info plusieurs valeurs:

No.  Register Date Name    Purchased  Accessories 
--------------------------------------------------------------------- 
1500 1/1/2008   Smith, John  Some Product 5,7,9 
1501 1/1/2008   Hancock, John Another   2,3 
1502 1/1/2008   Brown, James  And Another  2,9 

que je dois faire quelque chose de similaire avec « Accessoires » que je l'ai fait avec « produit » et tirer accessory_name du MyDBO.accessories table utilisant accessory_id. Je ne suis pas sûr de savoir par où commencer, car je devrais d'abord extraire les ID, puis concaténer plusieurs valeurs dans une chaîne. Donc, chaque ligne aurait "accessname1, accessoryname2, accessoryname3":

No.  Register Date Name    Purchased  Accessories 
--------------------------------------------------------------------- 
1500 1/1/2008   Smith, John  Some Product Case,Bag,Padding 
1501 1/1/2008   Hancock, John Another   Wrap,Label 
1502 1/1/2008   Brown, James  And Another  Wrap,Padding 

Comment faire?

EDIT >> Affichage mon code final:

J'ai créé cette fonction:

CREATE FUNCTION SQL_GTOInc.Split 
(
    @delimited varchar(50), 
    @delimiter varchar(1) 
) RETURNS @t TABLE 
(
-- Id column can be commented out, not required for sql splitting string 
    id INT identity(1,1), -- I use this column for numbering splitted parts 
    val INT 
) 
AS 
BEGIN 
    declare @xml xml 
    set @xml = N'<root><r>' + replace(@delimited,@delimiter,'</r><r>') + '</r></root>' 

    insert into @t(val) 
    select 
    r.value('.','varchar(5)') as item 
    from @xml.nodes('//root/r') as records(r) 

    RETURN 
END 

et mis à jour mon code en conséquence:

SELECT w1.warranty_id, 
     i1.accessories, 
     (
      CASE 
       WHEN i1.accessories <> '' AND i1.accessories <> 'NULL' AND LEN(i1.accessories) > 0 THEN 
        STUFF(
         (
          SELECT ', ' + a1.accessory 
          FROM MyDBO.accessories a1 
          INNER JOIN MyDBO.Split(i1.accessories, ',') a2 
          ON a1.accessory_id = a2.val 
          FOR XML PATH('') 
         ), 1, 1, '' 
        ) 
       ELSE '' 
      END 
     ) AS "Accessories" 
FROM MyDBO.warranty w1 
    LEFT OUTER JOIN MyDBO.warranty_info i1 
    ON i1.warranty_id = w1.warranty_id 
+0

Eh bien, il (devrait) aller sans dire, mais vous ne devriez pas stocker une colonne de acccessories dans les warranty_info comme ça. Vous devriez avoir une table de jointure, Warranty_info_accessories qui contient deux colonnes: le warranty_id et le accessory_id. Cela simplifierait grandement votre problème. – Gerrat

+0

Je suis d'accord, c'est ce que j'aurais fait si j'avais construit la structure. Malheureusement, l'auteur de la DB faisait tout ce qu'il pouvait pour économiser de l'espace, et c'était l'une des méthodes qu'il utilisait. – Dexter

+0

J'apprécie toute l'aide des gars, et si je pouvais choisir plus d'une réponse, je le ferais. Apparemment, je ne lisais pas assez attentivement et j'ai omis le 'FOR XML PATH ('')' dans ma déclaration, causant l'erreur. J'ai appliqué toutes les suggestions, et cela ne fonctionne pas parfaitement. – Dexter

Répondre

5

Vous pouvez écrire une fonction de valeur table qui divise simplement une chaîne séparée par des virgules en XML et transforme les nœuds XML en lignes.

Voir: http://www.kodyaz.com/articles//t-sql-convert-split-delimeted-string-as-rows-using-xml.aspx

Inscrivez-vous pour les accessoires grâce à la suite de l'appel de la fonction, et d'autres choses le résultat à séparées par des virgules liste des noms.

code non testé:

SELECT w1.warranty_id AS "No.", 
     w1.created AS "Register Date" 
     w1.full_name AS "Name", 
     w1.purchase_date AS "Purchased", 
     (
      SELECT p1.product_name 
      FROM WarrDBO.products p1 WITH(NOLOCK) 
      WHERE p1.product_id = i1.product_id 
     ) AS "Product Purchased", 
     STUFF(
     (
      SELECT 
      ', ' + a.name 
      FROM [table-valued-function](i1.accessories) acc_list 
      INNER JOIN accessories a ON acc_list.id = a.id 
      FOR XML PATH('') 
     ), 1, 1, '' 
     ) AS [accessories] 
FROM WarrDBO.warranty w1 
    LEFT OUTER JOIN WarrDBO.warranty_info i1 
    ON i1.warranty_id = w1.warranty_id 
ORDER BY w1.warranty_id ASC 
+0

Essayé cela, et j'obtiens une erreur: 'Sous-requête a renvoyé plus de 1 valeur. Cela n'est pas autorisé lorsque la sous-requête suit =,! =, <, <= , >,> = ou lorsque la sous-requête est utilisée comme expression. Si je mets une clause where et ne sélectionne qu'un seul accessoire de la liste, cela fonctionne correctement. Je ne pense pas que ça va me permettre d'utiliser la fonction dans une sous-requête dans le cadre d'un STUFF() – Dexter

1

Vous avez juste besoin d'utiliser la fonctionnalité de FOR XML SQL Server pour facilement chat chaînes:

Exemple de la blog lié:

SELECT 
    STUFF(
    (
    SELECT 
     ' ' + Description 
    FROM dbo.Brands 
    FOR XML PATH('') 
    ), 1, 1, '' 
) As concatenated_string 

Pour analyser un champ qui a déjà été stocké sous forme séparés par des virgules, vous devrez écrire une UDF qui analyse le champ et retourne une table qui peut ensuite être utilisé avec un prédicat IN dans WHERE clause. Regardez here for starters, et here.

+0

Ok, donc cela sélectionnerait tous les accessoires de la table d'accessoires et les mettrait en une seule corde. Mais comment puis-je obtenir les identifiants d'accessoires à partir de la chaîne délimitée par des virgules "2,3,9"? Et une fois que j'ai cela, comment les utiliser comme condition pour le SELECT dans le STUFF? – Dexter

+0

Merci, je vais regarder à travers cela. – Dexter

3

Rien à voir avec votre question.Juste une note que votre peut aussi être écrit requête originale, en déplaçant le subqery à une jointure, comme:

SELECT w1.warranty_id AS "No.", 
     w1.created AS "Register Date" 
     w1.full_name AS "Name", 
     w1.purchase_date AS "Purchased", 
     p1.product_name AS "Product Purchased", 
     i1.accessories 
FROM WarrDBO.warranty w1 
INNER JOIN WarrDBO.products p1 
    ON p1.product_id = i1.product_id 
LEFT OUTER JOIN WarrDBO.warranty_info i1 
    ON i1.warranty_id = w1.warranty_id 
ORDER BY w1.warranty_id ASC 
+0

Merci de m'avoir informé. J'ai essayé de déplacer tous ces sélections vers les JOINTS INTÉRIEURS, mais j'ai eu du mal à former cette partie. J'ai probablement 5 telles tables que je tire de la même manière, et en ajoutant plus de 2 INNER JOINs, ma requête ne retourne rien. Pas certain de pourquoi. – Dexter

+0

Une sous-requête dans la liste 'SELECT' peut généralement être transformée en une jointure' LEFT'. Pas de jointure "INNER". –

Questions connexes