সামগ্রিক স্ট্রিং / একত্রিত করার সর্বোত্তম উপায়


106

আমি বিভিন্ন সারি থেকে একক সারিতে স্ট্রিংগুলি একত্র করার জন্য একটি উপায় খুঁজে পাচ্ছি। আমি এটি বিভিন্ন স্থানে করতে দেখছি, তাই এটির সুবিধার্থে একটি ফাংশন থাকা ভাল হবে। আমি ব্যবহার করে সমাধান চেষ্টা করেছি COALESCEএবং FOR XMLতারা কেবল এটি আমার জন্য কাটেনি।

স্ট্রিং এগ্রিগেশন এমন কিছু করবে:

id | Name                    Result: id | Names
-- - ----                            -- - -----
1  | Matt                            1  | Matt, Rocks
1  | Rocks                           2  | Stylus
2  | Stylus

আমি কটাক্ষপাত গ্রহণ করেছি CLR-সংজ্ঞায়িত সমষ্টিগত ফাংশন জন্য একটি প্রতিস্থাপন হিসাবে COALESCEএবং FOR XML, কিন্তু দৃশ্যত এসকিউএল নভোনীল নেই CLR-সংজ্ঞায়িত কাপড়, যা আমার জন্য একটি ব্যথা কারণ আমি এটা ব্যবহার করতে একটি সম্পূর্ণ অনেক বিশ্লিষ্ট করা হবে সক্ষম হচ্ছে জানেন সমর্থন আমার জন্য সমস্যা

এমন কি কোনও সম্ভাব্য কাজ, বা একইভাবে অনুকূল পদ্ধতি (যা সিএলআরের মতো অনুকূল নাও হতে পারে, তবে আরে আমি যা পেতে পারি তা গ্রহণ করব) যা আমি আমার জিনিসগুলিকে একত্রিত করতে ব্যবহার করতে পারি?


কোন উপায়ে আপনার for xmlপক্ষে কাজ করে না?
মিকেল এরিকসন

4
এটি কাজ করে, তবে আমি এক্সিকিউশন প্ল্যানটি একবার দেখেছিলাম এবং প্রতিটিতে for xmlক্যোয়ারি পারফরম্যান্সের ক্ষেত্রে 25% ব্যবহার দেখানো হয়েছে (ক্যোয়ারীর একটি বড় অংশ)
ম্যাট

4
for xml pathকোয়েরি করার বিভিন্ন উপায় রয়েছে । অন্যদের তুলনায় কিছুটা দ্রুত। এটি আপনার ডেটার উপর নির্ভর করতে পারে তবে ব্যবহারকারীরা distinctআমার অভিজ্ঞতাতে ব্যবহারের চেয়ে ধীর group by। এবং যদি আপনি সংক্ষিপ্ত .value('.', nvarchar(max))মানগুলি পেতে ব্যবহার করছেন তবে আপনার এটি পরিবর্তন করা উচিত.value('./text()[1]', nvarchar(max))
মিকেল এরিকসন

4
তোমার গৃহীত উত্তর আমার বর্ণনার অনুরূপ উত্তর উপর stackoverflow.com/questions/11137075/... যা আমি চিন্তা দ্রুততর এক্সএমএল চেয়ে। কোয়েরি ব্যয় দ্বারা বোকা বোকাবেন না, কোনটি দ্রুত তা দেখার জন্য আপনার পর্যাপ্ত ডেটা দরকার। এক্সএমএল দ্রুততর, যা একই প্রশ্নের উত্তর @ মিকায়েল এরিকসনের উত্তর হতে পারে । এক্সএমএল পদ্ধতির জন্য নির্বাচন করুন
মাইকেল বুয়েন

4
: এই এখানে একটি নেটিভ সমাধান জন্য ভোট করুন connect.microsoft.com/SQLServer/feedback/details/1026336
JohnLBevan

উত্তর:


68

সমাধান

