web-dev-qa-db-ja.com

SQL Serverでカンマ区切り値で結合する

テーブルがあります_[CourseMaster]_ LIKE

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

そして、私は別のテーブル_[StudentMaster]_を学生の詳細LIKEに持っています

_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         
_

ここでは、CourseName(Not CourseId)を使用して生徒の詳細を取得します。

Courseの値がコンマで区切られていない場合、結合で詳細をフェッチするのは非常に単純なqueryになります。

私の知る限りでは、2つのqueriesを実行して同じ結果を得ることができます。学生の詳細を_[StudentMaster]_からフロントエンドにフェッチするための1つのクエリ。そして、ループを通じてCourseNameを対応させることにより、_[CourseMaster]_からCourseIdのみをフェッチするための他の1つ。

しかし、この小さなタスクで2つのqueryを書くのではなく、1つのqueriesだけで結果を取得したいという事実。

100%可能だと思います。そして、私の期待される結果は次のようになります:

_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
Tufan Chand

カンマで区切られた値を1つのタプルに詰め込むのではなく、学生が受講するコースのジャンクションテーブルが必要です。この次善の設計のためにこれがあなたが持つ最後の問題だと思うなら、あなたは大きな驚きに直面しています。あなたは本当にこのプロジェクトの所有者に正規化について読んでもらう必要があります-はい、スキーマを変更するのは苦痛ですが、それをそのままにしておくことの制限に常に対処しています。

とにかく、それが言われて、あなたは分割関数を必要とします。コンマで区切られた値は数値であるため、XML関数のバリエーションを使用して回避できます。他にもいくつかあります このブログの投稿では

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)
   );

今、あなたのクエリは:

;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;

繰り返しますが、これは複雑なソリューションであり、データベース構造が劣っているため、次に実行する必要のあるクエリも同様に複雑で面倒です。このタイプのデザインがトピックについてのほぼすべてのブログ、エッセイ、または本で反対されている理由があります...

6
Aaron Bertrand

Aaron Bertrandがコンマ区切りの値を作成するときに提供するものと同じソリューションですが、CourseMaster.CourseIdStudentMaster.Courseの値を接続する方法が少し異なります。

SQLフィドル

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');

クエリ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;

結果

| 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

StudentMasterテーブルからリスト値を取得する簡単な方法(および結果に結合することができます)は、(前述のように分割文字列を使用して、関数が呼び出された列を返すと仮定して)各リスト項目の項目):

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