এসকিউএল সার্ভার গতিশীল পিভট কোয়েরি?


202

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

date        category        amount
1/1/2012    ABC             1000.00
2/1/2012    DEF             500.00
2/1/2012    GHI             800.00
2/10/2012   DEF             700.00
3/1/2012    ABC             1100.00

নিম্নলিখিত মধ্যে:

date        ABC             DEF             GHI
1/1/2012    1000.00
2/1/2012                    500.00
2/1/2012                                    800.00
2/10/2012                   700.00
3/1/2012    1100.00

ফাঁকা দাগগুলি NULL বা ফাঁকা হতে পারে, হয় ভাল, এবং বিভাগগুলি গতিশীল হতে হবে। এর আর একটি সম্ভাব্য সতর্কতা হ'ল আমরা একটি সীমাবদ্ধ ক্ষমতায় ক্যোয়ারী চালাচ্ছি যার অর্থ টেম্প টেবিলগুলি বাইরে are আমি গবেষণার চেষ্টা করেছি এবং অবতরণ PIVOTকরেছি কিন্তু আমি এটি ব্যবহার করার আগে আমার সর্বোত্তম প্রচেষ্টা সত্ত্বেও আমি সত্যিই এটি বুঝতে না পারার আগে কখনও ব্যবহার করি নি। কেউ কি আমাকে সঠিক দিকে নির্দেশ করতে পারে?


3
এসকিউএল সার্ভারের কোন সংস্করণ দয়া করে?
অ্যারন বারট্র্যান্ড 21

উত্তর:


250

গতিশীল এসকিউএল পাইভট:

create table temp
(
    date datetime,
    category varchar(3),
    amount money
)

insert into temp values ('1/1/2012', 'ABC', 1000.00)
insert into temp values ('2/1/2012', 'DEF', 500.00)
insert into temp values ('2/1/2012', 'GHI', 800.00)
insert into temp values ('2/10/2012', 'DEF', 700.00)
insert into temp values ('3/1/2012', 'ABC', 1100.00)


DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX);

