質問

3行の名前を持つデータベーステーブルを検討します。

Peter
Paul
Mary

これを Peter、Paul、Mary の単一の文字列に変換する簡単な方法はありますか?

役に立ちましたか?

解決

SQL Server 2017またはAzureを使用している場合は、 Mathieu Rendaの回答をご覧ください。

1対多の関係で2つのテーブルを結合しようとしたときに、同様の問題が発生しました。 SQL 2005では、 XML PATH メソッドが行の連結を非常に簡単に処理できることがわかりました。

STUDENTS

というテーブルがある場合
SubjectID       StudentName
----------      -------------
1               Mary
1               John
1               Sam
2               Alaina
2               Edward

期待していた結果:

SubjectID       StudentName
----------      -------------
1               Mary, John, Sam
2               Alaina, Edward

次の T-SQL を使用しました:

SELECT Main.SubjectID,
       LEFT(Main.Students,Len(Main.Students)-1) As "Students"
FROM
    (
        SELECT DISTINCT ST2.SubjectID, 
            (
                SELECT ST1.StudentName + ',' AS [text()]
                FROM dbo.Students ST1
                WHERE ST1.SubjectID = ST2.SubjectID
                ORDER BY ST1.SubjectID
                FOR XML PATH ('')
            ) [Students]
        FROM dbo.Students ST2
    ) [Main]

最初にコンマを連結し、 substring を使用して最初のコンマをスキップできる場合、サブクエリを実行する必要がないため、同じことをよりコンパクトに行うことができます:

SELECT DISTINCT ST2.SubjectID, 
    SUBSTRING(
        (
            SELECT ','+ST1.StudentName  AS [text()]
            FROM dbo.Students ST1
            WHERE ST1.SubjectID = ST2.SubjectID
            ORDER BY ST1.SubjectID
            FOR XML PATH ('')
        ), 2, 1000) [Students]
FROM dbo.Students ST2

他のヒント

  

この回答は、予期しない結果を返す場合があります一貫した結果を得るには、他の回答で詳しく説明されているFOR XML PATHメソッドのいずれかを使用してください。

COALESCE を使用:

DECLARE @Names VARCHAR(8000) 
SELECT @Names = COALESCE(@Names + ', ', '') + Name 
FROM People

少し説明をしてください(この回答は比較的規則的な見方をしているようです):

  • 合体は、実際には2つのことを達成する有用なチートです:

1)空の文字列値で @Names を初期化する必要はありません。

