نحوه چرخش داده ها با استفاده از Dynamic SQL در SQL Server
SQL Server PIVOT
عملگر زمانی مفید است که همه مقادیری را که باید به ستون تبدیل شوند را بدانید. اما اگر نمی دانید دقیقاً در جدول چیست. چگونه سطرها را به ستون تبدیل کنیم؟
SQL پویا می تواند به آن کمک کند. بیایید آن را در مثال ساده بررسی کنیم. یک میز وجود دارد Goods
. تعداد مواد غذایی و فروشگاه ها می تواند نامحدود باشد. اگر همیشه فقط 3 فروشگاه و 3 ماده غذایی داشت، می توانستیم از استاندارد استفاده کنیم PIVOT
.
فروشگاه | غذا | میزان |
---|---|---|
فروشگاه 1 | سیب زمینی | 50 |
فروشگاه 5 | سیب زمینی | 150 |
فروشگاه 12 | سیب زمینی | 300 |
فروشگاه 1 | گوجه فرنگی | 220 |
فروشگاه 5 | گوجه فرنگی | 180 |
فروشگاه 12 | گوجه فرنگی | 60 |
فروشگاه 1 | خیار | 500 |
فروشگاه 5 | خیار | 10 |
فروشگاه 12 | خیار | 90 |
بنابراین باید مواد غذایی را به ستون تبدیل کنیم. نتیجه باید به این صورت باشد:
فروشگاه | سیب زمینی | گوجه فرنگی | خیار |
---|---|---|---|
فروشگاه 1 | 50 | 220 | 500 |
فروشگاه 5 | 150 | 180 | 10 |
فروشگاه 12 | 30 | 60 | 90 |
بیایید یک جدول ایجاد کنیم و داده های خود را وارد کنیم:
CREATE TABLE Goods([Store] VARCHAR(255), [Food] VARCHAR(255), [Amount] INT);
INSERT INTO Goods(Store, Food, Amount)
VALUES
('Store 1', 'potato', 50),
('Store 5', 'potato', 150),
('Store 12', 'potato', 300),
('Store 1', 'tomato', 220),
('Store 5', 'tomato', 180),
('Store 12', 'tomato', 60),
('Store 1', 'cucumber', 500),
('Store 5', 'cucumber', 10),
('Store 12', 'cucumber', 90);
GO
برای محاسبات به دو جدول موقت نیاز داریم:
/*for turning rows into columns*/
IF OBJECT_ID('tempdb..#tmp') IS NOT NULL
DROP TABLE #tmp
CREATE TABLE #tmp(Store VARCHAR(255), Food VARCHAR(255))
GO
/*for final result*/
IF OBJECT_ID('tempdb..#result') IS NOT NULL
DROP TABLE #result
GO
سه متغیر:
DECLARE @sql NVARCHAR(MAX) = '' /*dynamic string*/
, @columns NVARCHAR(MAX) = STUFF(ISNULL((SELECT DISTINCT ', ['+ Food +'] INT'
FROM Goods
FOR XML PATH('')),''),1,2,'') /*column names for the final result table*/
, @summary NVARCHAR(MAX) = STUFF(ISNULL((SELECT DISTINCT ', SUM(['+ Food +']) AS ['+ Food +']'
FROM Goods
FOR XML PATH('')),''),1,2,'') /*summing amount*/
SELECT @columns AS [columns], @summary AS [summary];
حالا بیایید اقلام غذایی را به ستون تبدیل کنیم و آن را ایمن کنیم #tmp
جدول
INSERT INTO #tmp
SELECT DISTINCT Store,
REPLACE(STUFF(ISNULL((SELECT DISTINCT ', 0 AS ['+ Food +']'
FROM Goods rw
FOR XML PATH('')),''),1,2,''),'0 AS ['+ Food +']', CAST(org.Amount AS VARCHAR) + ' AS ['+org.Food+']')
FROM Goods org;
SELECT * FROM #tmp;
و اکنون باید از ستون Food خود ستون هایی ایجاد کنیم. برای این منظور از SQL پویا استفاده خواهیم کرد.
--create final table according to number of columns from @columns and insert data from #tmp
SET @sql = N'create table #result(food varchar(255),'+@columns+'); '+CHAR(10)+
'insert into #result '+CHAR(10);
SELECT @sql = @sql + N'select '''+Store+''' as Store, '+Food+' union all '+CHAR(10) FROM #tmp;
--remove last 'union all'
SET @sql = REVERSE(STUFF(REVERSE(@sql),1,11,''));
SET @sql = @sql + N'select food, '+@summary+' from #result group by food';
EXEC sp_executesql @sql;
نتیجه ی sp_executesql
رویه خواهد بود
در صورت نیاز به حفظ آن، می توانید یک جدول اضافی ایجاد کنید و داده ها را در آن وارد کنید. مثلا:
SET @sql = @sql + N'create table final_result(food varchar(255),'+@columns+');'+CHAR(10)+
'insert into final_result'+CHAR(10)+
'select food, '+@summary+' from #result group by food';
EXEC sp_executesql @sql;
SELECT* FROM final_result;
همچنین میتوانید هنگام چرخش دادهها، ستونهای اضافی مانند مقدار کل، تعداد کل در ستونها یا ردیفها اضافه کنید. این یک رویکرد کاملا انعطاف پذیر است.
توجه داشته باشید: اگر نسخه SQL شما بالاتر از 2017 باشد، امکان استفاده وجود دارد STRING_AGG()
تابع برای ساده کردن STUFF/XML
بیانیه.
لطفاً اگر از روش دیگری استفاده می کنید، در نظرات زیر به من اطلاع دهید. جالب است که چیز جدیدی را امتحان کنید.