SET @cols = STUFF((SELECT distinct ',' + QUOTENAME(c.category) 
            FROM temp c
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT date, ' + @cols + ' from 
            (
                select date
                    , amount
                    , category
                from temp
           ) x
            pivot 
            (
                 max(amount)
                for category in (' + @cols + ')
            ) p '


execute(@query)

drop table temp

ফলাফল:

Date                        ABC         DEF    GHI
2012-01-01 00:00:00.000     1000.00     NULL    NULL
2012-02-01 00:00:00.000     NULL        500.00  800.00
2012-02-10 00:00:00.000     NULL        700.00  NULL
2012-03-01 00:00:00.000     1100.00     NULL    NULL

সুতরাং \ @ কুলগুলি অবশ্যই স্ট্রিং-কনটেটেটেড হতে হবে, তাই না? আমরা সেখানে sp_executesql এবং পরামিতি-বাইন্ডিং ইন্টারপোলেট করতে ব্যবহার করতে পারি না \ @ কুলগুলিতে? যদিও আমরা \ @ কুলগুলি নিজেরাই নির্মাণ করি, তবে এতে যদি কোনওভাবে দূষিত এসকিউএল থাকে। এটি যুক্ত করে তা কার্যকর করার আগে আমি যে কোনও অতিরিক্ত প্রশমিত পদক্ষেপ নিতে পারি?
লাল মটর 20

আপনি এটিতে সারি এবং কলামগুলি কীভাবে বাছাই করবেন?
প্যাট্রিক শম্বুর্গ

@ পেট্রিকসোম্বર્ગ বিভিন্ন ধরণের উপায় রয়েছে - আপনি যদি বাছাই করতে চান @colsতবে আপনি DISTINCTব্যবহারটি সরিয়ে ফেলতে পারবেন GROUP BYএবং ORDER BYযখন আপনি তালিকাটি পেয়েছিলেন @cols
Taryn

আমি চেষ্টা করব। সারি কি? আমি একটি তারিখও ব্যবহার করছি, এবং এটি কার্যকরভাবে আসে না।
প্যাট্রিক শম্বুর্গ

1
কোনও বিষয় নয় আমি অর্ডারটি ভুল জায়গায় রেখে দিচ্ছি।
প্যাট্রিক শম্বুর্গ

27

গতিশীল এসকিউএল পাইভট

কলাম স্ট্রিং তৈরির জন্য পৃথক পদ্ধতি

create table #temp
(
    date datetime,
    category varchar(3),
    amount money
)

insert into #temp values ('1/1/2012', 'ABC', 1000.00)
insert into #temp values ('2/1/2012', 'DEF', 500.00)
insert into #temp values ('2/1/2012', 'GHI', 800.00)
insert into #temp values ('2/10/2012', 'DEF', 700.00)
insert into #temp values ('3/1/2012', 'ABC', 1100.00)

DECLARE @cols  AS NVARCHAR(MAX)='';
DECLARE @query AS NVARCHAR(MAX)='';

SELECT @cols = @cols + QUOTENAME(category) + ',' FROM (select distinct category from #temp ) as tmp
select @cols = substring(@cols, 0, len(@cols)) --trim "," at end

set @query = 
'SELECT * from 
(
    select date, amount, category from #temp
) src
pivot 
(
    max(amount) for category in (' + @cols + ')
) piv'

execute(@query)
drop table #temp

ফলাফল

date                    ABC     DEF     GHI
2012-01-01 00:00:00.000 1000.00 NULL    NULL
2012-02-01 00:00:00.000 NULL    500.00  800.00
2012-02-10 00:00:00.000 NULL    700.00  NULL
2012-03-01 00:00:00.000 1100.00 NULL    NULL

13

আমি জানি যে এই প্রশ্নটি আরও পুরানো তবে আমি উত্তরগুলি খুঁজছিলাম এবং ভেবেছিলাম যে আমি সমস্যার "গতিশীল" অংশটি প্রসারিত করতে এবং সম্ভবত কাউকে সাহায্য করতে সক্ষম হতে পারি।

প্রথম এবং সর্বাগ্রে আমি কয়েকজন সহকর্মী অসুবিধাগুলি এবং বৃহত ডেটা সেটগুলি দ্রুত তৈরি করা দরকার এমন একটি সমস্যার সমাধানের জন্য এই সমাধানটি তৈরি করেছি।

এই সমাধানটির জন্য একটি সঞ্চিত প্রক্রিয়া তৈরি করা দরকার তাই এটি যদি আপনার প্রয়োজনের জন্য প্রশ্ন থেকে থাকে তবে দয়া করে এখনই পড়া বন্ধ করুন।

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

সঞ্চিত পদ্ধতি তৈরি করতে কোড দিয়ে শুরু করা যাক। এই কোডটি এসএসএমএস 2005 এবং তারপরের সমস্ত সংস্করণে কাজ করা উচিত তবে আমি 2005 বা 2016 সালে এটি পরীক্ষা করেছি না তবে কেন এটি কাজ করবে না তা আমি দেখতে পাচ্ছি না।

create PROCEDURE [dbo].[USP_DYNAMIC_PIVOT]
    (
        @STATIC_COLUMN VARCHAR(255),
        @PIVOT_COLUMN VARCHAR(255),
        @VALUE_COLUMN VARCHAR(255),
        @TABLE VARCHAR(255),
        @AGGREGATE VARCHAR(20) = null
    )

AS


BEGIN

SET NOCOUNT ON;
declare @AVAIABLE_TO_PIVOT NVARCHAR(MAX),
        @SQLSTRING NVARCHAR(MAX),
        @PIVOT_SQL_STRING NVARCHAR(MAX),
        @TEMPVARCOLUMNS NVARCHAR(MAX),
        @TABLESQL NVARCHAR(MAX)

if isnull(@AGGREGATE,'') = '' 
    begin
        SET @AGGREGATE = 'MAX'
    end


 SET @PIVOT_SQL_STRING =    'SELECT top 1 STUFF((SELECT distinct '', '' + CAST(''[''+CONVERT(VARCHAR,'+ @PIVOT_COLUMN+')+'']''  AS VARCHAR(50)) [text()]
                            FROM '+@TABLE+'
                            WHERE ISNULL('+@PIVOT_COLUMN+','''') <> ''''
                            FOR XML PATH(''''), TYPE)
                            .value(''.'',''NVARCHAR(MAX)''),1,2,'' '') as PIVOT_VALUES
                            from '+@TABLE+' ma
                            ORDER BY ' + @PIVOT_COLUMN + ''

declare @TAB AS TABLE(COL NVARCHAR(MAX) )

INSERT INTO @TAB EXEC SP_EXECUTESQL  @PIVOT_SQL_STRING, @AVAIABLE_TO_PIVOT 

SET @AVAIABLE_TO_PIVOT = (SELECT * FROM @TAB)


SET @TEMPVARCOLUMNS = (SELECT replace(@AVAIABLE_TO_PIVOT,',',' nvarchar(255) null,') + ' nvarchar(255) null')


SET @SQLSTRING = 'DECLARE @RETURN_TABLE TABLE ('+@STATIC_COLUMN+' NVARCHAR(255) NULL,'+@TEMPVARCOLUMNS+')  
                    INSERT INTO @RETURN_TABLE('+@STATIC_COLUMN+','+@AVAIABLE_TO_PIVOT+')

                    select * from (
                    SELECT ' + @STATIC_COLUMN + ' , ' + @PIVOT_COLUMN + ', ' + @VALUE_COLUMN + ' FROM '+@TABLE+' ) a

                    PIVOT
                    (
                    '+@AGGREGATE+'('+@VALUE_COLUMN+')
                    FOR '+@PIVOT_COLUMN+' IN ('+@AVAIABLE_TO_PIVOT+')
                    ) piv

                    SELECT * FROM @RETURN_TABLE'



EXEC SP_EXECUTESQL @SQLSTRING

END

পরবর্তী আমরা উদাহরণের জন্য আমাদের ডেটা প্রস্তুত করব। সামগ্রিক পরিবর্তনের বৈচিত্র্যময় আউটপুটগুলি দেখানোর জন্য ধারণার এই প্রমাণটিতে ব্যবহার করার জন্য আমি বেশ কয়েকটি ডাটা উপাদান যুক্ত করে গ্রহণযোগ্য উত্তর থেকে ডেটা উদাহরণ গ্রহণ করেছি।

create table temp
(
    date datetime,
    category varchar(3),
    amount money
)

insert into temp values ('1/1/2012', 'ABC', 1000.00)
insert into temp values ('1/1/2012', 'ABC', 2000.00) -- added
insert into temp values ('2/1/2012', 'DEF', 500.00)
insert into temp values ('2/1/2012', 'DEF', 1500.00) -- added
insert into temp values ('2/1/2012', 'GHI', 800.00)
insert into temp values ('2/10/2012', 'DEF', 700.00)
insert into temp values ('2/10/2012', 'DEF', 800.00) -- addded
insert into temp values ('3/1/2012', 'ABC', 1100.00)

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

exec [dbo].[USP_DYNAMIC_PIVOT] 'date','category','amount','dbo.temp','sum'
exec [dbo].[USP_DYNAMIC_PIVOT] 'date','category','amount','dbo.temp','max'
exec [dbo].[USP_DYNAMIC_PIVOT] 'date','category','amount','dbo.temp','avg'
exec [dbo].[USP_DYNAMIC_PIVOT] 'date','category','amount','dbo.temp','min'

এই প্রয়োগটি যথাক্রমে নিম্নলিখিত ডেটা সেটগুলি প্রদান করে।

এখানে চিত্র বর্ণনা লিখুন


ভাল করেছ! আপনি কি দয়া করে সঞ্চিত পদ্ধতির পরিবর্তে টিভিএফের একটি বিকল্প তৈরি করতে পারেন? এই জাতীয় TVF থেকে নির্বাচন করা সুবিধাজনক হবে।
প্রেজেমিস্লা পুনরায় স্মরণ

3
দুর্ভাগ্যক্রমে, আমার জ্ঞানের সেরাটি নয়, কারণ আপনার কাছে কোনও টিভিএফের জন্য গতিশীল কাঠামো থাকতে পারে না। আপনার একটি টিভিএফ এ কলামগুলির স্থির সেট থাকতে হবে।
এসফ্রেজফস্কি

8

পিভট কলাম তালিকাটি তৈরি করতে STRING_AGG ফাংশনটি ব্যবহার করে এসকিউএল সার্ভার 2017 এর জন্য আপডেট হওয়া সংস্করণ:

create table temp
(
    date datetime,
    category varchar(3),
    amount money
);

insert into temp values ('20120101', 'ABC', 1000.00);
insert into temp values ('20120201', 'DEF', 500.00);
insert into temp values ('20120201', 'GHI', 800.00);
insert into temp values ('20120210', 'DEF', 700.00);
insert into temp values ('20120301', 'ABC', 1100.00);


DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX);

SET @cols = (SELECT STRING_AGG(category,',') FROM (SELECT DISTINCT category FROM temp WHERE category IS NOT NULL)t);

set @query = 'SELECT date, ' + @cols + ' from 
            (
                select date
                    , amount
                    , category
                from temp
           ) x
            pivot 
            (
                 max(amount)
                for category in (' + @cols + ')
            ) p ';

execute(@query);

drop table temp;

6

আপনি ডায়নামিক টিএসকিউএল ব্যবহার করে এটি অর্জন করতে পারেন (এসকিউএল ইঞ্জেকশন আক্রমণগুলি এড়াতে কোটেম ব্যবহার করতে ভুলবেন না):

এসকিউএল সার্ভার 2005-এ ডায়নামিক কলাম সহ পিভট

এসকিউএল সার্ভার - গতিশীল পিভট টেবিল - এসকিউএল ইঞ্জেকশন

ডাইনামিক এসকিউএল এর অভিশাপ এবং আশীর্বাদ সম্পর্কে বাধ্যতামূলক রেফারেন্স


11
FWIW QUOTENAMEকেবলমাত্র এসকিউএল ইঞ্জেকশন আক্রমণগুলিতে সহায়তা করে যদি আপনি @tableName কোনও ব্যবহারকারীর কাছ থেকে প্যারামিটার হিসাবে গ্রহণ করে থাকেন এবং এটির মতো কোনও প্রশ্নের সাথে যুক্ত করে থাকেন SET @sql = 'SELECT * FROM ' + @tableName;। আপনি প্রচুর সংবেদনশীল ডায়নামিক এসকিউএল স্ট্রিং তৈরি করতে পারেন এবং QUOTENAMEআপনাকে সহায়তা করার জন্য কোনও টিক চিহ্ন ব্যবহার করবেন না।
অ্যারন বার্ট্র্যান্ড 21

2
@davids দয়া করে পড়ুন এই মেটা আলোচনা । আপনি হাইপারলিংকগুলি সরিয়ে ফেললে আপনার উত্তরটি অসম্পূর্ণ।
কেরিমিট 16

@ ক্রিমিট, আমি সম্মত হই যে কোডটি দেখানো আরও সহায়ক, তবে আপনি কি বলছেন যে এটির উত্তর হওয়ার জন্য এটি প্রয়োজন? লিঙ্কগুলি ছাড়াই, আমার উত্তরটি "আপনি ডায়নামিক টিএসকিউএল ব্যবহার করে এটি অর্জন করতে পারেন" " নির্বাচিত উত্তরটি একই পথের পরামর্শ দেয়, যুক্ত কীভাবে এটি কীভাবে কীভাবে করা যায় তাও বেনিফিট সহ, কেন এটি উত্তর হিসাবে নির্বাচিত হয়েছিল।
ডেভিডস

2
আমি নির্বাচিত উত্তরটিকে ভোট দিয়েছি (এটি নির্বাচিত হওয়ার আগে) কারণ এটির একটি উদাহরণ ছিল এবং নতুন কাউকে আরও ভালভাবে সহায়তা করবে। তবে আমি মনে করি যে নতুন কেউ আমার দেওয়া লিঙ্কগুলিও পড়া উচিত, এ কারণেই আমি সেগুলি সরিয়ে নেই।
ডেভিডস

3

আমার সমাধানটি অদৃশ্য নাল মানগুলি পরিষ্কার করছে

DECLARE @cols AS NVARCHAR(MAX),
@maxcols AS NVARCHAR(MAX),
@query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT ',' + QUOTENAME(CodigoFormaPago) 
                from PO_FormasPago
                order by CodigoFormaPago
        FOR XML PATH(''), TYPE
        ).value('.', 'NVARCHAR(MAX)') 
    ,1,1,'')

select @maxcols = STUFF((SELECT ',MAX(' + QUOTENAME(CodigoFormaPago) + ') as ' + QUOTENAME(CodigoFormaPago)
                from PO_FormasPago
                order by CodigoFormaPago
        FOR XML PATH(''), TYPE
        ).value('.', 'NVARCHAR(MAX)')
    ,1,1,'')

set @query = 'SELECT CodigoProducto, DenominacionProducto, ' + @maxcols + '
            FROM
            (
                SELECT 
                CodigoProducto, DenominacionProducto,
                ' + @cols + ' from 
                 (
                    SELECT 
                        p.CodigoProducto as CodigoProducto,
                        p.DenominacionProducto as DenominacionProducto,
                        fpp.CantidadCuotas as CantidadCuotas,
                        fpp.IdFormaPago as IdFormaPago,
                        fp.CodigoFormaPago as CodigoFormaPago
                    FROM
                        PR_Producto p
                        LEFT JOIN PR_FormasPagoProducto fpp
                            ON fpp.IdProducto = p.IdProducto
                        LEFT JOIN PO_FormasPago fp
                            ON fpp.IdFormaPago = fp.IdFormaPago
                ) xp
                pivot 
                (
                    MAX(CantidadCuotas)
                    for CodigoFormaPago in (' + @cols + ')
                ) p 
            )  xx 
            GROUP BY CodigoProducto, DenominacionProducto'

t @query;

execute(@query);

2

নিচের কোড ফলাফল যা প্রতিস্থাপন উপলব্ধ শূন্য থেকে শূন্য আউটপুটে।

সারণী তৈরি এবং ডেটা সন্নিবেশ:

create table test_table
 (
 date nvarchar(10),
 category char(3),
 amount money
 )

 insert into test_table values ('1/1/2012','ABC',1000.00)
 insert into test_table values ('2/1/2012','DEF',500.00)
 insert into test_table values ('2/1/2012','GHI',800.00)
 insert into test_table values ('2/10/2012','DEF',700.00)
 insert into test_table values ('3/1/2012','ABC',1100.00)

সঠিক ফলাফল উত্পন্ন করার জন্য অনুসন্ধান যা শূন্যের সাথে NUL কে প্রতিস্থাপন করে:

DECLARE @DynamicPivotQuery AS NVARCHAR(MAX),
@PivotColumnNames AS NVARCHAR(MAX),
@PivotSelectColumnNames AS NVARCHAR(MAX)

--Get distinct values of the PIVOT Column
SELECT @PivotColumnNames= ISNULL(@PivotColumnNames + ',','')
+ QUOTENAME(category)
FROM (SELECT DISTINCT category FROM test_table) AS cat

--Get distinct values of the PIVOT Column with isnull
SELECT @PivotSelectColumnNames 
= ISNULL(@PivotSelectColumnNames + ',','')
+ 'ISNULL(' + QUOTENAME(category) + ', 0) AS '
+ QUOTENAME(category)
FROM (SELECT DISTINCT category FROM test_table) AS cat

--Prepare the PIVOT query using the dynamic 
SET @DynamicPivotQuery = 
N'SELECT date, ' + @PivotSelectColumnNames + '
FROM test_table
pivot(sum(amount) for category in (' + @PivotColumnNames + ')) as pvt';

--Execute the Dynamic Pivot Query
EXEC sp_executesql @DynamicPivotQuery

আউটপুট:

এখানে চিত্র বর্ণনা লিখুন

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