অনুকূল সংজ্ঞাটি পরিবর্তিত হতে পারে, তবে নিয়মিত লেনদেন এসকিউএল ব্যবহার করে বিভিন্ন সারি থেকে স্ট্রিং সংযুক্ত করার পদ্ধতি এখানে রয়েছে, যা অ্যাজুরেতে সূক্ষ্মভাবে কাজ করা উচিত।

;WITH Partitioned AS
(
    SELECT 
        ID,
        Name,
        ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Name) AS NameNumber,
        COUNT(*) OVER (PARTITION BY ID) AS NameCount
    FROM dbo.SourceTable
),
Concatenated AS
(
    SELECT 
        ID, 
        CAST(Name AS nvarchar) AS FullName, 
        Name, 
        NameNumber, 
        NameCount 
    FROM Partitioned 
    WHERE NameNumber = 1

    UNION ALL

    SELECT 
        P.ID, 
        CAST(C.FullName + ', ' + P.Name AS nvarchar), 
        P.Name, 
        P.NameNumber, 
        P.NameCount
    FROM Partitioned AS P
        INNER JOIN Concatenated AS C 
                ON P.ID = C.ID 
                AND P.NameNumber = C.NameNumber + 1
)
SELECT 
    ID,
    FullName
FROM Concatenated
WHERE NameNumber = NameCount

ব্যাখ্যা

পদ্ধতিটি তিনটি ধাপে ফোটায়:

  1. সংযোগের জন্য প্রয়োজনীয় হিসাবে সারিগুলি ব্যবহার করে OVERএবং PARTITIONগোষ্ঠীভুক্ত করার জন্য তাদের অর্ডার করুন। ফলাফল Partitionedসিটিই। ফলাফলগুলি ফিল্টার করার জন্য আমরা প্রতিটি বিভাজনে সারি গণনা রাখি।

  2. পুনরাবৃত্ত সিটিই ( Concatenated) ব্যবহার করে NameNumberকলামে Nameমান যুক্ত করে সারি সংখ্যা ( কলাম) দিয়ে পুনরাবৃত্তি করা হবে FullName

  3. সমস্ত ফলাফল ফিল্টার আউট তবে সবচেয়ে বেশি রয়েছে NameNumber

অনুগ্রহ করে মনে রাখবেন যে এই ক্যোয়ারীটিকে অনুমানযোগ্য করে তুলতে উভয়কে গ্রুপিং সংজ্ঞায়িত করতে হবে (উদাহরণস্বরূপ, আপনার IDদৃশ্যে একই সাথে সারিগুলি সংক্ষিপ্ত করা হয়) এবং বাছাই করা (আমি ধরে নিয়েছি যে আপনি স্ট্রিংটি বর্ণনাক্রমে পূর্বে বর্ণমালিকভাবে সাজান)।

নিম্নলিখিত ডাটাগুলির সাথে আমি দ্রুত এসকিউএল সার্ভার ২০১২-তে সমাধানটি পরীক্ষা করেছি:

INSERT dbo.SourceTable (ID, Name)
VALUES 
(1, 'Matt'),
(1, 'Rocks'),
(2, 'Stylus'),
(3, 'Foo'),
(3, 'Bar'),
(3, 'Baz')

ক্যোয়ারির ফলাফল:

ID          FullName
----------- ------------------------------
2           Stylus
3           Bar, Baz, Foo
1           Matt, Rocks

4
আমি xmlpath এর বিপরীতে এইভাবে সময় ব্যয় করেছিলাম এবং আমি প্রায় 4 মিলিসেকেন্ড বনাম প্রায় 54 মিলি সেকেন্ডে পৌঁছেছি। সুতরাং xmplath উপায় বড় ক্ষেত্রে ক্ষেত্রে বিশেষত ভাল। আমি তুলনা কোডটি একটি পৃথক উত্তরে লিখব।
কিউমাস্টার

এটি অনেক বেশি ভাল যেহেতু এই পদ্ধতির সর্বাধিক 100 মানের জন্য কাজ করে।
রোমানো জুম্বু

