3行の名前を持つデータベーステーブルを考えます。
Peter
Paul
Mary
これをPeter, Paul, Mary
の単一の文字列に変換する簡単な方法はありますか?
SQL Server 2017またはAzureをお使いの場合は、 Mathieu Renda answer を参照してください。
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
ちょっと説明してください(この回答は比較的定期的な見解を得るように思われるので):
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 sを除外し、2番目のオプションはそれらをマーカーメッセージでリストに残します。 。
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からは、変数や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;
SELECT Stuff(
(SELECT N', ' + Name FROM Names FOR XML PATH(''),TYPE)
.value('text()[1]','nvarchar(max)'),1,2,N'')
すなわち.
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 [email protected]
2 NULL
3 [email protected], [email protected]
データに無効なXML文字が含まれていてもこれは機能します。
データに'"},{"_":"'
が含まれている場合は'"},{"_":"',
にエスケープされるため、"},{\"_\":\"
は安全です。
', '
は任意の文字列の区切り文字に置き換えることができます
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 Serverで以下のコードを書く。
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 Release 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
最終結果を保持して選択する変数を作成する必要があります。
DECLARE @char VARCHAR(MAX);
SELECT @char = COALESCE(@char + ', ' + [column], [column])
FROM [table];
PRINT @char;
再帰的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
PostgreSQL 9.0以降では、これは非常に簡単です。
select string_agg(name, ',')
from names;
Hgmnzで示されるように9.0より前のバージョンではarray_agg()
を使うことができます
SQL Server vNextでは、これはSTRING_AGG関数を使用して構築されます。詳細については、こちらを参照してください。 https://msdn.Microsoft.com/ja-jp/library/mt790580.aspx
追加のコンマなしですぐに使えるソリューション:
select substring(
(select ', '+Name AS 'data()' from Names for xml path(''))
,3, 255) as "MyList"
空のリストはNULL値になります。通常は、リストをテーブルの列またはプログラム変数に挿入します。必要に応じて最大長の255を調整します。
(DiwakarとJens Frandsenは良い答えを出しましたが、改善が必要です。)
XMLを使用すると、行をコンマで区切ることができました。余分なカンマのために私達はSQL Serverのreplace機能を使うことができます。コンマを追加する代わりに、AS 'data()'を使用すると、行がスペースで連結されます。スペースは、後で説明する構文のように後でコンマに置き換えることができます。
REPLACE(
(select FName AS 'data()' from NameList for xml path(''))
, ' ', ', ')
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 Release 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's answer の優雅さがとても気に入りました。それを完成させたいだけでした。
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
Assemblyへのパスがサーバーからアクセス可能である可能性があることに注意してください。これですべてのステップが正常に完了したので、次のような機能を使用することができます。
SELECT dbo.Concat(field1, ',')
FROM Table1
それが役に立てば幸い!!!
私は通常SQL Serverで文字列を連結するのにこのようにselectを使います:
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
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
Nullを扱う場合は、where句を追加するか、最初の句の周囲に別のCOALESCEを追加することで実現できます。
DECLARE @Names VARCHAR(8000)
SELECT @Names = COALESCE(COALESCE(@Names + ', ', '') + Name, @Names) FROM People
これも役に立ちます
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
Oracleでは、wm_concat
です。この機能は 10gリリース 以降で利用可能だと思います。
このメソッドは、そのNPATH機能を利用する場合に限り、Teradata Asterデータベースに適用されます。
また、テーブルStudentsがあります。
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]
私のリストは10項目に満たなかったのでパフォーマンスの分析をしたわけではありませんが、30個の奇妙な答えを見て驚きました。私の変数を設定する必要さえありません(とにかくデフォルトはNULLです)、そしてそれは私のソースデータテーブルの全てのエントリが空白でないと仮定します:
DECLARE @MyList VARCHAR(1000), @Delimiter CHAR(2) = ', '
SELECT @MyList = CASE WHEN @MyList > '' THEN @MyList + @Delimiter ELSE '' END + FieldToConcatenate FROM MyData
私はCOALESCEが内部的に同じ考えを使っていると確信しています。 MSがこれを変更しないようにしてください。
これを達成するための完全な解決策は次のとおりです。
-- Table Creation
CREATE TABLE Tbl
( CustomerCode VARCHAR(50)
, CustomerName VARCHAR(50)
, Type VARCHAR(50)
,Items VARCHAR(50)
)
insert into Tbl
SELECT 'C0001','Thomas','BREAKFAST','Milk'
union SELECT 'C0001','Thomas','BREAKFAST','Bread'
union SELECT 'C0001','Thomas','BREAKFAST','Egg'
union SELECT 'C0001','Thomas','LUNCH','Rice'
union SELECT 'C0001','Thomas','LUNCH','Fish Curry'
union SELECT 'C0001','Thomas','LUNCH','Lessy'
union SELECT 'C0002','JOSEPH','BREAKFAST','Bread'
union SELECT 'C0002','JOSEPH','BREAKFAST','Jam'
union SELECT 'C0002','JOSEPH','BREAKFAST','Tea'
union SELECT 'C0002','JOSEPH','Supper','Tea'
union SELECT 'C0002','JOSEPH','Brunch','Roti'
-- function creation
GO
CREATE FUNCTION [dbo].[fn_GetItemsByType]
(
@CustomerCode VARCHAR(50)
,@Type VARCHAR(50)
)
RETURNS @ItemType TABLE ( Items VARCHAR(5000) )
AS
BEGIN
INSERT INTO @ItemType(Items)
SELECT STUFF((SELECT distinct ',' + [Items]
FROM Tbl
WHERE CustomerCode = @CustomerCode
AND Type=@Type
FOR XML PATH(''))
,1,1,'') as Items
RETURN
END
GO
-- fianl Query
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)
select @cols = STUFF((SELECT distinct ',' + QUOTENAME(Type)
from Tbl
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query = 'SELECT CustomerCode,CustomerName,' + @cols + '
from
(
select
distinct CustomerCode
,CustomerName
,Type
,F.Items
FROM Tbl T
CROSS APPLY [fn_GetItemsByType] (T.CustomerCode,T.Type) F
) x
pivot
(
max(Items)
for Type in (' + @cols + ')
) p '
execute(@query)
- SQL Server 2005以降
CREATE TABLE dbo.Students
(
StudentId INT
, Name VARCHAR(50)
, CONSTRAINT PK_Students PRIMARY KEY (StudentId)
);
CREATE TABLE dbo.Subjects
(
SubjectId INT
, Name VARCHAR(50)
, CONSTRAINT PK_Subjects PRIMARY KEY (SubjectId)
);
CREATE TABLE dbo.Schedules
(
StudentId INT
, SubjectId INT
, CONSTRAINT PK__Schedule PRIMARY KEY (StudentId, SubjectId)
, CONSTRAINT FK_Schedule_Students FOREIGN KEY (StudentId) REFERENCES dbo.Students (StudentId)
, CONSTRAINT FK_Schedule_Subjects FOREIGN KEY (SubjectId) REFERENCES dbo.Subjects (SubjectId)
);
INSERT dbo.Students (StudentId, Name) VALUES
(1, 'Mary')
, (2, 'John')
, (3, 'Sam')
, (4, 'Alaina')
, (5, 'Edward')
;
INSERT dbo.Subjects (SubjectId, Name) VALUES
(1, 'Physics')
, (2, 'Geography')
, (3, 'French')
, (4, 'Gymnastics')
;
INSERT dbo.Schedules (StudentId, SubjectId) VALUES
(1, 1) --Mary, Physics
, (2, 1) --John, Physics
, (3, 1) --Sam, Physics
, (4, 2) --Alaina, Geography
, (5, 2) --Edward, Geography
;
SELECT
sub.SubjectId
, sub.Name AS [SubjectName]
, ISNULL( x.Students, '') AS Students
FROM
dbo.Subjects sub
OUTER APPLY
(
SELECT
CASE ROW_NUMBER() OVER (ORDER BY stu.Name) WHEN 1 THEN '' ELSE ', ' END
+ stu.Name
FROM
dbo.Students stu
INNER JOIN dbo.Schedules sch
ON stu.StudentId = sch.StudentId
WHERE
sch.SubjectId = sub.SubjectId
ORDER BY
stu.Name
FOR XML PATH('')
) x (Students)
;
これはどう:
ISNULL(SUBSTRING(REPLACE((select ',' FName as 'data()' from NameList for xml path('')), ' ,',', '), 2, 300), '') 'MyList'
「300」が表示されると思われるアイテムの最大数を考慮して任意の幅にすることができます。
SQL Serverでこれを実行する1つの方法は、テーブルのコンテンツをXML(XML rawの場合)として返し、結果を文字列に変換してからタグを "、"に置き換えることです。
@ User1460901このようなことを試すことができます。
WITH cte_base AS (
SELECT CustomerCode, CustomerName,
CASE WHEN Typez = 'Breakfast' THEN Items ELSE NULL END AS 'BREAKFAST'
, CASE WHEN Typez = 'Lunch' THEN Items ELSE NULL END AS 'LUNCH'
FROM #Customer
)
SELECT distinct CustomerCode, CustomerName,
SUBSTRING(
(
SELECT ','+BREAKFAST AS [text()]
FROM cte_base b1
WHERE b1.CustomerCode = b2.CustomerCode AND b1.CustomerName = b2.CustomerName
ORDER BY b1.BREAKFAST
FOR XML PATH('')
), 2, 1000
) [BREAKFAST],
SUBSTRING(
(
SELECT ','+LUNCH AS [text()]
FROM cte_base b1
WHERE b1.CustomerCode = b2.CustomerCode AND b1.CustomerName = b2.CustomerName
ORDER BY b1.LUNCH
FOR XML PATH('')
), 2, 1000
) [LUNCH]
FROM cte_base b2
SELECT PageContent = Stuff(
( SELECT PageContent
FROM dbo.InfoGuide
WHERE CategoryId = @CategoryId
AND SubCategoryId = @SubCategoryId
for xml path(''), type
).value('.[1]','nvarchar(max)'),
1, 1, '')
FROM dbo.InfoGuide info
テーブル定義
CREATE TABLE "NAMES" ("NAME" VARCHAR2(10 BYTE))) ;
この表に値を挿入しましょう
INSERT INTO NAMES VALUES('PETER');
INSERT INTO NAMES VALUES('PAUL');
INSERT INTO NAMES VALUES('MARY');
手続きはここから始まります
DECLARE
MAXNUM INTEGER;
CNTR INTEGER := 1;
C_NAME NAMES.NAME%TYPE;
NSTR VARCHAR2(50);
BEGIN
SELECT MAX(ROWNUM) INTO MAXNUM FROM NAMES;
LOOP
SELECT NAME INTO C_NAME FROM
(SELECT ROWNUM RW, NAME FROM NAMES ) P WHERE P.RW = CNTR;
NSTR := NSTR ||','||C_NAME;
CNTR := CNTR + 1;
EXIT WHEN CNTR > MAXNUM;
END LOOP;
dbms_output.put_line(SUBSTR(NSTR,2));
END;
結果
PETER,PAUL,MARY
TABLE型ではとても簡単です。あなたのテーブルがStudents
と呼ばれ、それにカラムname
があると想像してみましょう。
declare @rowsCount INT
declare @i INT = 1
declare @names varchar(max) = ''
DECLARE @MyTable TABLE
(
Id int identity,
Name varchar(500)
)
insert into @MyTable select name from Students
set @rowsCount = (select COUNT(Id) from @MyTable)
while @i < @rowsCount
begin
set @names = @names + ', ' + (select name from @MyTable where Id = @i)
set @i = @i + 1
end
select @names
この例はMS SQL Server 2008 R2でテストされています
手遅れですが、すでに多くの解決策があります。これがMySQLのための簡単な解決策です:
SELECT t1.id,
GROUP_CONCAT(t1.id) ids
FROM table t1 JOIN table t2 ON (t1.id = t2.id)
GROUP BY t1.id
再帰的なクエリでそれをすることができます:
-- Create example table
CREATE TABLE tmptable (NAME VARCHAR(30)) ;
-- Insert example data
INSERT INTO tmptable VALUES('PETER');
INSERT INTO tmptable VALUES('PAUL');
INSERT INTO tmptable VALUES('MARY');
-- Recurse query
with tblwithrank as (
select * , row_number() over(order by name) rang , count(*) over() NbRow
from tmptable
),
tmpRecursive as (
select *, cast(name as varchar(2000)) as AllName from tblwithrank where rang=1
union all
select f0.*, cast(f0.name + ',' + f1.AllName as varchar(2000)) as AllName
from tblwithrank f0 inner join tmpRecursive f1 on f0.rang=f1.rang +1
)
select AllName from tmpRecursive
where rang=NbRow
Oracleにはさらに2つの方法があります。
create table name
(first_name varchar2(30));
insert into name values ('Peter');
insert into name values ('Paul');
insert into name values ('Mary');
Solution 1:
select substr(max(sys_connect_by_path (first_name, ',')),2) from (select rownum r, first_name from name ) n start with r=1 connect by prior r+1=r
o/p=> Peter,Paul,Mary
Soution 2:
select rtrim(xmlagg (xmlelement (e, first_name || ',')).extract ('//text()'), ',') first_name from name
o/p=> Peter,Paul,Mary
次のように、RECUSRSIVITY、WITH CTE、UNION ALLを使用できます。
declare @mytable as table(id int identity(1,1), str nvarchar(100))
insert into @mytable values('Peter'),('Paul'),('Mary')
declare @myresult as table(id int,str nvarchar(max),ind int, R# int)
;with cte as(select id,cast(str as nvarchar(100)) as str, cast(0 as int) ind from @mytable
union all
select t2.id,cast(t1.str+',' +t2.str as nvarchar(100)) ,t1.ind+1 from cte t1 inner join @mytable t2 on t2.id=t1.id+1)
insert into @myresult select *,row_number() over(order by ind) R# from cte
select top 1 str from @myresult order by R# desc
クリス・シャファーの答えの上に
データが繰り返される可能性がある場合
Tom
ALi
John
ALi
Tom
Mike
Tom,ALi,John,ALi,Tom,Mike
の代わりに
DISTINCTを使用して重複を避け、Tom,ALi,John,Mike
を取得できます
DECLARE @Names VARCHAR(8000)
SELECT DISTINCT @Names = COALESCE(@Names + ',', '') + Name
FROM People
WHERE Name IS NOT NULL
SELECT @Names