একক সারিতে একাধিক সারি থেকে কলাম একত্রিত করুন


14

customer_commentsডেটাবেস ডিজাইনের কারণে আমি একাধিক সারিতে বিভক্ত হয়েছি এবং একটি প্রতিবেদনের জন্য আমাকে commentsপ্রতিটি অনন্য idথেকে এক সারিতে একত্রিত করতে হবে । আমি পূর্বে SELECT ধারা এবং COALESCE কৌশল থেকে এই বিস্মৃত তালিকাটির সাথে কাজ করার কিছু চেষ্টা করেছি তবে আমি এটি পুনরায় স্মরণ করতে পারি না এবং অবশ্যই এটি সংরক্ষণ করা উচিত নয়। আমি এই ক্ষেত্রে এটি কাজ করতে পারে বলে মনে হয় না, কেবল একটি একক সারিতে কাজ করে বলে মনে হচ্ছে।

ডেটা দেখতে এমন দেখাচ্ছে:

id  row_num  customer_code comments
-----------------------------------
1   1        Dilbert        Hard
1   2        Dilbert        Worker
2   1        Wally          Lazy

আমার ফলাফলগুলি এর মতো দেখতে হবে:

id  customer_code comments
------------------------------
1   Dilbert        Hard Worker
2   Wally          Lazy

সুতরাং row_numপ্রত্যেকের জন্য ফলাফলের কেবলমাত্র এক সারি রয়েছে; মন্তব্যগুলি ক্রমের সাথে মিলিত হওয়া উচিত row_num। উপরের লিঙ্কযুক্ত SELECTকৌশলটি একটি সারি হিসাবে একটি নির্দিষ্ট ক্যোয়ারির জন্য সমস্ত মান পেতে কাজ করে, তবে আমি কীভাবে এটি SELECTএই সমস্ত সারিকে আলাদা করে দেয় এমন একটি বিবৃতির অংশ হিসাবে কাজ করতে পারি তা বুঝতে পারি না ।

আমার ক্যোয়ারীটি নিজের নিজের পুরো টেবিলের মধ্যে দিয়ে যেতে হবে এবং এই সারিগুলি আউটপুট দেয়। আমি এগুলিকে একাধিক কলামে একত্রিত করছি না, প্রতিটি সারির জন্য একটি, তাই PIVOTপ্রযোজ্য বলে মনে হয় না।

উত্তর:


18

এটি তুলনামূলকভাবে তুচ্ছ একটি সম্পর্কযুক্ত subquery সঙ্গে করতে। আপনি উল্লিখিত ব্লগ পোস্টে হাইলাইট করা COALESCE পদ্ধতিটি ব্যবহার করতে পারবেন না আপনি যদি না এটি ব্যবহারকারীর সংজ্ঞায়িত ফাংশনে (বা আপনি কেবল একবারে এক সারি ফিরে আসতে না চান) না থাকে। এখানে আমি সাধারণত এটি করি কীভাবে:

DECLARE @x TABLE 
(
  id INT, 
  row_num INT, 
  customer_code VARCHAR(32), 
  comments VARCHAR(32)
);

INSERT @x SELECT 1,1,'Dilbert','Hard'
UNION ALL SELECT 1,2,'Dilbert','Worker'
UNION ALL SELECT 2,1,'Wally','Lazy';

SELECT id, customer_code, comments = STUFF((SELECT ' ' + comments 
    FROM @x AS x2 WHERE id = x.id
     ORDER BY row_num
     FOR XML PATH('')), 1, 1, '')
FROM @x AS x
GROUP BY id, customer_code
ORDER BY id;

আপনি একটি মামলা থাকে তাহলে যেখানে মন্তব্য ডাটা অনিরাপদ-জন্য-এক্সএমএল অক্ষর থাকতে পারেনি ( >, <, &), আপনি এটি পরিবর্তন করা উচিত:

     FOR XML PATH('')), 1, 1, '')

এই আরও বিস্তৃত পদ্ধতির জন্য:

     FOR XML PATH(''), TYPE).value(N'(./text())[1]', N'varchar(max)'), 1, 1, '')

(সঠিক গন্তব্য ডেটা টাইপ, varcharবা nvarchar, এবং সঠিক দৈর্ঘ্য এবং Nঅবশ্যই ব্যবহারের সাথে সমস্ত স্ট্রিং লিটারাল উপসর্গ ব্যবহার করতে ভুলবেন না nvarchar))