@ রোমানো-জুম্ব- আপনার প্রয়োজন অনুসারে সিটিই সীমা নির্ধারণ করতে MAXRECURSION ব্যবহার করুন।
সার্ভ বেলভ

4
আশ্চর্যের বিষয়, সিটিই আমার পক্ষে বেশ ধীর ছিল। sqlperformance.com/2014/08/t-sql-queries/… বেশ কয়েকটি কৌশলগুলির সাথে তুলনা করে এবং আমার ফলাফলগুলির সাথে একমত বলে মনে হচ্ছে।
নিকোলে

1 মিলিয়নেরও বেশি রেকর্ড সহ কোনও টেবিলের জন্য এই সমাধানটি কাজ করে না। এছাড়াও, আমাদের পুনরাবৃত্তিমূলক গভীরতার একটি সীমা রয়েছে
আর্দলান শাহঘোলি

52

এক্সএমএল পথের জন্য পদ্ধতিগুলি কি নীচের মতো সত্যিই ধীর? ইটজিক বেন-গান লিখেছেন যে এই পদ্ধতির তার টি-এসকিউএল ক্যোয়ারিং বইটিতে ভাল পারফরম্যান্স রয়েছে (আমার বক্তব্য অনুসারে মিঃ বেন-গান একটি বিশ্বাসযোগ্য উত্স)।

create table #t (id int, name varchar(20))

insert into #t
values (1, 'Matt'), (1, 'Rocks'), (2, 'Stylus')

select  id
        ,Names = stuff((select ', ' + name as [text()]
        from #t xt
        where xt.id = t.id
        for xml path('')), 1, 2, '')
from #t t
group by id

idকোনও টেবিলের আকার একবার সমস্যা হয়ে গেলে সেই কলামটিতে একটি সূচক রাখতে ভুলবেন না ।
milivojeviCH

4
এবং কীভাবে স্ট্যাম্প / এক্সএমএল পাথ কাজ ( স্ট্যাকওভারফ্লো.com / a / 31212160 / 1026 ) পড়ার পরে , আমি আত্মবিশ্বাসী যে এর নামে এক্সএমএল থাকা সত্ত্বেও এটি একটি ভাল সমাধান :)
নিকোলে

4
@ স্ল্যাকটারম্যান চালিত হওয়া রেকর্ডের সংখ্যার উপর নির্ভর করে। আমি মনে করি সিটিইয়ের তুলনায় এক্সএমএল স্বল্প সংখ্যায় ঘাটতি রয়েছে, তবে উপরের ভলিউমের গণনায় পুনরাবৃত্তির ডিপোটি সীমাবদ্ধতা হ্রাস করে এবং সঠিকভাবে এবং সংশ্লেষপূর্ণভাবে সম্পন্ন করা হলে নেভিগেট করা সহজ।
গোল্ডবিশপ 18

এক্সএমএল পথের পদ্ধতিগুলির জন্য যদি আপনার ডেটাতে ইমোজি বা বিশেষ / সারোগেট অক্ষর থাকে তবে তা ফুরিয়ে যায় !!!
ডেভিনিস্ট

4
এই কোডটির ফলাফল এক্সএমএল-এনকোডযুক্ত পাঠ্যে ( &স্যুইচ করা &, এবং এ জাতীয়)। আরও সঠিক for xmlসমাধান এখানে সরবরাহ করা হয়
ফ্রেডারিক

36

আমাদের মধ্যে যারা এটি পেয়েছি তাদের জন্য এবং অ্যাজুরি এসকিউএল ডেটাবেস ব্যবহার করছে না:

STRING_AGG()পোস্টগ্রেএসকিউএল, এসকিউএল সার্ভার 2017 এবং অ্যাজুরে এসকিউএল
https://www.postgresql.org/docs/current/static/funitions-aggregate.html
https://docs.microsoft.com/en-us/sql/t-sql/ ফাংশন / স্ট্রিং-অগ্রি-লেনদেন-এসকেএল

GROUP_CONCAT()মাইএসকিউএল
http://dev.mysql.com/doc/refman/5.7/en/group-by-function.html#function_group-concat এ

