Here are the sample data :
CalculationDatePLResult
2014-01-02 100
2014-01-03 200
2014-02-03 300
2014-02-04 400
2014-02-27 500
Here are the expected result (in logical format) :
January February
CalculationDatePLResultCalculationDatePLResult
2014-01-02 100 2014-02-03 300
2014-01-03 200 2014-02-04 400
2014-02-27 500
Here are the expected result (using T-SQL Query) :
Jan-CalculationDateJan-PLResultFeb-CalculationDateFeb-PLResult
2014-01-02 100 2014-02-03 300
2014-01-03 200 2014-02-04 400
2014-02-27 500
Objective:
- Classify the result according to the month. In the above example, the January's results are placed in the January breakdown.
- The number of months can be dynamic. In the above example, it only shows January and February because there are only results for 2 months
- The result will be displayed through Excel. Actually I can query multiple query tables to aggregate the result across different months, but if it's possible to return all the result through one single query, then it will be easier to be maintained and debugged.
Here are the scripts to populate the sample data :
CREATE TABLE #PLResultPerDay ( CalculationDate DATETIME, PLResult DECIMAL(18,8) )
INSERT INTO #PLResultPerDay ( CalculationDate, PLResult ) VALUES ('2014-01-02' , 100 )
INSERT INTO #PLResultPerDay ( CalculationDate, PLResult ) VALUES ('2014-01-03' , 200 )
INSERT INTO #PLResultPerDay ( CalculationDate, PLResult ) VALUES ('2014-02-03' , 300 )
INSERT INTO #PLResultPerDay ( CalculationDate, PLResult ) VALUES ('2014-02-04' , 400 )
So far here is my attempt in building the query :
SELECT
CalculationDate, [January], CalculationDate, [February]
FROM
(
SELECT CalculationDate, PLResult, DATENAME(MONTH, CalculationDate) AS [MTH]
FROM #PLResultPerDay
) x
PIVOT
(
MIN(PLResult)
FOR [MTH] IN ([January], [February])
) p
As has been said this isn't actually possible, the closest you could get is:
January2014CalculationDate | January2014PLResult | February2014CalculationDate | February2014PLResult
---------------------------+---------------------+-----------------------------+------------------
2014-01-02 | 100 | 2014-02-03 | 300
2014-01-03 | 200 | 2014-02-04 | 400
NULL | NULL | 2014-02-27 | 500
And even that is not simple and I would still advise handling formatting like this outside of sql. The first step is to partition the data by month, and then rank the dates in each month:
SELECT CalculationDate,
PLResult,
CalculationMonth,
DenseRank = DENSE_RANK() OVER(PARTITION BY CalculationMonth ORDER BY CalculationDate)
FROM ( SELECT CalculationDate,
PLResult,
CalculationMonth = DATEADD(MONTH, DATEDIFF(MONTH, 0, CalculationDate), 0)
FROM #PLResultPerDay
) pl;
This gives:
CalculationDate PLResult CalculationMonth DenseRank
2014-01-02 100 2014-01-01 1
2014-01-03 200 2014-01-01 2
2014-02-03 300 2014-02-01 1
2014-02-04 400 2014-02-01 2
2014-02-27 500 2014-02-01 3
You can then pivot this data:
WITH Data AS
( SELECT CalculationDate,
PLResult,
CalculationMonth,
DenseRank = DENSE_RANK() OVER(PARTITION BY CalculationMonth ORDER BY CalculationDate)
FROM ( SELECT CalculationDate,
PLResult,
CalculationMonth = DATEADD(MONTH, DATEDIFF(MONTH, 0, CalculationDate), 0)
FROM #PLResultPerDay
) pl
)
SELECT Jan2014CalcDate = MIN(CASE WHEN CalculationMonth = '20140101' THEN CalculationDate END),
Jan2014Result = SUM(CASE WHEN CalculationMonth = '20140101' THEN PLResult END),
Feb2014CalcDate = MIN(CASE WHEN CalculationMonth = '20140201' THEN CalculationDate END),
Feb2014Result = SUM(CASE WHEN CalculationMonth = '20140201' THEN PLResult END)
FROM Data
GROUP BY DenseRank
ORDER BY DenseRank;
This gives:
Jan2014CalcDate Jan2014Result Feb2014CalcDate Feb2014Result
2014-01-02 100 2014-02-03 300
2014-01-03 200 2014-02-04 400
NULL NULL 2014-02-27 500
Then since you have a dynamic number of months you need to build the above statement dynamically and use SP_EXECUTESQL
to run it:
DECLARE @SQL NVARCHAR(MAX) = '';
WITH Months AS
( SELECT M,
ColName = DATENAME(MONTH, M) + DATENAME(YEAR, M),
CharFormat = CONVERT(VARCHAR(8), M, 112)
FROM ( SELECT DISTINCT M = DATEADD(MONTH, DATEDIFF(MONTH, 0, CalculationDate), 0)
FROM #PLResultPerDay
) m
)
SELECT @SQL = 'WITH Data AS
( SELECT CalculationDate,
PLResult,
CalculationMonth,
DenseRank = DENSE_RANK() OVER(PARTITION BY CalculationMonth ORDER BY CalculationDate)
FROM ( SELECT CalculationDate,
PLResult,
CalculationMonth = DATEADD(MONTH, DATEDIFF(MONTH, 0, CalculationDate), 0)
FROM #PLResultPerDay
) pl
)
SELECT ' +
STUFF(( SELECT ', ' + ColName + 'CalculationDate = MIN(CASE WHEN CalculationMonth = ''' + CharFormat + ''' THEN CalculationDate END), ' +
ColName + 'PLResult = SUM(CASE WHEN CalculationMonth = ''' + CharFormat + ''' THEN PLResult END)'
FROM Months
ORDER BY M
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 2, '') +
'FROM Data
GROUP BY DenseRank
ORDER BY DenseRank;';
EXECUTE SP_EXECUTESQL @SQL;
Example on SQL Fiddle
Please note, I still advise against this technique, and think SQL should be left to storing/retrieving data, and the presentation layer for formatting it