3つの行を持つ名前を保持するデータベーステーブルを考えます。
Peter
Paul
Mary
これを単一の文字列に変換する簡単な方法はありPeter, Paul, Mary
ますか?
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
<
or などのXML文字が含まれている場合、これは機能しないよう&
です。@BenHinmanのコメントを参照してください。
FOR XML PATH ('')
。つまり、パッチやアップデートによってこの機能が変更される可能性があるため、信頼性があると見なすべきではありません。それは基本的に非推奨の機能に依存しています。
FOR XML
任意の文字列を連結するのではなく、XMLを生成することです。それはエスケープだからこそ &
、<
および>
(XMLエンティティコードに&
、<
、>
)。私はそれがまた脱出すると仮定"
して'
まで"
と'
同様の属性に。それはだではない GROUP_CONCAT()
、string_agg()
、array_agg()
、listagg()
あなたは一種のmake、それはそれを行うことができる場合であっても、など。我々はすべきであるマイクロソフトが適切な機能を実装厳しい私たちの時間を過ごすこと。
string_agg
v.Next に追加される予定です。そして、これらすべてがなくなる可能性があります。
この回答は予期しない結果を返す可能性があります一貫した結果を得るには、他の回答で詳述されているFOR XML PATHメソッドの1つを使用してください。
使用COALESCE
:
DECLARE @Names VARCHAR(8000)
SELECT @Names = COALESCE(@Names + ', ', '') + Name
FROM People
ほんの一部の説明(この回答は比較的規則的な見解を持っているように見えるため):
1)@Names
空の文字列値で初期化する必要はありません。
2)最後に余分なセパレーターを取り除く必要はありません。
@Names
NULLをその行の後、次の行が再び空の文字列としてオーバーを開始します。簡単に二つのうちの一つで固定しますソリューション: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'を適切なものに置き換えます))。
SQL Serverの次のバージョンから、変数やXMLウィッチャリに頼ることなく、最終的に行全体を連結できます。
グループ化なし
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;
XML
data()
MS SQL Serverのコマンドでまだ表示されていない方法の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
+ ', '
連結された各要素の間にスペースが1つ追加されることに気づきました。
SELECT STUFF(REPLACE((SELECT '#!'+city AS 'data()' FROM #cityzip FOR XML PATH ('')),' #!',', '),1,2,'')
SELECT Stuff(
(SELECT N', ' + Name FROM Names FOR XML PATH(''),TYPE)
.value('text()[1]','nvarchar(max)'),1,2,N'')
FOR JSON構文を使用できます
すなわち
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文字が含まれている場合でも機能します
'"},{"_":"'
データが含まれ'"},{"_":"',
ている場合はエスケープされるため、これは安全です"},{\"_\":\"
', '
任意の文字列セパレーターで置き換えることができます
新しいSTRING_AGG関数を使用できます
<
、>
、&
、などこれはFOR XML PATH('')
自動的にエスケープしますが。
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
SEPARATOR '", "'
と、最後のエントリの最後にいくつかの文字が表示されなくなります。なぜこれが起こるのですか?
CHAR
、あなたはそれをキャストする必要があり、例えば経てGROUP_CONCAT( CAST(id AS CHAR(8)) ORDER BY id ASC SEPARATOR ',')
2)あなたは多くの値が来ている場合、あなたは増加するはずgroup_concat_max_len
で書かれたようstackoverflow.com/a/1278210/1498405
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
Declare @Numbers AS Nvarchar(MAX)
、それはうまくいきました。使用しないことをお勧めする理由を教えてください。
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以降はさらに簡単です。
select array_to_string(array_agg(name||'('||id||')'
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文字を超えないようにする独自の関数をロールする必要があります。
LISTAGG
完璧に動作します!ここにリンクされているドキュメントを読んでください。wm_concat
バージョン12c以降は削除されました。
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
SELECT @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ' ' END + Name FROM Names
SELECT @names = @names + ISNULL(' ' + Name, '')
再帰的な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
name
列をsの4つのグループのコンマ区切り文字列にフラット化しますid
。一見すると、これはSQL Serverの他のほとんどのソリューションが行うよりも多くの作業だと思います。
PostgreSQL 9.0以降、これは非常に簡単です。
select string_agg(name, ',')
from names;
9.0より前のバージョンではarray_agg()
、hgmnzで示されているように使用できます。
SELECT string_agg(non_text_type::text, ',') FROM table
varchar
または、char
XMLを使用すると、行をコンマで区切るのに役立ちました。余分なコンマについては、SQL Serverの置換機能を使用できます。カンマを追加する代わりに、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)でラップする必要があります。
SELECT句で相関サブクエリとしてXMLパスを使用することもできます(ただし、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の場合は、次の質問を参照してください。 、ストアドプロシージャを作成せずに、複数の行を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()関数もオプションです。安定しているように見えますが、OracleはアプリケーションSQLに使用しないことを明確に推奨しているため、自己責任で使用してください。
それ以外に、独自の関数を作成する必要があります。上記のOracleドキュメントには、その方法に関するガイドがあります。
null値を回避するには、CONCAT()を使用できます
DECLARE @names VARCHAR(500)
SELECT @names = CONCAT(@names, ' ', name)
FROM Names
select @names
ダナの答えの優雅さが本当に好きだった。それを完全にしたかっただけです。
DECLARE @names VARCHAR(MAX)
SET @names = ''
SELECT @names = @names + ', ' + Name FROM Names
-- Deleting last two symbols (', ')
SET @sSql = LEFT(@sSql, LEN(@sSql) - 1)
SELECT @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ', ' END + Name FROM Names
後で切り捨てる必要がないようにします。
この回答を機能させるには、サーバーで何らかの特権が必要です。
アセンブリはあなたにとって良いオプションです。それを作成する方法を説明する多くのサイトがあります。私は非常によく説明されていると思います一つはこれです1
必要に応じて、私はすでにアセンブリを作成しています。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
それが役に立てば幸い!!!
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
私は通常、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
これは私のために働きました(SqlServer 2016):
SELECT CarNamesString = STUFF((
SELECT ',' + [Name]
FROM tbl_cars
FOR XML PATH('')
), 1, 1, '')
これがソースです:https : //www.mytecbits.com/
MySqlのソリューション(このページはGoogleでMySqlに表示されるため)
SELECT [Name],
GROUP_CONCAT(DISTINCT [Name] SEPARATOR ',')
FROM tbl_cars
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