(আজুর আপডেটের জন্য @ ব্রায়ানজার্ডেন এবং @ মিলানিয়োকে ধন্যবাদ)

উদাহরণ কোড:

select Id
, STRING_AGG(Name, ', ') Names 
from Demo
group by Id

এসকিউএল ফিডল: http://sqlfiddle.com/#!18/89251/1


4
আমি এটি পরীক্ষা করেছি এবং এখন এটি অ্যাজুরে এসকিউএল ডেটাবেস-এর সাথে দুর্দান্ত কাজ করে।
মিলানিও

4
STRING_AGG
২০১

4
ধন্যবাদ, এসকিউএল সার্ভার সংস্করণ পরিবর্তনের জন্য আমির এবং মরগান থ্র্যাপ। আপডেট হয়েছে। (লেখার সময় এটি 2016 সালের সংস্করণে সমর্থিত বলে দাবি করা হয়েছিল))
হ্রবকি

26

যদিও @ সেরজ উত্তরটি সঠিক তবে আমি xmlpath এর সাথে তার সময়ের সময় ব্যয় তুলনা করেছি এবং আমি খুঁজে পেয়েছি যে এক্সএমপাথটি এত দ্রুত। আমি তুলনা কোড লিখব এবং আপনি নিজের দ্বারা এটি পরীক্ষা করতে পারেন। এটি @ সেরেজ উপায়:

DECLARE @startTime datetime2;
DECLARE @endTime datetime2;
DECLARE @counter INT;
SET @counter = 1;

set nocount on;

declare @YourTable table (ID int, Name nvarchar(50))

WHILE @counter < 1000
BEGIN
    insert into @YourTable VALUES (ROUND(@counter/10,0), CONVERT(NVARCHAR(50), @counter) + 'CC')
    SET @counter = @counter + 1;
END

SET @startTime = GETDATE()

;WITH Partitioned AS
(
    SELECT 
        ID,
        Name,
        ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Name) AS NameNumber,
        COUNT(*) OVER (PARTITION BY ID) AS NameCount
    FROM @YourTable
),
Concatenated AS
(
    SELECT ID, CAST(Name AS nvarchar) AS FullName, Name, NameNumber, NameCount FROM Partitioned WHERE NameNumber = 1

    UNION ALL

    SELECT 
        P.ID, CAST(C.FullName + ', ' + P.Name AS nvarchar), P.Name, P.NameNumber, P.NameCount
    FROM Partitioned AS P
        INNER JOIN Concatenated AS C ON P.ID = C.ID AND P.NameNumber = C.NameNumber + 1
)
SELECT 
    ID,
    FullName
FROM Concatenated
WHERE NameNumber = NameCount

SET @endTime = GETDATE();

SELECT DATEDIFF(millisecond,@startTime, @endTime)
--Take about 54 milliseconds

এবং এটি এক্সএমপথ উপায়:

DECLARE @startTime datetime2;
DECLARE @endTime datetime2;
DECLARE @counter INT;
SET @counter = 1;

set nocount on;

declare @YourTable table (RowID int, HeaderValue int, ChildValue varchar(5))

WHILE @counter < 1000
BEGIN
    insert into @YourTable VALUES (@counter, ROUND(@counter/10,0), CONVERT(NVARCHAR(50), @counter) + 'CC')
    SET @counter = @counter + 1;
END

SET @startTime = GETDATE();

set nocount off
SELECT
    t1.HeaderValue
        ,STUFF(
                   (SELECT
                        ', ' + t2.ChildValue
                        FROM @YourTable t2
                        WHERE t1.HeaderValue=t2.HeaderValue
                        ORDER BY t2.ChildValue
                        FOR XML PATH(''), TYPE
                   ).value('.','varchar(max)')
                   ,1,2, ''
              ) AS ChildValues
    FROM @YourTable t1
    GROUP BY t1.HeaderValue

SET @endTime = GETDATE();

