Pivot table with custom values - sql-server

i have table say FIDDLE HERE
+----+------+------+-----+-----+
| id | year | sell | buy | own |
+----+------+------+-----+-----+
| 1 | 2016 | 9 | 2 | 10 |
| 1 | 2017 | 9 | | 10 |
| 1 | 2018 | | 2 | 10 |
| 2 | 2016 | 7 | 2 | 11 |
| 2 | 2017 | 2 | | |
| 2 | 2018 | | | 18 |
+----+------+------+-----+-----+
create table test(id varchar(20), year varchar(20),
sell varchar(20), buy varchar(20),
own varchar(20));
insert into test values('1', '2016','9','2','10' )
insert into test values('1', '2017','9',NULL,'10' )
insert into test values('1', '2018',NULL,'2','10' )
insert into test values('2', '2016','7','2','11' )
insert into test values('2', '2017','2',NULL,'17' )
insert into test values('2', '2018','5','2','18' )
I'm trying to PIVOT but instead of aggregate the values, i wanted to keep some letters if it is not null (S-Sell,B-Buy,O-Own). If there are values for all columns for particular year then i need S_B_O for that year. If there are values only for sell and buy then S_B etc., so Expected output is
+----+-------+------+------+
| ID | 2016 | 2017 | 2018 |
+----+-------+------+------+
| 1 | S_B_O | S_O | B_O |
+----+-------+------+------+
| 2 | S_B_O | S | O |
+----+-------+------+------+
The closest i have got is using conditional aggrgarion( MAX and concat) instead of PIVOT but this is also giving null if any one is NULL. Please suggest a solution.
select ID,
MAX(CASE WHEN Year = '2016' AND sell is not null THEN 'S_' END +
CASE WHEN Year = '2016' AND buy is not null THEN 'B_' END +
CASE WHEN Year = '2016' AND own is not null THEN 'O' END)
AS [2016],
MAX(CASE WHEN Year = '2017' AND sell is not null THEN 'S_' END +
CASE WHEN Year = '2017' AND buy is not null THEN 'B_' END +
CASE WHEN Year = '2017' AND own is not null THEN 'O' END)
AS [2017]
/* ......for all year */
from test
group by id
FIDDLE HERE

