web-dev-qa-db-fra.com

Rejoindre avec des valeurs séparées par des virgules dans SQL Server

J'ai une table [CourseMaster] COMME

CourseId    CourseName
-----------------------
  01          ABC
  02          DEF
  03          GHI
  04          JKL
  05          MNO
  06          PQR
  07          STU

Et j'ai une autre table [StudentMaster] pour les détails des étudiants COMME

ROLLNO  NAME    ADDRESS          Course
------------------------------------------------
12345   RAM     RAM ADDRESS      01,02,06                      
25695   HARI    HARI ADDRESS     02,06                         
89685   JEFF    JEFF ADDRESS     03,05,06,07                   
47896   DAISY   DAISY ADDRESS    03         

Ici, je veux récupérer les détails de l'étudiant avec CourseName (pas CourseId).

Si les valeurs dans Course ne sont pas séparées par des virgules, il serait très simple query de récupérer les détails avec join.

À ma connaissance, je peux exécuter deux queries pour le même résultat que je veux, une requête pour récupérer les détails de l'étudiant à partir de [StudentMaster] à l'extrémité avant. Et un autre pour récupérer uniquement le CourseName de [CourseMaster] par CourseId correspondant dans une boucle.

Mais le fait que je veux le résultat par un seul query plutôt que d'écrire deux queries pour cette petite tâche.

Je suppose que c'est 100% possible. Et mon résultat attendu ressemblera à:

ROLLNO  NAME    ADDRESS         Course
-------------------------------------------
12345   RAM     RAM ADDRESS     ABC,DEF,PQR                   
25695   HARI    HARI ADDRESS    DEF,PQR                       
89685   JEFF    JEFF ADDRESS    GHI,MNO,PQR,STU               
47896   DAISY   DAISY ADDRESS   GHI                 

Merci et toute suggestion utile sera très appréciée.

4
Tufan Chand

Vous devriez vraiment avoir une table de jonction pour les cours suivis par un étudiant, plutôt que de brouiller les valeurs séparées par des virgules dans un seul tuple. Si vous pensez que c'est le dernier problème que vous aurez à cause de cette conception sous-optimale, vous êtes dans une grande surprise. Vous devriez vraiment demander aux propriétaires de ce projet de lire sur la normalisation - oui, il est douloureux de changer votre schéma, mais il est donc constamment confronté aux limites de le laisser tel quel.

Quoi qu'il en soit, cela dit, vous avez besoin d'une fonction partagée. Étant donné que vos valeurs séparées par des virgules sont numériques, vous pouvez vous en sortir avec une variation de ma fonction XML; il y en a plusieurs autres au choix dans cet article de blog .

CREATE FUNCTION dbo.SplitStrings_XML
(
   @List       VARCHAR(MAX),
   @Delimiter  CHAR(1) = ','
)
RETURNS TABLE
WITH SCHEMABINDING
AS
   RETURN 
   (  
      SELECT Item = y.i.value('(./text())[1]', 'varchar(8000)')
      FROM 
      ( 
        SELECT x = CONVERT(XML, '<i>' 
          + REPLACE(@List, @Delimiter, '</i><i>') 
          + '</i>').query('.')
      ) AS a CROSS APPLY x.nodes('i') AS y(i)
   );

Maintenant, votre requête est:

;WITH x AS 
(
  SELECT s.ROLLNO, s.Name, s.Address, c.CourseId, c.CourseName
  FROM dbo.StudentMaster AS s
  CROSS APPLY dbo.SplitStrings_XML(s.Course, default) AS f
  INNER JOIN dbo.CourseMaster AS c
  ON f.item = c.CourseId
)
SELECT ROLLNO, Name, Address, STUFF((
  SELECT ',' + CourseName FROM x AS x2 
  WHERE x2.ROLLNO = x.ROLLNO
  ORDER BY CourseId FOR XML PATH, 
  TYPE).value(N'.[1]',N'varchar(max)'), 1, 1, '')
FROM x
GROUP BY ROLLNO, Name, Address;

Encore une fois, c'est une solution compliquée, et en raison de votre structure de base de données inférieure, la prochaine requête que vous devrez effectuer sera également alambiquée et encombrante. Il y a une raison pour laquelle ce type de conception est contesté dans presque tous les blogs, essais ou livres sur le sujet ...

6
Aaron Bertrand

Même solution que celle proposée par Aaron Bertrand pour la construction des valeurs séparées par des virgules mais un peu différente pour la connexion de CourseMaster.CourseId avec les valeurs dans StudentMaster.Course.

SQL Fiddle

Configuration du schéma MS SQL Server 2014 :

create table dbo.CourseMaster
(
  CourseId char(2),
  CourseName char(3)
);

create table dbo.StudentMaster
(
  ROLLNO char(5),
  NAME varchar(10),
  ADDRESS varchar(20),
  Course varchar(100)
);

insert into dbo.CourseMaster values
('01', 'ABC'),
('02', 'DEF'),
('03', 'GHI'),
('04', 'JKL'),
('05', 'MNO'),
('06', 'PQR'),
('07', 'STU');

insert into dbo.StudentMaster values
('12345', 'RAM',   'RAM ADDRESS',   '01,02,06'),                      
('25695', 'HARI',  'HARI ADDRESS',  '02,06'),                         
('89685', 'JEFF',  'JEFF ADDRESS',  '03,05,06,07'),                   
('47896', 'DAISY', 'DAISY ADDRESS', '03');

Requête 1 :

select SM.ROLLNO,
       SM.NAME, 
       SM.ADDRESS,
       (
       select ','+CM.CourseName
       from dbo.CourseMaster as CM
       where ','+SM.Course+',' like '%,'+CM.CourseId+',%'
       for xml path(''), type
       ).value('substring(text()[1], 2)', 'varchar(max)') as Course
from dbo.StudentMaster as SM;

Résultats:

| ROLLNO |  NAME |       ADDRESS |          Course |
|--------|-------|---------------|-----------------|
|  12345 |   RAM |   RAM ADDRESS |     ABC,DEF,PQR |
|  25695 |  HARI |  HARI ADDRESS |         DEF,PQR |
|  89685 |  JEFF |  JEFF ADDRESS | GHI,MNO,PQR,STU |
|  47896 | DAISY | DAISY ADDRESS |             GHI |
4
Mikael Eriksson

Un moyen simple pour obtenir les valeurs de la liste à partir de la table StudentMaster (et vous pouvez vous joindre aux résultats) serait de (à l'aide d'une chaîne fractionnée comme mentionné dans les réponses précédentes, et en supposant que la fonction renvoie une colonne appelée pour chaque élément de la liste):

SELECT ROLLNO, NAME, ADDRESS, item
FROM StudentMaster
CROSS APPLY fSplitString(Course);
0
keb
select D.DATE,D.DESCP,
   (
   select ','+S.NAME
   from tb_schems as S
   where ',' + D.SCHME + ',' like '%,' + cast(S.id as nvarchar(20)) + ',%'
   for xml path(''), type
   ).value('substring(text()[1], 2)', 'varchar(max)') as SCHEME
from TB_DETAIL as D;
0
user158308