SELECT DATEDIFF(millisecond,@startTime, @endTime)
--Take about 4 milliseconds

4
+1, আপনি কিউমাস্টার (ডার্ক আর্টস এর)! আমি আরও একটি নাটকীয় পার্থক্য পেয়েছি। (000 3000 এমসিসি সিটিই বনাম S এসকিউএল সার্ভার ২০০ R সালে Windows০ এমসিসি এক্সএমএল ২০০৮ উইন্ডোজ সার্ভার ২০০৮ আর 2 তে ইনটেল জিওন ই 5-2630 ভি 4 @ 2.20 গিগাহার্টজ এক্স 2 ডাব্লু / ~ 1 জিবি ফ্রি)। কেবলমাত্র পরামর্শগুলি হ'ল: হয় উভয় সংস্করণের জন্য ওপি'র বা (সাধারণত) জেনেরিক পদগুলি ব্যবহার করুন, ২) যেহেতু ওপি'র প্রশ্নটি কীভাবে "সংহত / সমষ্টিযুক্ত স্ট্রিং " করতে হয় এবং এটি কেবল স্ট্রিংগুলির জন্য প্রয়োজন (বনাম একটি সংখ্যাসম্য ), জেনেরিক পদগুলি খুব সাধারণ। কেবলমাত্র "গ্রুপ নাম্বার" এবং "স্ট্রিংভ্যালু" ব্যবহার করুন, ৩) ঘোষণা করুন এবং একটি "ডিলিমিটার" ভেরিয়েবল ব্যবহার করুন এবং "লেন (ডিলিমিটার)" বনাম "2" ব্যবহার করুন।
টম

4
এক্সএমএল এনকোডিংয়ে বিশেষ চরিত্রটি প্রসারিত না করার জন্য +1 (যেমন 'অন্যান্য & নিকৃষ্ট সমাধানের মতো' & 'প্রসারিত হয় না)
বিপরীত প্রকৌশলী ২

16

আপডেট: এমএস এসকিউএল সার্ভার 2017+, অ্যাজুরি এসকিউএল ডেটাবেস

আপনি ব্যবহার করতে পারেন: STRING_AGG

ওপির অনুরোধের জন্য ব্যবহারটি বেশ সহজ:

SELECT id, STRING_AGG(name, ', ') AS names
FROM some_table
GROUP BY id

আরও পড়ুন

আমার পুরাতন উত্তর-উত্তর সঠিকভাবে মুছে ফেলা হয়েছে (নীচে কৌশলটি বামে), তবে ভবিষ্যতে কেউ যদি এখানে অবতরণ করে, তবে সুসংবাদ রয়েছে। তারা আজুর এসকিউএল ডেটাবেসেও STRING_AGG () ছড়িয়ে দিয়েছে। এটি মূলত এই পোস্টে অনুরোধ করা সঠিক কার্যকারিতা সরবরাহ করতে হবে দেশীয় এবং সমর্থনে নির্মিত। @ হ্রোব্কি এটি আগে এসকিউএল সার্ভার 2016 বৈশিষ্ট্য হিসাবে উল্লেখ করেছিলেন।

--- পুরাতন পোস্ট: সরাসরি @ হ্রোবকিকে জবাব দেওয়ার জন্য এখানে যথেষ্ট খ্যাতি নেই, তবে STRING_AGG দেখতে দুর্দান্ত দেখাচ্ছে, তবে এটি বর্তমানে কেবল এসকিউএল সার্ভার 2016 ভিএনেক্সটে উপলভ্য। আশা করা যায় এটি খুব শিগগিরই আজুর এসকিউএল ডেটাবেসে অনুসরণ করবে ..


4
আমি এটি পরীক্ষা করে দেখেছি এবং এটি
অ্যাজুরে

4
STRING_AGG()এসকিউএল সার্ভার 2017 এ উপলব্ধ হয়ে উঠেছে, কোনও সামঞ্জস্যতার স্তরে। ডকস.মাইক্রোসফট.এইন
ব্যবহারকারী

4
হ্যাঁ. STRING_AGG উপলব্ধ SQL সার্ভার 2016 হয়
Magne

2

আমি সেরজের উত্তরটি খুব আশাব্যঞ্জক বলে মনে করেছি, তবে এটি লিখিতভাবে পারফরম্যান্সের সমস্যার মুখোমুখি হয়েছিল। যাইহোক, যখন আমি অস্থায়ী টেবিলগুলি ব্যবহার করতে এবং ডাবল সিটিই টেবিলগুলি অন্তর্ভুক্ত না করার জন্য এটি পুনর্গঠন করি তখন সম্পাদনাটি 1 মিনিট 40 সেকেন্ড থেকে 1000 মিলিত রেকর্ডের জন্য সাব-সেকেন্ডে চলে যায়। এসকিউএল সার্ভারের পুরানো সংস্করণগুলিতে এক্সএমএল ছাড়াই এটি করা দরকার এমন সবার জন্য এখানে:

DECLARE @STRUCTURED_VALUES TABLE (
     ID                 INT
    ,VALUE              VARCHAR(MAX) NULL
    ,VALUENUMBER        BIGINT
    ,VALUECOUNT         INT
);

INSERT INTO @STRUCTURED_VALUES
SELECT   ID
        ,VALUE
        ,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY VALUE) AS VALUENUMBER
        ,COUNT(*) OVER (PARTITION BY ID)    AS VALUECOUNT
