كيفية سلسلة النص من صفوف متعددة في سلسلة نصية واحدة في خادم SQL؟

StackOverflow https://stackoverflow.com/questions/194852

سؤال

خذ بعين الاعتبار جدول قاعدة بيانات يحتوي على أسماء، مع ثلاثة صفوف:

Peter
Paul
Mary

هل هناك طريقة سهلة لتحويل هذا إلى سلسلة واحدة من Peter, Paul, Mary?

هل كانت مفيدة؟

المحلول

إذا كنت على SQL Server 2017 أو الأزرق السماوي، انظر ماتيو RENDA الجواب .

وكان لي مشكلة مشابهة عندما كنت أحاول أن انضمام جدولين مع علاقات واحد لكثير. في 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]

ويمكنك أن تفعل الشيء نفسه بطريقة أكثر إحكاما إذا كنت تستطيع CONCAT الفواصل في بداية واستخدام 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

فقط بعض التوضيحات (حيث يبدو أن هذه الإجابة تحصل على مشاهدات منتظمة نسبيًا):

  • إن Coalesce هو في الواقع مجرد وسيلة غش مفيدة تحقق شيئين:

1) لا حاجة للتهيئة @Names مع قيمة سلسلة فارغة.

2) لا حاجة لتجريد فاصل إضافي في النهاية.

  • سيعطي الحل أعلاه نتائج غير صحيحة إذا كان الصف يحتوي على باطل قيمة الاسم (إذا كان هناك باطل, ، ال باطل سيجعل @Names باطل بعد هذا الصف، وسيبدأ الصف التالي من جديد كسلسلة فارغة مرة أخرى.يمكن إصلاحه بسهولة باستخدام أحد الحلين:
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

اعتمادًا على السلوك الذي تريده (الخيار الأول يقوم فقط بتصفية باطلبعد الخروج، الخيار الثاني يبقيهم في القائمة برسالة علامة [استبدل "N/A" بما هو مناسب لك]).

طريقة واحدة لم تظهر بعد عبر XML data() الأمر في MS SQL Server هو:

افترض جدولًا يسمى NameList يحتوي على عمود واحد يسمى FName،

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 خادم 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 جملة

وأي بمعنى.

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 غير صالحة

وعلى '"},{"_":"' غير آمنة لأنه إذا تحتوي '"},{"_":"', لك البيانات التي سيتم هرب إلى "},{\"_\":\"

ويمكنك استبدال ', ' مع أي فاصل سلسلة


وفي SQL Server 2017، قاعدة البيانات SQL أزور

ويمكنك استخدام STRING_AGG وظيفة

في الخلية هناك وظيفة، <وأ href = "http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat" يختلط = "noreferrer" > 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

استخدم على تلتحم - يمكنك <لأ href = "https://msdn.microsoft.com/en-IN/library/ms190349.aspx" يختلط = "noreferrer" > تعرف على المزيد من هنا

للحصول على مثال:

<اقتباس فقرة>   

102

     

103

     

104

وثم كتابة رمز أدناه في مزود خدمة،

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=# \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)

وحررت

ومنذ كيو 9.0 هو href="https://stackoverflow.com/a/13425819/952135"> .

وأوراكل 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 في المنزل، لذلك أنا تخمين في بناء الجملة هنا، لكنها أكثر أو أقل:

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;

وبدءا كيو 9.0 وهذا هو بسيط جدا:

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

في الإصدارات قبل يمكن استخدام 9.0 array_agg() كما يتضح من hgmnz

في SQL خادم vNext وسيتم بناء هذا في مع وظيفة STRING_AGG، قراءة المزيد حول هذا الموضوع هنا: https://msdn.microsoft.com/en-us/library/mt790580.aspx

وعن طريق XML ساعدني في الحصول على الصفوف فصل بفواصل. للفاصلة إضافية يمكننا استخدام وظيفة استبدال من SQL Server. بدلا من إضافة فاصلة، واستخدام "البيانات () 'وAS سلسلة الصفوف مع مسافات، والتي في وقت لاحق يمكن استبدال الفواصل كما بناء الجملة مكتوبة أدناه.

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 كحد أقصى للحاجة الخاصة بك

و(شريطة ديواكار وينس فراندسين إجابات جيدة، ولكن تحتاج إلى تحسين.)

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 جدا (ولكن يهمني أن تنتظر حتى أعود إلى العمل لأن جوجل لا تفعل الاشياء العمل في المنزل: -)

ومع إجابات أخرى، يجب أن يكون الشخص الذي يقرأ الإجابة على علم الجدول مجال معين مثل السيارة أو الطالب. يجب إنشاء الجدول وملؤها مع البيانات لاختبار الحل.

وفيما يلي مثال يستخدم SQL خادم "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 

لأوراكل قواعد بيانات، انظر هذا السؤال: <لأ href = "https://stackoverflow.com/questions/1076011/how-can-multiple-rows-be-concatenated-into-one-in-oracle-without- خلق واحد في ستور "> كيف يمكن أن تكون متصلا صفوف متعددة في واحد في أوراكل دون إنشاء إجراء مخزن؟

وأفضل إجابة على ما يبدو من قبلEmmanuel، وذلك باستخدام وظيفة المدمج في LISTAGG ()، وهي متاحة في أوراكل 11g الإصدار 2 والإصدارات الأحدث.

SELECT question_id,
   LISTAGG(element_id, ',') WITHIN GROUP (ORDER BY element_id)
FROM YOUR_TABLE;
GROUP BY question_id
وأشار

وكما @ user762952 بها، وفقا لوثائق أوراكل http://www.oracle-base.com/articles/misc/string-aggregation-techniques.php أو وظيفة WM_CONCAT () هو أيضا خيارا. يبدو مستقرا، ولكن توصي أوراكل صراحة ضد استخدامه لأي SQL التطبيق، وذلك باستخدام على مسؤوليتك الخاصة.

وبخلاف ذلك، سيكون لديك لكتابة وظيفة الخاصة بك؛ وثيقة أوراكل فوق لديه دليل على كيفية القيام بذلك.

أنا حقا أحب أناقة جواب دانا.أردت فقط أن تجعلها كاملة.

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

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

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

لتجنب القيم الخالية يمكنك استخدام CONCAT ()

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

ستتطلب هذه الإجابة بعض الامتيازات في الخادم حتى تعمل.

الجمعيات هي خيار جيد بالنسبة لك.هناك الكثير من المواقع التي تشرح كيفية إنشائها.الذي أعتقد أنه تم شرحه جيدًا هو هذا واحد

إذا أردت، فقد قمت بالفعل بإنشاء التجميع، ومن الممكن تنزيل ملف 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

نأمل أن يساعد!!!

وعادة ما تستخدم اختيار مثل هذا لسلسلة سلاسل في 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

إذا كنت ترغب في التعامل مع القيم الخالية يمكنك القيام بذلك عن طريق إضافة بند فيها أو إضافة تلتحم آخر حول أول واحد.

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

في أوراكل، فمن 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

تنطبق هذه الطريقة على قاعدة بيانات Teradata Aster فقط لأنها تستخدم وظيفة NPATH الخاصة بها.

مرة أخرى، لدينا طلاب الجدول

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

ثم مع NPATH يكون مجرد تحديد واحد:

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