3
+1 আমি তার জন্য তাত্ক্ষণিকভাবে দেখার জন্য একটি পলিক তৈরি করেছি sqlfiddle.com/#!3/e4ee5/2
মার্লোনআরবুনাল

3
হ্যাঁ, এটি একটি কবজির মতো কাজ করে। @ মার্লোনআরবিউনাল এসকিউএল ফিডল এর ​​সত্যিই রূপ নিচ্ছে!
বেন ব্রোকা

@ নিকচ্যামাস - আমি আমার ঘাড়ে আটকে যাচ্ছি এবং বলতে চাই যে order byসাব কোয়েরিটি ব্যবহার করে আদেশটি গ্যারান্টিযুক্ত । এটি এক্সএমএল ব্যবহার করে তৈরি করছে for xmlএবং এটি টিএসকিউএল ব্যবহার করে এক্সএমএল তৈরি করার উপায়। এক্সএমএল ফাইলগুলিতে উপাদানগুলির ক্রম একটি গুরুত্বপূর্ণ বিষয় এবং এটি নির্ভর করা যেতে পারে। সুতরাং এই কৌশলটি যদি আদেশের গ্যারান্টি না দেয় তবে টিএসকিউএলে এক্সএমএল সমর্থন মারাত্মকভাবে ভেঙে গেছে।
মিকেল এরিকসন

2
আমি যাচাই করেছি যে কোয়েরিটি অন্তর্নিহিত টেবিলের ক্লাস্টারড সূচক নির্বিশেষে সঠিক ক্রমে ফলাফলগুলি প্রত্যাবর্তন করবে (এমনকি একটি ক্লাস্টার ইনডেক্স row_num descঅবশ্যই order byমিকেলের পরামর্শ অনুসারে মেনে চলবে )। আমি অন্যথায় পরামর্শ দেওয়ার মত মন্তব্যগুলি সরিয়ে ফেলছি যা এখন ক্যোয়ারিতে সঠিক রয়েছে order byএবং আশা করি যে @ জনসিগেল একই কাজ করে বলে মনে করে।
অ্যারন বারট্র্যান্ড

6

যদি আপনাকে আপনার পরিবেশে সিএলআর ব্যবহার করার অনুমতি দেওয়া হয় তবে এটি কোনও ব্যবহারকারী-সংজ্ঞায়িত সামগ্রীর জন্য একটি দর্জি দ্বারা তৈরি কেস।