2)最後に余分なセパレータを取り除く必要はありません。

  • 上記のソリューションは、行に NULL Name値がある場合、誤った結果を返します( NULL がある場合、 NULL @Names NULL はその行の後、次の行は空の文字列として最初からやり直します。次の2つの解決策のいずれかで簡単に修正できます。
DECLARE @Names VARCHAR(8000) 
SELECT @Names = COALESCE(@Names + ', ', '') + Name
FROM People
WHERE Name IS NOT NULL

または:

DECLARE @Names VARCHAR(8000) 
SELECT @Names = COALESCE(@Names + ', ', '') + 
    ISNULL(Name, 'N/A')
FROM People

必要な動作に応じて(最初のオプションは NULL を除外するだけで、2番目のオプションはそれらをマーカーメッセージと共にリストに保持します['N / A'を適切なものに置き換えますあなた])。

MS SQL Serverの XML data()コマンドでまだ表示されていないメソッドの1つは次のとおりです。

FNameという1つの列を持つNameListというテーブルを想定します

SELECT FName + ', ' AS 'data()' 
FROM NameList 
FOR XML PATH('')

戻り値:

"Peter, Paul, Mary, "

余分なコンマのみを処理する必要があります。

編集: @NReilinghのコメントから採用されているように、次の方法を使用して末尾のコンマを削除できます。同じテーブル名と列名を想定:

STUFF(REPLACE((SELECT '#!' + LTRIM(RTRIM(FName)) AS 'data()' FROM NameList
FOR XML PATH('')),' #!',', '), 1, 2, '') as Brands

SQL Server 2017+およびSQL Azure:STRING_AGG

次のバージョンのSQL Serverから始めて、変数やXMLの魔術に頼ることなく、最終的に行を連結できます。

STRING_AGG(Transact-SQL)

グループ化なし

SELECT STRING_AGG(Name, ', ') AS Departments
FROM HumanResources.Department;

グループ化:

SELECT GroupName, STRING_AGG(Name, ', ') AS Departments
FROM HumanResources.Department
GROUP BY GroupName;

グループ化とサブソート付き

SELECT GroupName, STRING_AGG(Name, ', ') WITHIN GROUP (ORDER BY Name ASC) AS Departments
FROM HumanResources.Department 
GROUP BY GroupName;

SQL Server 2005

SELECT Stuff(
  (SELECT N', ' + Name FROM Names FOR XML PATH(''),TYPE)
  .value('text()[1]','nvarchar(max)'),1,2,N'')

SQL Server 2016の場合

を使用できますFOR JSON構文

i.e。

SELECT per.ID,
Emails = JSON_VALUE(
   REPLACE(
     (SELECT _ = em.Email FROM Email em WHERE em.Person = per.ID FOR JSON PATH)
    ,'"},{"_":"',', '),'$[0]._'
) 
FROM Person per

そして結果は次のようになります

Id  Emails
1   abc@gmail.com
2   NULL
3   def@gmail.com, xyz@gmail.com

これには、データに無効なXML文字が含まれていても機能します

the '"}、{" _":"' は、データに '"}、{" _":"'が含まれている場合、 "}、{\" _ \":\"

にエスケープされます

'、' は任意の文字列区切り文字で置き換えることができます


およびSQL Server 2017では、Azure SQLデータベース

新しい STRING_AGGを使用できます関数

MySQLには、 GROUP_CONCAT()。複数の行の値を連結できます。例:

SELECT 1 AS a, GROUP_CONCAT(name ORDER BY name ASC SEPARATOR ', ') AS people 
FROM users 
WHERE id IN (1,2,3) 
GROUP BY a

COALESCE を使用-こちらから詳細をご覧ください

例:

  

102

     

103

     

104

次に、SQLサーバーで以下のコードを記述します

Declare @Numbers AS Nvarchar(MAX) -- It must not be MAX if you have few numbers 
SELECT  @Numbers = COALESCE(@Numbers + ',', '') + Number
FROM   TableName where Number IS NOT NULL

SELECT @Numbers

出力は次のようになります:

102,103,104

Postgres配列は素晴らしいです。例:

テストデータを作成します:

postgres=# \c test
You are now connected to database "test" as user "hgimenez".
test=# create table names (name text);
CREATE TABLE                                      
test=# insert into names (name) values ('Peter'), ('Paul'), ('Mary');                                                          
INSERT 0 3
test=# select * from names;
 name  
-------
 Peter
 Paul
 Mary
(3 rows)

それらを配列に集約します:

test=# select array_agg(name) from names;
 array_agg     
------------------- 
 {Peter,Paul,Mary}
(1 row)

配列をコンマ区切りの文字列に変換します:

test=# select array_to_string(array_agg(name), ', ') from names;
 array_to_string
-------------------
 Peter, Paul, Mary
(1 row)

完了

PostgreSQL 9.0以降では、さらに簡単です。

Oracle 11gリリース2は、LISTAGG関数をサポートしています。ドキュメントこちら

COLUMN employees FORMAT A50

SELECT deptno, LISTAGG(ename, ',') WITHIN GROUP (ORDER BY ename) AS employees
FROM   emp
GROUP BY deptno;

    DEPTNO EMPLOYEES
---------- --------------------------------------------------
        10 CLARK,KING,MILLER
        20 ADAMS,FORD,JONES,SCOTT,SMITH
        30 ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD

3 rows selected.

警告

結果の文字列が4000文字を超える可能性がある場合は、この関数の実装に注意してください。例外をスローします。その場合は、例外を処理するか、結合文字列が4000文字を超えないようにする独自の関数をロールする必要があります。

SQL Server 2005以降では、以下のクエリを使用して行を連結します。

DECLARE @t table
(
    Id int,
    Name varchar(10)
)
INSERT INTO @t
SELECT 1,'a' UNION ALL
SELECT 1,'b' UNION ALL
SELECT 2,'c' UNION ALL
SELECT 2,'d' 

SELECT ID,
stuff(
(
    SELECT ','+ [Name] FROM @t WHERE Id = t.Id FOR XML PATH('')
),1,1,'') 
FROM (SELECT DISTINCT ID FROM @t ) t

自宅ではSQL Serverにアクセスできないため、ここの構文は推測しますが、多かれ少なかれです:

DECLARE @names VARCHAR(500)

SELECT @names = @names + ' ' + Name
FROM Names

再帰的なCTEソリューションが提案されましたが、コードは提供されていません。以下のコードは、再帰CTEの例です。結果は質問と一致しますが、データは指定された説明と非常に一致しないことに注意してください。テーブル内のすべての行ではなく、行のグループ。テーブル内のすべての行に一致するように変更することは、読者の課題として残されています。

;with basetable as 
(   SELECT id, CAST(name as varchar(max))name, 
        ROW_NUMBER() OVER(Partition By id     order by seq) rw, 
        COUNT(*) OVER (Partition By id) recs 
FROM (VALUES (1, 'Johnny', 1), (1,'M', 2), 
                  (2,'Bill', 1), (2, 'S.', 4), (2, 'Preston', 5), (2, 'Esq.', 6),
        (3, 'Ted', 1), (3,'Theodore', 2), (3,'Logan', 3),
                  (4, 'Peter', 1), (4,'Paul', 2), (4,'Mary', 3)

           )g(id, name, seq)
),
rCTE as (
    SELECT recs, id, name, rw from basetable where rw=1
    UNION ALL
    SELECT b.recs, r.ID, r.name +', '+ b.name name, r.rw+1
    FROM basetable b
         inner join rCTE r
    on b.id = r.id and b.rw = r.rw+1
)
SELECT name FROM rCTE
WHERE recs = rw and ID=4

最終結果を保持する変数を作成し、選択する必要があります。

最も簡単な解決策

DECLARE @char VARCHAR(MAX);

SELECT @char = COALESCE(@char + ', ' + [column], [column]) 
FROM [table];

PRINT @char;

PostgreSQL 9.0以降、これは非常に簡単です:

select string_agg(name, ',') 
from names;

9.0より前のバージョンでは、hgmnzが示すように array_agg()を使用できます

SQL Server vNextでは、これはSTRING_AGG関数を使用して組み込まれます。詳細については、以下を参照してください。 https://msdn.microsoft.com/en-us/library/mt790580.aspx

XMLを使用すると、行をコンマで区切ることができました。追加のコンマには、SQL  Serverのreplace関数を使用できます。コンマを追加する代わりに、AS 'data()'を使用すると、行がスペースで連結されます。これは、後で記述されている構文のようにコンマで置き換えることができます。

REPLACE(
        (select FName AS 'data()'  from NameList  for xml path(''))
         , ' ', ', ') 

追加のコンマを使用しない、すぐに使用できるソリューション:

select substring(
        (select ', '+Name AS 'data()' from Names for xml path(''))
       ,3, 255) as "MyList"

空のリストはNULL値になります。 通常は、テーブルの列またはプログラム変数にリストを挿入します。必要に応じて最大長を255に調整します。

(DiwakarとJens Frandsenは良い答えを提供しましたが、改善が必要です。)

SELECT STUFF((SELECT ', ' + name FROM [table] FOR XML PATH('')), 1, 2, '')

サンプルは次のとおりです。

DECLARE @t TABLE (name VARCHAR(10))
INSERT INTO @t VALUES ('Peter'), ('Paul'), ('Mary')
SELECT STUFF((SELECT ', ' + name FROM @t FOR XML PATH('')), 1, 2, '')
--Peter, Paul, Mary
DECLARE @Names VARCHAR(8000)
SELECT @name = ''
SELECT @Names = @Names + ',' + Names FROM People
SELECT SUBSTRING(2, @Names, 7998)

これにより、浮遊コンマが先頭に配置されます。

ただし、他の列が必要な場合、または子テーブルをCSVにするには、これをスカラーユーザー定義フィールド(UDF)でラップする必要があります。

XMLパスをSELECT句の相関サブクエリとして使用することもできます(ただし、Googleは自宅で仕事をしていないため、仕事に戻るまで待つ必要があります:-)

他の回答では、回答を読む人は、車両や学生などの特定のドメインテーブルを知っている必要があります。ソリューションをテストするには、テーブルを作成してデータを入力する必要があります。

以下は、SQL Server" Information_Schema.Columns"を使用する例です。表。このソリューションを使用すると、テーブルを作成したりデータを追加したりする必要がありません。この例では、データベース内のすべてのテーブルの列名のコンマ区切りリストを作成します。

SELECT
    Table_Name
    ,STUFF((
        SELECT ',' + Column_Name
        FROM INFORMATION_SCHEMA.Columns Columns
        WHERE Tables.Table_Name = Columns.Table_Name
        ORDER BY Column_Name
        FOR XML PATH ('')), 1, 1, ''
    )Columns
FROM INFORMATION_SCHEMA.Columns Tables
GROUP BY TABLE_NAME 

Oracle DBについては、次の質問を参照してください:ストアドプロシージャを作成せずにOracleで複数の行を1つに連結するにはどうすればよいですか?

最良の答えは、Oracle 11gリリース2以降で利用可能な組み込みLISTAGG()関数を使用した@Emmanuelによるものと思われます。

SELECT question_id,
   LISTAGG(element_id, ',') WITHIN GROUP (ORDER BY element_id)
FROM YOUR_TABLE;
GROUP BY question_id

@ user762952が指摘し、Oracleのドキュメントによると、 http://www.oracle-base.com/articles/misc/string-aggregation-techniques.php では、WM_CONCAT()関数もオプションです。安定しているように見えますが、アプリケーションSQLには使用しないことを明示的に推奨しているため、自己責任で使用してください。

それ以外は、独自の関数を作成する必要があります。上記のOracleドキュメントには、その方法に関するガイドがあります。

Danaの回答の優雅さが本当に好きでした。完成させたかっただけです。

DECLARE @names VARCHAR(MAX)
SET @names = ''

SELECT @names = @names + ', ' + Name FROM Names 

-- Deleting last two symbols (', ')
SET @sSql = LEFT(@sSql, LEN(@sSql) - 1)

null値を回避するには、CONCAT()を使用できます

DECLARE @names VARCHAR(500)
SELECT @names = CONCAT(@names, ' ', name) 
FROM Names
select @names

この回答を行うには、サーバーでの何らかの権限が必要です。

アセンブリは最適なオプションです。作成方法を説明するサイトがたくさんあります。非常によく説明されていると思うのは、この one

必要に応じて、既にアセンブリを作成しており、DLL こちら

ダウンロードしたら、SQL Serverで次のスクリプトを実行する必要があります。

CREATE Assembly concat_assembly 
   AUTHORIZATION dbo 
   FROM '<PATH TO Concat.dll IN SERVER>' 
   WITH PERMISSION_SET = SAFE; 
GO 

CREATE AGGREGATE dbo.concat ( 

    @Value NVARCHAR(MAX) 
  , @Delimiter NVARCHAR(4000) 

) RETURNS NVARCHAR(MAX) 
EXTERNAL Name concat_assembly.[Concat.Concat]; 
GO  

sp_configure 'clr enabled', 1;
RECONFIGURE

アセンブリへのパスにサーバーからアクセスできる可能性があることに注意してください。すべてのステップを正常に完了したため、次のような関数を使用できます。

SELECT dbo.Concat(field1, ',')
FROM Table1

願っています!!!

通常、このようなselectを使用して、SQL Serverで文字列を連結します。

with lines as 
( 
  select 
    row_number() over(order by id) id, -- id is a line id
    line -- line of text.
  from
    source -- line source
), 
result_lines as 
( 
  select 
    id, 
    cast(line as nvarchar(max)) line 
  from 
    lines 
  where 
    id = 1 
  union all 
  select 
    l.id, 
    cast(r.line + N', ' + l.line as nvarchar(max))
  from 
    lines l 
    inner join 
    result_lines r 
    on 
      l.id = r.id + 1 
) 
select top 1 
  line
from
  result_lines
order by
  id desc

nullを処理する場合は、where句を追加するか、最初の句の周りに別のCOALESCEを追加することにより、nullを処理できます。

DECLARE @Names VARCHAR(8000) 
SELECT @Names = COALESCE(COALESCE(@Names + ', ', '') + Name, @Names) FROM People

MySQLの完全な例:

多数のデータを持つことができるユーザーがおり、リストにすべてのユーザーデータを表示できる出力が必要です:

結果:

___________________________
| id   |  rowList         |
|-------------------------|
| 0    | 6, 9             |
| 1    | 1,2,3,4,5,7,8,1  |
|_________________________|

テーブルのセットアップ:

CREATE TABLE `Data` (
  `id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1;


INSERT INTO `Data` (`id`, `user_id`) VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 1),
(5, 1),
(6, 0),
(7, 1),
(8, 1),
(9, 0),
(10, 1);


CREATE TABLE `User` (
  `id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;


INSERT INTO `User` (`id`) VALUES
(0),
(1);

クエリ:

SELECT User.id, GROUP_CONCAT(Data.id ORDER BY Data.id) AS rowList FROM User LEFT JOIN Data ON User.id = Data.user_id GROUP BY User.id

Oracleでは、 wm_concat です。この機能は、 10gリリース以降で利用できると思います。

これも便利です

create table #test (id int,name varchar(10))
--use separate inserts on older versions of SQL Server
insert into #test values (1,'Peter'), (1,'Paul'), (1,'Mary'), (2,'Alex'), (3,'Jack')

DECLARE @t VARCHAR(255)
SELECT @t = ISNULL(@t + ',' + name, name) FROM #test WHERE id = 1
select @t
drop table #test

返品

Peter,Paul,Mary

この方法は、NPATH関数を使用するTeradata Asterデータベースにのみ適用されます。

繰り返しますが、学生テーブルがあります

SubjectID       StudentName
----------      -------------
1               Mary
1               John
1               Sam
2               Alaina
2               Edward

NPATHを使用すると、単一のSELECTになります。

SELECT * FROM npath(
  ON Students
  PARTITION BY SubjectID
  ORDER BY StudentName
  MODE(nonoverlapping)
  PATTERN('A*')
  SYMBOLS(
    'true' as A
  )
  RESULT(
    FIRST(SubjectID of A) as SubjectID,
    ACCUMULATE(StudentName of A) as StudentName
  )
);

結果:

SubjectID       StudentName
----------      -------------
1               [John, Mary, Sam]
2               [Alaina, Edward]
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top