FROM    RAW_VALUES_TABLE;

WITH CTE AS (
    SELECT   SV.ID
            ,SV.VALUE
            ,SV.VALUENUMBER
            ,SV.VALUECOUNT
    FROM    @STRUCTURED_VALUES SV
    WHERE   VALUENUMBER = 1

    UNION ALL

    SELECT   SV.ID
            ,CTE.VALUE + ' ' + SV.VALUE AS VALUE
            ,SV.VALUENUMBER
            ,SV.VALUECOUNT
    FROM    @STRUCTURED_VALUES SV
    JOIN    CTE 
        ON  SV.ID = CTE.ID
        AND SV.VALUENUMBER = CTE.VALUENUMBER + 1

)
SELECT   ID
        ,VALUE
FROM    CTE
WHERE   VALUENUMBER = VALUECOUNT
ORDER BY ID
;

1

আপনি স্ট্রিংগুলি একত্রিত করতে + = ব্যবহার করতে পারেন, উদাহরণস্বরূপ:

declare @test nvarchar(max)
set @test = ''
select @test += name from names

আপনি যদি @ সর্বশেষ নির্বাচন করেন তবে এটি আপনাকে সমস্ত নাম একত্রিত করবে give


এসকিউএল ডায়ালেক্ট বা সংস্করণটি কখন থেকে সমর্থনযোগ্য তা নির্দিষ্ট করুন।
হ্রোবকি

এটি এসকিউএল সার্ভার ২০১২-এ কাজ করে Note নোট করুন যে কমা-বিচ্ছিন্ন তালিকাটি তৈরি করা যেতে পারেselect @test += name + ', ' from names
আর্ট শ্মিট

4
এটি অপরিবর্তিত আচরণ ব্যবহার করে এবং নিরাপদ নয়। ORDER BYআপনার ক্যোয়ারিতে যদি একটি থাকে তবে এটি বিশেষত একটি অদ্ভুত / ভুল ফলাফল দেওয়ার সম্ভাবনা রয়েছে । আপনার তালিকাভুক্ত বিকল্পগুলির একটি ব্যবহার করা উচিত।
ড্যান্নো নভো

4
এই ধরণের কোয়েরিটি কখনই আচরণের সংজ্ঞায়িত হয়নি এবং এসকিউএল সার্ভার 2019-এ আমরা পূর্ববর্তী সংস্করণগুলির তুলনায় আরও নিয়মিতভাবে ভুল আচরণ খুঁজে পেয়েছি। এই পদ্ধতির ব্যবহার করবেন না।
ম্যাথু রোডাটাস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.