You can use CONCAT function, which will handle NULLs automatically.
select ID,
CONCAT(MAX(CASE WHEN Year = '2016' AND sell is not null THEN 'S_' END) ,
MAX(CASE WHEN Year = '2016' AND buy is not null THEN 'B_' END) ,
MAX(CASE WHEN Year = '2016' AND buy is not null THEN 'O' END))
AS [2016],
CONCAT(MAX(CASE WHEN Year = '2017' AND sell is not null THEN 'S_' END) ,
MAX(CASE WHEN Year = '2017' AND buy is not null THEN 'B_' END) ,
MAX(CASE WHEN Year = '2017' AND buy is not null THEN 'O' END))
AS [2017]
from test
group by id
+----+-------+------+
| ID | 2016 | 2017 |
+----+-------+------+
| 1 | S_B_O | S_ |
| 2 | S_B_O | S_ |
+----+-------+------+
UPDATE Dynamic query. As #Larnu told, You should have asked this as separate question. You should not change the requirement.
DECLARE #lst_Years NVARCHAR(MAX) , #query NVARCHAR(MAX)
SET #lst_Years = STUFF((SELECT distinct ',' + QUOTENAME([Year])
FROM test
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = 'SELECT * FROM
(
select ID, [Year],
CONCAT(MAX(CASE WHEN sell is not null THEN ''S_'' END) ,
MAX(CASE WHEN buy is not null THEN ''B_'' END) ,
MAX(CASE WHEN buy is not null THEN ''O'' END))
AS [Value]
from test
group by id, [year]) as t
pivot
(
max(value) FOR YEAR IN (' + #lst_Years + ')
) as pvt'
EXEC(#query)
+----+-------+------+-------+
| ID | 2016 | 2017 | 2018 |
+----+-------+------+-------+
| 1 | S_B_O | S_ | B_O |
| 2 | S_B_O | S_ | S_B_O |
+----+-------+------+-------+

Related

Include rows with 0s or NULLs in the SQL query output

I am querying data (multiple columns) for different item types through a UNION of 2 different queries. If there are no values in any of those columns for a particular item type, that record does not show up. But, I need all rows (including empty ones) pertaining to each item type. The empty rows can show 0.
My data is:
create table sales_table ([yr] int, [qtr] varchar(40), [item_type] varchar(40), [sale_price] int);
create table profit_table ([yr] int, [qtr] varchar(40), [item_type] varchar(40), [profit] int);
create table item_table ([item_type] varchar(40));
insert into sales_table values
(2010,'Q1','abc',31),(2010,'Q1','def',23),(2010,'Q1','mno',12),(2010,'Q1','xyz',7),(2010,'Q2','abc',54),(2010,'Q2','def',67),(2010,'Q2','mno',92),(2010,'Q2','xyz',8);
insert into profit_table values
(2010,'Q1','abc',10),(2010,'Q1','def',6),(2010,'Q1','mno',23),(2010,'Q1','xyz',7),(2010,'Q2','abc',21),(2010,'Q2','def',13),(2010,'Q2','mno',15),(2010,'Q2','xyz',2);
insert into item_table values
('abc'),('def'),('ghi'),('jkl'),('mno'),('xyz');
My Query is:
SELECT a.yr, a.qtr, b.item_type, MAX(a.sales), MAX(a.avg_price), MAX(a.profit)
FROM
(SELECT [yr], [qtr],
CASE WHEN item_type IN ('mno', 'xyz') THEN 'Other' else upper(item_type) END AS [item_type],
COUNT(sale_price) OVER (PARTITION BY yr, qtr, item_type) [sales],
AVG(sale_price) OVER (PARTITION BY yr, qtr, item_type) [avg_price],
NULL [profit]
FROM sales_table
WHERE yr >=2010
UNION ALL
SELECT yr, qtr,
CASE WHEN item_type IN ('mno', 'xyz') THEN 'Other' else upper(item_type) END AS [item_type],
NULL [sales],
NULL [avg_price],
SUM(profit) OVER (PARTITION BY yr, qtr, item_type) [profit]
FROM profit_table
WHERE yr >=2010
) a
FULL OUTER JOIN
(SELECT
CASE WHEN item_type IN ('mno', 'xyz') THEN 'Other' else upper(item_type) END AS [item_type]
FROM item_table
WHERE item_type in ('abc','def','ghi','jkl','mno','xyz')
) b
ON a.item_type = b.item_type
GROUP BY a.yr, a.qtr, b.item_type
ORDER BY a.yr, a.qtr, b.item_type;
The current output is like this:
yr qtr item_type sales avg_price profit
(null) (null) GHI (null) (null) (null)
(null) (null) JKL (null) (null) (null)
2010 Q1 ABC 1 31 10
2010 Q1 DEF 1 23 6
2010 Q1 Other 1 12 23
2010 Q2 ABC 1 54 21
2010 Q2 DEF 1 67 13
2010 Q2 Other 1 92 15
What I want is like as shown below.
yr qtr item_type sales avg_price profit
2010 Q1 ABC 1 31 10
2010 Q1 DEF 1 23 6
2010 Q1 GHI 0 0 0
2010 Q1 JKL 0 0 0
2010 Q1 Other 2 9.5 30
2010 Q2 ABC 1 54 21
2010 Q2 DEF 1 67 13
2010 Q2 GHI 0 0 0
2010 Q2 JKL 0 0 0
2010 Q2 Other 2 50 17
Please advise.
Here you go. Just the way it's described in the comments.
I made a mini calendar table, but you'll want to spend some time making a real one. Once you have it, you'll use it all the time.
if OBJECT_ID('tempdb..#sales_table', 'U') is not null
drop table #sales_table
if OBJECT_ID('tempdb..#profit_table', 'U') is not null
drop table #profit_table
if OBJECT_ID('tempdb..#item_table', 'U') is not null
drop table #item_table
if OBJECT_ID('tempdb..#date_table', 'U') is not null
drop table #date_table
create table #sales_table
(
[yr] int
, [qtr] varchar(40)
, [item_type] varchar(40)
, [sale_price] int
);
create table #profit_table
(
[yr] int
, [qtr] varchar(40)
, [item_type] varchar(40)
, [profit] int
);
create table #item_table
(
[item_type] varchar(40)
);
create table #date_table
(
[yr] int
, [qtr] varchar(2)
);
insert into #sales_table values
(2010,'Q1','abc',31)
,(2010,'Q1','def',23)
,(2010,'Q1','mno',12)
,(2010,'Q1','xyz',7)
,(2010,'Q2','abc',54)
,(2010,'Q2','def',67)
,(2010,'Q2','mno',92)
,(2010,'Q2','xyz',8);
insert into #profit_table values
(2010,'Q1','abc',10)
,(2010,'Q1','def',6)
,(2010,'Q1','mno',23)
,(2010,'Q1','xyz',7)
,(2010,'Q2','abc',21)
,(2010,'Q2','def',13)
,(2010,'Q2','mno',15)
,(2010,'Q2','xyz',2);
insert into #item_table values
('abc'),('def'),('ghi'),('jkl'),('mno'),('xyz');
insert into #date_table values
(2010,'Q1'),(2010,'Q2'), (2010,'Q3'),(2010,'Q4');
SELECT
b.yr
, b.qtr
, b.item_type
, COALESCE(MAX(a.sales),0) AS sales
, COALESCE(MAX(a.avg_price),0) AS avg_price
, COALESCE(MAX(a.profit),0) AS profit
FROM
(
SELECT
dt.[yr]
,dt.[qtr]
,CASE
WHEN it.[item_type] IN ('mno', 'xyz') THEN 'Other'
ELSE UPPER(it.[item_type])
END AS [item_type]
FROM
#date_table AS dt
CROSS JOIN
#item_table AS it
WHERE
dt.[yr] >=2010
GROUP BY
dt.[yr]
,dt.[qtr]
,CASE
WHEN it.[item_type] IN ('mno', 'xyz') THEN 'Other'
ELSE UPPER(it.[item_type])
END
) AS b
LEFT JOIN
(SELECT [yr], [qtr],
CASE
WHEN item_type IN ('mno', 'xyz') THEN 'Other'
ELSE UPPER([item_type])
END AS [item_type],
COUNT(sale_price) OVER (PARTITION BY yr, qtr, item_type) [sales],
AVG(sale_price) OVER (PARTITION BY yr, qtr, item_type) [avg_price],
NULL [profit]
FROM #sales_table
WHERE yr >=2010
UNION ALL
SELECT yr, qtr,
CASE
WHEN item_type IN ('mno', 'xyz') THEN 'Other'
ELSE UPPER([item_type])
END AS [item_type],
NULL [sales],
NULL [avg_price],
SUM(profit) OVER (PARTITION BY yr, qtr, item_type) [profit]
FROM #profit_table
WHERE yr >=2010
) a
ON
a.[yr] = b.[yr]
AND
a.[qtr] = b.[qtr]
AND
a.[item_type] = b.[item_type]
GROUP BY
b.yr, b.qtr, b.item_type
ORDER BY b.yr, b.qtr, b.item_type;
Results:
+------+-----+-----------+-------+-----------+--------+
| yr | qtr | item_type | sales | avg_price | profit |
+------+-----+-----------+-------+-----------+--------+
| 2010 | Q1 | ABC | 1 | 31 | 10 |
| 2010 | Q1 | DEF | 1 | 23 | 6 |
| 2010 | Q1 | GHI | 0 | 0 | 0 |
| 2010 | Q1 | JKL | 0 | 0 | 0 |
| 2010 | Q1 | Other | 1 | 12 | 23 |
| 2010 | Q2 | ABC | 1 | 54 | 21 |
| 2010 | Q2 | DEF | 1 | 67 | 13 |
| 2010 | Q2 | GHI | 0 | 0 | 0 |
| 2010 | Q2 | JKL | 0 | 0 | 0 |
| 2010 | Q2 | Other | 1 | 92 | 15 |
| 2010 | Q3 | ABC | 0 | 0 | 0 |
| 2010 | Q3 | DEF | 0 | 0 | 0 |
| 2010 | Q3 | GHI | 0 | 0 | 0 |
| 2010 | Q3 | JKL | 0 | 0 | 0 |
| 2010 | Q3 | Other | 0 | 0 | 0 |
| 2010 | Q4 | ABC | 0 | 0 | 0 |
| 2010 | Q4 | DEF | 0 | 0 | 0 |
| 2010 | Q4 | GHI | 0 | 0 | 0 |
| 2010 | Q4 | JKL | 0 | 0 | 0 |
| 2010 | Q4 | Other | 0 | 0 | 0 |
+------+-----+-----------+-------+-----------+--------+

SQL server Query for SUM group

I have table:
-------------------------------------
| Code | Name | Type | Qty |
-------------------------------------
| 0001 | Mouse | Purchase | 5 |
| 0002 | KeyBrd | Purchase | 8 |
| 0003 | Mouse | Sale | 2 |
-------------------------------------
i want to select them like this:
------------------------------------------------------------
| Code | Name | Total Purchase | Total Sale | Stock |
------------------------------------------------------------
| 0001 | Mouse | 5 | 2 | 3 |
| 0002 | KeyBrd | 8 | 0 | 8 |
------------------------------------------------------------
Im so confused, please help me with the query, thank you in advance
Try This
SELECT
Code,
Name,
TotalPurchase = SUM(CASE WHEN [Type] = 'Purchase' THEN Qty ELSE 0 END),
TotalSale = SUM(CASE WHEN [Type] = 'Sale' THEN Qty ELSE 0 END),
Stock = SUM(CASE WHEN [Type] = 'Purchase' THEN Qty ELSE 0 END) - SUM(CASE WHEN [Type] = 'Sale' THEN Qty ELSE 0 END)
FROM YourTable
group by Code,
Name
Conditional aggregation with the help of case expression is one approach
select min(Code) Code,
Name,
sum(case when Type = 'Purchase' then Qty else 0 end) [Total Purchase],
sum(case when Type = 'Sale' then Qty else 0 end) [Total Sale],
sum(case when Type = 'Purchase' then -Qty else Qty end) Stock
from table t
group by Name
We can also use like below.(Using Pivot) Please try this.
Data Generation
CREATE TABLE TableCase
(
Code VARCHAR(10)
,[Name] VARCHAR(10)
,[Type] VARCHAR(20)
,Qty INT
)
GO
INSERT INTO TableCase VALUES
('0001','Mouse' ,'Purchase',5),
('0002','KeyBrd' ,'Purchase',8),
('0003','Mouse' ,'Sale',2 )
GO
SOLUTION
SELECT MIN(Code) Code,[Name]
, ISNULL(SUM([Purchase]),0) [TotalPurchase]
, ISNULL(SUM([Sale]),0) TotalSale
, ISNULL(SUM([Purchase]),0) - ISNULL(SUM([Sale]),0) Stock
FROM TableCase
PIVOT
(
MAX(Qty) FOR [Type] IN ([Purchase],[Sale])
)t
GROUP By [Name]
ORDER BY [code]
OUTPUT
Code Name TotalPurchase TotalSale Stock
---------- ---------- ------------- ----------- -----------
0001 Mouse 5 2 3
0002 KeyBrd 8 0 8
(2 rows affected)

how to subtract values in consecutive rows?

I have following table with some data.
CREATE TABLE #NetProfit (ID int, [Name] varchar(50),[Class] varchar(50), Balance money)
go
--Populate Sample records
INSERT INTO #NetProfit VALUES(4,'Income','No Class',303386.8462)
INSERT INTO #NetProfit VALUES(6,'Expenses','No Class',22443.5317)
INSERT INTO #NetProfit VALUES(4,'Income','2 TestUser3',0.00)
INSERT INTO #NetProfit VALUES(5,'Cost','2 TestUser3',0.3875)
INSERT INTO #NetProfit VALUES(6,'Expenses','2 TestUser3',6439.2129)
INSERT INTO #NetProfit VALUES(5,'Cost','3 TestUser3',0.1395)
INSERT INTO #NetProfit VALUES(6,'Expenses','3 TestUser3',6451.6129)
INSERT INTO #NetProfit VALUES(5,'Cost','38 Code#1012',3.0225)
INSERT INTO #NetProfit VALUES(6,'Expenses','38 Code#1012',30.225)
go
select * from #NetProfit
drop table #NetProfit
+----+----------+--------------+-------------+
| ID | Name | Class | Balance |
+----+----------+--------------+-------------+
| 4 | Income | No Class | 303386.8462 |
| 6 | Expenses | No Class | 22443.5317 |
| 4 | Income | 2 TestUser3 | 0 |
| 5 | Cost | 2 TestUser3 | 0.3875 |
| 6 | Expenses | 2 TestUser3 | 6439.2129 |
| 5 | Cost | 3 TestUser3 | 0.1395 |
| 6 | Expenses | 3 TestUser3 | 6451.6129 |
| 5 | Cost | 38 Code#1012 | 3.0225 |
| 6 | Expenses | 38 Code#1012 | 30.225 |
+----+----------+--------------+-------------+
I want to subtract [Balance] column row wise group by [Class] column.
For ex. NetProfit = (Income - Cost - Expenses) for each [Class] column with group by [Class].
Here is the output I am expecting.
+-------------+-------------+-------------+--------------+
| No Class | 2 TestUser3 | 3 TestUser3 | 38 Code#1012 |
+-------------+-------------+-------------+--------------+
| 280943.3145 | 6439.6004 | 6451.7524 | 33.2475 |
+-------------+-------------+-------------+--------------+
Any help would be appreciated. Thanks!
This could be the first step to achieve desired result:
SELECT NP.Class, ABS(ISNULL(SUM(CASE NP.Name WHEN 'Income' THEN NP.Balance END), 0) - SUM(CASE NP.Name WHEN 'Income' THEN NULL ELSE NP.Balance END)) AS Balance
FROM #NetProfit AS NP
GROUP BY NP.Class;
Then you would have to dynamically pivot these results. Statically, your query should look like that:
SELECT *
FROM (
SELECT NP.Class, ABS(ISNULL(SUM(CASE NP.Name WHEN 'Income' THEN NP.Balance END), 0) - SUM(CASE NP.Name WHEN 'Income' THEN NULL ELSE NP.Balance END)) AS Balance
FROM #NetProfit AS NP
GROUP BY NP.Class) AS SourceTable
PIVOT (MAX(Balance) FOR Class IN ([2 TestUser3], [3 TestUser3], [38 Code#1012], [No Class])) AS PivotTable;
So by plugging in dynamic SQL, This is the result:
DECLARE #SQL NVARCHAR(MAX);
DECLARE #Col NVARCHAR(MAX);
SET #Col = STUFF(( SELECT DISTINCT ',' + QUOTENAME(NP.Class)
FROM #NetProfit AS NP
FOR XML PATH('')), 1, 1, '');
SET #SQL = N'
SELECT *
FROM (
SELECT NP.Class, ABS(ISNULL(SUM(CASE NP.Name WHEN ''Income'' THEN NP.Balance END), 0) - SUM(CASE NP.Name WHEN ''Income'' THEN NULL ELSE NP.Balance END)) AS Balance
FROM #NetProfit AS NP
GROUP BY NP.Class) AS SourceTable
PIVOT (MAX(Balance) FOR Class IN (' + #Col + N')) AS PivotTable';
EXECUTE sys.sp_executesql #SQL;
That's the output:
+-------------+-------------+--------------+-------------+
| 2 TestUser3 | 3 TestUser3 | 38 Code#1012 | No Class |
+-------------+-------------+--------------+-------------+
| 6439,6004 | 6451,7524 | 33,2475 | 280943,3145 |
+-------------+-------------+--------------+-------------+
This query return calculation in rows.
With pivot table you can turn rows to columns
select isnull(c1, c2), isnull(i, 0)-isnull(e,0) from (
select * from
(select class c1, sum(balance) i
from #NetProfit
where Name = 'income'
group by class) i
full join
(
select class c2, sum(balance) e
from #NetProfit
where Name != 'income'
group by class) e on i.c1 = e.c2) t
Try this (assuming ID 4, for income, is a positive whereas the rest should be treated as negatives;
select
[Class No]=SUM(case when [Class]='No Class' then (case when ID=4 then Balance else Balance*-1 end) else 0 end),
[TestUser2]=SUM(case when [Class]='2 TestUser3' then (case when ID=4 then Balance else Balance*-1 end) else 0 end),
[TestUser3]=SUM(case when [Class]='3 TestUser3' then (case when ID=4 then Balance else Balance*-1 end) else 0 end),
[Code#1012]=SUM(case when [Class]='Code#1012' then (case when ID=4 then Balance else Balance*-1 end) else 0 end)
from
#NetProfit

How to convert table columns to rows [duplicate]

This question already has answers here:
Convert Rows to columns using 'Pivot' in SQL Server
(9 answers)
Closed 6 years ago.
I have a table like below:
+-----------+------------+--------+
| Col1 | Col2 | Col3 |
+-----------+------------+--------+
| 12345678 | FirstName | John |
| 12345678 | LastName | Smith |
| 987456321 | LastName | Clancy |
| 987456321 | MiddleName | T |
| 987456321 | Height | 176cm |
| 654125878 | FirstName | Tan |
| 654125878 | Weight | 150lb |
+-----------+------------+--------+
How to convert that to:
+-----------+-----------+------------+----------+--------+--------+
| ID | FirstName | MiddleName | LastName | Height | Weight |
+-----------+-----------+------------+----------+--------+--------+
| 12345678 | John | null | Smith | null | null |
| 987456321 | null | T | Clancy | 176cm | null |
| 654125878 | Tan | null | null | null | 150lb |
+-----------+-----------+------------+----------+--------+--------+
I think a conditional aggregation would do the trick here.
Assuming you don't need dynamic
Select ID = Col1
,FirstName = max(case when Col2='FirstName' then Col3 else null end)
,MiddleName = max(case when Col2='MiddleName ' then Col3 else null end)
,LastName = max(case when Col2='LastName ' then Col3 else null end)
,Height = max(case when Col2='Height' then Col3 else null end)
,Weight = max(case when Col2='Weight' then Col3 else null end)
From YourTable
Group By Col1
If you need dynamic
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName([Col2]) From Yourtable Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select [Col1] as ID ,' + #SQL + '
From YourTable
Pivot (max(Col3) For [Col2] in (' + #SQL + ') ) p'
Exec(#SQL);
If it helps, the generated SQL for the dynamic Pivot is as follows:
Select [Col1] as ID ,[FirstName],[Height],[LastName],[MiddleName],[Weight]
From YourTable
Pivot (max(Col3) For [Col2] in ([FirstName],[Height],[LastName],[MiddleName],[Weight]) ) p

Unpivotting multiple columns - substring of column name as a new column with CROSS APPLY

I have a table with the following format
YEAR, MONTH, ITEM, REQ_QTY1, REQ_QTY2 , ....REQ_QTY31 ,CONVERTED1, CONVERTED2 ....CONVERTED31
Where the suffix of each column is the day of the month.
I need to convert it to the following format, where Day_of_month is the numeric suffix of each column
YEAR, MONTH, DAY_OF_MONTH, ITEM, REQ_QTY, CONVERTED
I thought of using CROSS APPLY to retrieve the data, but I can't use CROSS APPLY to get the "Day of Month"
SELECT A.YEAR, A.MONTH, A.ITEM, B.REQ_QTY, B.CONVERTED
FROM TEST A
CROSS APPLY
(VALUES
(REQ_QTY1, CONVERTED1),
(REQ_QTY2, CONVERTED2),
(REQ_QTY3, CONVERTED3),
......
(REQ_QTY31, CONVERTED31)
)B (REQ_QTY, CONVERTED)
The only way I found is to use a nested select with inner join
SELECT A.YEAR, A.MONTH, A.DAY_OF_MONTH, A.ITEM,A.REQ_QTY, D.CONVERTED FROM
(SELECT YEAR, MONTH, ITEM, SUBSTRING(DAY_OF_MONTH,8,2) AS DAY_OF_MONTH, REQ_QTY FROM TEST
UNPIVOT
(REQ_QTY FOR DAY_OF_MONTH IN ([REQ_QTY1],[REQ_QTY2],[REQ_QTY3],......[REQ_QTY30],[REQ_QTY31])
) B
) A
INNER JOIN (SELECT YEAR, MONTH, ITEM, SUBSTRING(DAY_OF_MONTH,10,2) AS DAY_OF_MONTH, CONVERTED FROM TEST
UNPIVOT
(CONVERTED FOR DAY_OF_MONTH IN ([CONVERTED1],[CONVERTED2],[CONVERTED3],....[CONVERTED30],[CONVERTED31])
) C
) D
ON D.YEAR = A.YEAR AND D.MONTH = A.MONTH AND D.ITEM = A.ITEM AND D.DAY_OF_MONTH = A.DAY_OF_MONTH
Is there a way to use CROSS APPLY and yet get the DAY_OF_MONTH out?
This is not a solution with CROSS APPLY but it will definitely make it a bit faster as it uses a bit simpler approach and simpler execution plan.
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE Test_Table([YEAR] INT, [MONTH] INT, [ITEM] INT, REQ_QTY1 INT
, REQ_QTY2 INT ,REQ_QTY3 INT , CONVERTED1 INT, CONVERTED2 INT, CONVERTED3 INT)
INSERT INTO Test_Table VALUES
( 2015 , 1 , 1 , 10 , 20 , 30 , 100 , 200 , 300),
( 2015 , 2 , 1 , 10 , 20 , 30 , 100 , 200 , 300),
( 2015 , 3 , 1 , 10 , 20 , 30 , 100 , 200 , 300)
Query 1:
SELECT *
FROM
(
SELECT [YEAR]
,[MONTH]
,ITEM
,Vals
,CASE WHEN LEFT(N,3) = 'REQ' THEN SUBSTRING(N,8 ,2)
WHEN LEFT(N,3) = 'CON' THEN SUBSTRING(N,10,2)
END AS Day_Of_Month
,CASE WHEN LEFT(N,3) = 'REQ' THEN LEFT(N,7)
WHEN LEFT(N,3) = 'CON' THEN LEFT(N,9)
END AS Tran_Type
FROM Test_Table t
UNPIVOT (Vals FOR N IN ([REQ_QTY1],[REQ_QTY2],[REQ_QTY3],
[CONVERTED1],[CONVERTED2],[CONVERTED3]))up
)t2
PIVOT (SUM(Vals)
FOR Tran_Type
IN (REQ_QTY, CONVERTED))p
Results:
| YEAR | MONTH | ITEM | Day_Of_Month | REQ_QTY | CONVERTED |
|------|-------|------|--------------|---------|-----------|
| 2015 | 1 | 1 | 1 | 10 | 100 |
| 2015 | 1 | 1 | 2 | 20 | 200 |
| 2015 | 1 | 1 | 3 | 30 | 300 |
| 2015 | 2 | 1 | 1 | 10 | 100 |
| 2015 | 2 | 1 | 2 | 20 | 200 |
| 2015 | 2 | 1 | 3 | 30 | 300 |
| 2015 | 3 | 1 | 1 | 10 | 100 |
| 2015 | 3 | 1 | 2 | 20 | 200 |
| 2015 | 3 | 1 | 3 | 30 | 300 |
Well, I found a way using CROSS APPLY, but instead of taking a substring, I'm basically hardcoding the days. Works well enough so...
SELECT A.YEAR, A.MONTH, A.ITEM, B.DAY_OF_MONTH, B.REQ_QTY, B.CONVERTED
FROM TEST A
CROSS APPLY
(
VALUES
('01', REQ_QTY1, CONVERTED1),
('02', REQ_QTY2, CONVERTED2),
('03', REQ_QTY3, CONVERTED3),
('04', REQ_QTY4, CONVERTED4),
......
('31', REQ_QTY31, CONVERTED31)
) B (DAY_OF_MONTH, REQ_QTY, CONVERTED)

Resources