বিশেষত, যদি সোর্স ডেটা অ-তুচ্ছভাবে বড় হয় এবং / অথবা আপনার অ্যাপ্লিকেশনটিতে আপনাকে এই ধরণের জিনিসটি অনেক কিছু করতে হয় তবে সম্ভবত এটি যাওয়ার উপায়। আমি দৃ strongly়ভাবে সন্দেহ করি যে হারুনের সমাধানের জন্য ক্যোয়ারী পরিকল্পনাটি ইনপুট আকার বাড়ার সাথে সাথে স্কেল হবে না। (আমি টেম্প টেবিলের সাথে একটি সূচক যুক্ত করার চেষ্টা করেছি, তবে তাতে কোনও লাভ হয়নি didn't)

অন্যান্য সমাধানের মতো এই সমাধানটিও একটি বাণিজ্য off

  • এমনকি আপনার বা আপনার ক্লায়েন্টের, পরিবেশে সিএলআর ইন্টিগ্রেশন ব্যবহার করার জন্য রাজনীতি / নীতি।
  • সিএলআর ফাংশনটি সম্ভবত দ্রুততর এবং ডেটাগুলির প্রকৃত সেটকে দেওয়া হলে আরও ভাল স্কেল হবে।
  • সিএলআর ফাংশন অন্যান্য প্রশ্নের ক্ষেত্রে পুনরায় ব্যবহারযোগ্য হবে এবং প্রতিবার যখন এই ধরণের জিনিসটি করার দরকার হয় তখন আপনাকে একটি জটিল উপকণার সদৃশ (এবং ডিবাগ) করতে হবে না।
  • স্ট্রেট টি-এসকিউএল একটি বাহ্যিক কোডের এক টুকরো লেখা এবং পরিচালনার চেয়ে সহজ।
  • সম্ভবত আপনি কীভাবে সি # বা ভিবিতে প্রোগ্রাম করবেন তা জানেন না।
  • প্রভৃতি

সম্পাদনা: ঠিক আছে, আমি এটি দেখার চেষ্টা করতে গিয়েছিলাম যে এটি আসলে ভাল ছিল কিনা, এবং এটি প্রমাণিত হয়েছে যে মন্তব্যগুলি একটি নির্দিষ্ট ক্রমে হওয়া উচিত বর্তমানে সামগ্রিক ফাংশনটি ব্যবহার করে সন্তুষ্ট করা সম্ভব নয়। :(

স্ক্লুউসারডিফাইনডআগ্রিগ্রেটএট্রিবিউট.আইএসআইভারিয়ানটটো অর্ডার দেখুন । মূলত, আপনার যা করা দরকার তা হ'ল OVER(PARTITION BY customer_code ORDER BY row_num)তবে সমষ্টি করার সময় ORDER BYএই OVERদফাটিতে সমর্থন করা যায় না । আমি ধরে নিচ্ছি এসকিউএল সার্ভারে এই কার্যকারিতাটি যুক্ত করার ফলে কৃমির একটি ক্যান খোলে, কারণ কার্যকর করার পরিকল্পনায় যা পরিবর্তন করা দরকার তা তুচ্ছ। পূর্বোক্ত লিঙ্কটি বলছে এটি ভবিষ্যতের ব্যবহারের জন্য সংরক্ষিত, সুতরাং এটি ভবিষ্যতে প্রয়োগ করা যেতে পারে (২০০৫ এ আপনি সম্ভবত ভাগ্যের বাইরে রয়েছেন)।

এটি সামগ্রিক স্ট্রিংয়ের মধ্যে মানটি প্যাকিং এবং বিশ্লেষণ করে এখনও সিএলআর অবজেক্টের মধ্যে বাছাই করে সম্পন্ন করা যেতে পারে row_num... যা বেশ হ্যাকিশ বলে মনে হয়।

যে কোনও ইভেন্টে, অন্য যে কেউ সীমাবদ্ধতার পরেও এটি দরকারী খুঁজে পেতে ক্ষেত্রে আমি নীচের কোডটি ব্যবহার করেছি। আমি হ্যাকিংয়ের অংশটি পাঠকের অনুশীলন হিসাবে ছেড়ে দেব। নোট করুন যে আমি পরীক্ষার ডেটার জন্য অ্যাডভেঞ্চার ওয়ার্কস (2005) ব্যবহার করেছি।

সমষ্টি সমাবেশ:

using System;
using System.IO;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

namespace MyCompany.SqlServer
{
    [Serializable]
    [SqlUserDefinedAggregate
    (
        Format.UserDefined,
        IsNullIfEmpty = false,
        IsInvariantToDuplicates = false,
        IsInvariantToNulls = true,
        IsInvariantToOrder = false,
        MaxByteSize = -1
    )]
    public class StringConcatAggregate : IBinarySerialize
    {
        private string _accum;
        private bool _isEmpty;

        public void Init()
        {
            _accum = string.Empty;
            _isEmpty = true;
        }

        public void Accumulate(SqlString value)
        {
            if (!value.IsNull)
            {
                if (!_isEmpty)
                    _accum += ' ';
                else
                    _isEmpty = false;

                _accum += value.Value;
            }
        }

        public void Merge(StringConcatAggregate value)
        {
            Accumulate(value.Terminate());
        }

        public SqlString Terminate()
        {
            return new SqlString(_accum);
        }

        public void Read(BinaryReader r)
        {
            this.Init();

            _accum = r.ReadString();
            _isEmpty = _accum.Length == 0;
        }

        public void Write(BinaryWriter w)
        {
            w.Write(_accum);
        }
    }
}

পরীক্ষার জন্য টি-এসকিউএল ( CREATE ASSEMBLYএবং sp_configureসিএলআর বাদ দেওয়া সক্ষম করতে):

CREATE TABLE [dbo].[Comments]
(
    CustomerCode int NOT NULL,
    RowNum int NOT NULL,
    Comments nvarchar(25) NOT NULL
)

INSERT INTO [dbo].[Comments](CustomerCode, RowNum, Comments)
    SELECT
        DENSE_RANK() OVER(ORDER BY FirstName),
        ROW_NUMBER() OVER(PARTITION BY FirstName ORDER BY ContactID),
        Phone
        FROM [AdventureWorks].[Person].[Contact]
GO

CREATE AGGREGATE [dbo].[StringConcatAggregate]
(
    @input nvarchar(MAX)
)
RETURNS nvarchar(MAX)
EXTERNAL NAME StringConcatAggregate.[MyCompany.SqlServer.StringConcatAggregate]
GO


SELECT
    CustomerCode,
    [dbo].[StringConcatAggregate](Comments) AS AllComments
    FROM [dbo].[Comments]
    GROUP BY CustomerCode

1

এখানে একটি কার্সার-ভিত্তিক সমাধান যা মন্তব্যগুলির আদেশের গ্যারান্টি দেয় row_num। ( টেবিলটি কীভাবে তৈরি হয়েছিল তার জন্য আমার অন্য উত্তরটি দেখুন [dbo].[Comments]))

SET NOCOUNT ON

DECLARE cur CURSOR LOCAL FAST_FORWARD FOR
    SELECT
        CustomerCode,
        Comments
        FROM [dbo].[Comments]
        ORDER BY
            CustomerCode,
            RowNum

DECLARE @curCustomerCode int
DECLARE @lastCustomerCode int
DECLARE @curComment nvarchar(25)
DECLARE @comments nvarchar(MAX)

DECLARE @results table
(
    CustomerCode int NOT NULL,
    AllComments nvarchar(MAX) NOT NULL
)


OPEN cur

FETCH NEXT FROM cur INTO
    @curCustomerCode, @curComment

SET @lastCustomerCode = @curCustomerCode


WHILE @@FETCH_STATUS = 0
BEGIN

    IF (@lastCustomerCode != @curCustomerCode)
    BEGIN
        INSERT INTO @results(CustomerCode, AllComments)
            VALUES(@lastCustomerCode, @comments)

        SET @lastCustomerCode = @curCustomerCode
        SET @comments = NULL
    END

    IF (@comments IS NULL)
        SET @comments = @curComment
    ELSE
        SET @comments = @comments + N' ' + @curComment

    FETCH NEXT FROM cur INTO
        @curCustomerCode, @curComment

END

IF (@comments IS NOT NULL)
BEGIN
    INSERT INTO @results(CustomerCode, AllComments)
        VALUES(@curCustomerCode, @comments)
END

CLOSE cur
DEALLOCATE cur


SELECT * FROM @results

0
-- solution avoiding the cursor ...

DECLARE @idMax INT
DECLARE @idCtr INT
DECLARE @comment VARCHAR(150)

SELECT @idMax = MAX(id)
FROM [dbo].[CustomerCodeWithSeparateComments]

IF @idMax = 0
    return
DECLARE @OriginalTable AS Table
(
    [id] [int] NOT NULL,
    [row_num] [int] NULL,
    [customer_code] [varchar](50) NULL,
    [comment] [varchar](120) NULL
)

DECLARE @FinalTable AS Table
(
    [id] [int] IDENTITY(1,1) NOT NULL,
    [customer_code] [varchar](50) NULL,
    [comment] [varchar](120) NULL
)

INSERT INTO @FinalTable 
([customer_code])
SELECT [customer_code]
FROM [dbo].[CustomerCodeWithSeparateComments]
GROUP BY [customer_code]

INSERT INTO @OriginalTable
           ([id]
           ,[row_num]
           ,[customer_code]
           ,[comment])
SELECT [id]
      ,[row_num]
      ,[customer_code]
      ,[comment]
FROM [dbo].[CustomerCodeWithSeparateComments]
ORDER BY id, row_num

SET @idCtr = 1
SET @comment = ''

WHILE @idCtr < @idMax
BEGIN

    SELECT @comment = @comment + ' ' + comment
    FROM @OriginalTable 
    WHERE id = @idCtr
    UPDATE @FinalTable
       SET [comment] = @comment
    WHERE [id] = @idCtr 
    SET @idCtr = @idCtr + 1
    SET @comment = ''

END 

SELECT @comment = @comment + ' ' + comment
        FROM @OriginalTable 
        WHERE id = @idCtr

UPDATE @FinalTable
   SET [comment] = @comment
WHERE [id] = @idCtr

SELECT *
FROM @FinalTable

2
আপনি একটি কার্সার এড়ানো হয়নি। আপনি পরিবর্তে কিছুক্ষণ লুপটিকে আপনার কার্সার বলেছিলেন।
অ্যারন বার্ট্র্যান্ড
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.