Fetching Records from multiple tables month wise - database

I am having scenario like this : I am having many tables and each table has date fields.
Table: FA
Id | Created_Date |
------------------------
1 | 1/12/2012 |
2 | 2/15/2012 |
3 | 2/25/2012 |
Table: TPA
Id | Created_Date |
------------------------
1 | 3/10/2012 |
2 | 4/25/2012 |
3 | 5/20/2012 |
4 | 5/21/2012 |
Table: Gift
Id | Created_Date |
------------------------
1 | 6/10/2012 |
2 | 7/25/2012 |
3 | 8/10/2012 |
3 | 7/11/2012 |
I want output like I want total count of records from each table and display it respective to month wise by using Created_Date field dates.
Expected Output like this:

Give this solution a try,
SELECT MonthName,
COALESCE(FA, 0) FA,
COALESCE(TPA, 0) TPA,
COALESCE(GIFT, 0) GIFT
FROM
(
SELECT monthList.ordby,
monthList.MonthName,
org.TableName,
org.TotalCount
FROM
(
SELECT 1 ordby, 'January' MonthName UNION SELECT 2, 'February' UNION
SELECT 3, 'March' UNION SELECT 4, 'April' UNION
SELECT 5,'May' UNION SELECT 6,'June' UNION
SELECT 7,'July' UNION SELECT 8,'August' UNION
SELECT 9,'September' UNION SELECT 10,'October' UNION
SELECT 11,'November' UNION SELECT 12,'December'
) monthList
LEFT JOIN
(
SELECT 'FA' TableName,
DATENAME(mm,Created_Date) MonthName,
COUNT(*) TotalCount
FROM FA
GROUP BY DATENAME(mm,Created_Date)
UNION
SELECT 'TPA' TableName,
DATENAME(mm,Created_Date) MonthName,
COUNT(*) TotalCount
FROM TPA
GROUP BY DATENAME(mm,Created_Date)
UNION
SELECT 'Gift' TableName,
DATENAME(mm,Created_Date) MonthName,
COUNT(*) TotalCount
FROM Gift
GROUP BY DATENAME(mm,Created_Date)
) org ON monthList.MonthName = org.MonthName
) data
PIVOT
(
MAX(TotalCount)
FOR TableName IN ([FA], [TPA],[GIFT])
) head
ORDER BY ordby
http://www.sqlfiddle.com/#!3/dc613/14

I was trying to get the Find the solution since last 4 Hours
I think my answer is Low in Performance as Compare to above Answer,
create table #month
(
id [int] IDENTITY(1,1) NOT NULL,
[month] varchar(3)
)
DBCC CHECKIDENT(#month, RESEED, 1)
insert into #month
values
('Jan'),
('Feb'),
('Mar'),
('Apr'),
('May'),
('Jun'),
('Jul'),
('Aug'),
('Sep'),
('Oct'),
('Nov'),
('Dec')
declare #id int
set #id=1
declare #month varchar(3)
declare #sql nvarchar(max)
set #sql=''
declare #order varchar(3)
while (#id<13)
BEGIN
set #month=(select [month] from #month where id=#id)
set #order=CONVERT(varchar(3),#id)
set #sql=#sql+'insert into #display
(
[Month],
[COUNT(FA)],
[COUNT(TPA)],
[COUNT(Gift)],
[ORDER]
)
select '''+ #month +''' AS [Month],
(select
COUNT(CONVERT(VARCHAR(3), DATENAME(MM,Created_Date), 100))
from FA
where CONVERT(VARCHAR(3), DATENAME(MM,Created_Date), 100)='''+ #month +''') [COUNT(FA)],
(select
COUNT(CONVERT(VARCHAR(3), DATENAME(MM,Created_Date), 100))
from TPA
where CONVERT(VARCHAR(3), DATENAME(MM,Created_Date), 100)='''+ #month +''') [COUNT(TPA)],
(select
COUNT(CONVERT(VARCHAR(3), DATENAME(MM,Created_Date), 100))
from Gift
where CONVERT(VARCHAR(3), DATENAME(MM,Created_Date), 100)='''+ #month +''') [COUNT(Gift)],
'''+ #order + ''' [Order]
'
set #id=#id+1 --Incrementing the month
END
exec sp_executesql #sql
print #sql
select [MONTH],
[COUNT(FA)],
[COUNT(TPA)],
[COUNT(Gift)]
from #display
order by convert(int,[ORDER])

Related

Pivoting table in SQL

i have a problem with approach how to pivot this table:
How can i convert this:
Would you be kind enough give me hints (not whole code) how to do this? i really dont know where should i start (i dont know whether it is typical pivoting unpivoting)
+------+------+--------+----------+-----------+-----+
| ColI | Col2 | Month | Turnover | Provision | Fee |
+------+------+--------+----------+-----------+-----+
| 123 | Asdf | 201810 | 10000 | 100 | 0,1 |
| 123 | Asdf | 201811 | 20000 | 200 | 0,2 |
| 123 | Asdf | 201812 | 30000 | 300 | 0,3 |
+------+------+--------+----------+-----------+-----+
into this:
+------+------+---------------+-----------------+------------+---------------+-----------------+------------+---------------+-----------------+-----------+
| ColI | Col2 | Turnover20810 | Provision201810 | Fee201810 | Turnover20811 | Provision201811 | Fee201811 | Turnover20812 | Provision201812 | Fee201812 |
+------+------+---------------+-----------------+------------+---------------+-----------------+------------+---------------+-----------------+-----------+
| 123 | Asdf | 10000 | 100 | 0,1 | 20000 | 200 | 0,2 | 30000 | 300 | 0,3 |
+------+------+---------------+-----------------+------------+---------------+-----------------+------------+---------------+-----------------+-----------+
Thank you!
Assuming that each ColI, Col2 group would only have a maximum of three records, then we can try pivoting using the help of ROW_NUMBER:
WITH cte AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY ColI, Col2 ORDER BY Month) rn
FROM yourTable
)
SELECT
ColI,
Col2,
MAX(CASE WHEN rn = 1 THEN Turnover END) AS Turnover1,
MAX(CASE WHEN rn = 1 THEN Provision END) AS Provision1,
MAX(CASE WHEN rn = 1 THEN Fee END) AS Fee1,
MAX(CASE WHEN rn = 2 THEN Turnover END) AS Turnover2,
MAX(CASE WHEN rn = 2 THEN Provision END) AS Provision2,
MAX(CASE WHEN rn = 2 THEN Fee END) AS Fee2,
MAX(CASE WHEN rn = 3 THEN Turnover END) AS Turnover3,
MAX(CASE WHEN rn = 3 THEN Provision END) AS Provision3,
MAX(CASE WHEN rn = 3 THEN Fee END) AS Fee3
FROM cte
GROUP BY
ColI,
Col2;
Note that I did not hardwire more specific column names, to keep the query as general as possible. For example, perhaps there might be another ColI, Col2 group which would have a different three months.
By using Dynamic Sql
IF OBJECT_ID('tempdb..#TEMP') IS NOT NULL
DROP TABLE #TEMP
;WITH CTE (ColI , Col2 , Month , Turnover , Provision , Fee )
AS
(
SELECT 123 , 'Asdf' , 201810 , 10000 ,100 ,'0,1' UNION ALL
SELECT 123 , 'Asdf' , 201811 , 20000 ,200 , '0,2' UNION ALL
SELECT 123 , 'Asdf' , 201812 , 30000 ,300 , '0,3'
)
SELECT ColI , Col2,Turnover , Provision , Fee,MixedCol,Reqcol , ROW_NUMBER()OVER(ORDER BY (SELECT NULL)) AS Seq
INTO #Temp
FROM CTE
CROSS APPLY (VALUES (CONCAT('Turnover','_',[Month]),CAST(Turnover AS VARCHAR(20))),
(CONCAT('Provision','_',[Month]),CAST(Provision AS VARCHAR(20))),
(CONCAT('Fee','_',[Month]),CAST(Fee AS VARCHAR(20)))
)DT (MixedCol,Reqcol)
DECLARE #Sql nvarchar(max),
#DynamicColumn nvarchar(max),
#MaxDynamicColumn nvarchar(max)
SELECT #DynamicColumn = STUFF((SELECT ', '+QUOTENAME(CAST(MixedCol AS VARCHAR(100)))
FROM #TEMP ORDER BY Seq FOR XML PATH ('')),1,1,'')
SELECT #MaxDynamicColumn = STUFF((SELECT ', '+'MAX('+QUOTENAME(CAST(MixedCol AS VARCHAR(100)))+') AS '+QUOTENAME(CAST(MixedCol AS VARCHAR(100)))
FROM #TEMP ORDER BY Seq FOR XML PATH ('')),1,1,'')
SET #Sql=' SELECT ColI , Col2,'+ #MaxDynamicColumn+'
FROM
(
SELECT * FROM #TEMP
)AS src
PIVOT
(
MAX(Reqcol) FOR [MixedCol] IN ('+#DynamicColumn+')
) AS Pvt
GROUP BY ColI , Col2 '
EXEC (#Sql)
PRINT #Sql
Q: "...give me hints (not whole code) how to do this"
A: This example with dynamic transpose of table. You can try this. Unpivot your original table and transpose.
CREATE table #tbl (
color varchar(10), Paul int, John int, Tim int, Eric int);
insert #tbl select
'Red' ,1 ,5 ,1 ,3 union all select
'Green' ,8 ,4 ,3 ,5 union all select
'Blue' ,2 ,2 ,9 ,1;
select * FROM #tbl
--1) Transpose. Example without dynamic code. You create list of fields in query
select *
from #tbl
unpivot (value for name in ([Paul],[John],[Tim],[Eric])) up
pivot (max(value) for color in ([Blue],[Green],[Red])) p
--2) Transpose. Example with dynamic code, without names of fields.
DECLARE #cols NVARCHAR(MAX), #query NVARCHAR(MAX), #rows NVARCHAR(MAX)='';
-- XML with all values
SET #cols = STUFF(
(
SELECT DISTINCT
','+QUOTENAME(T.Color) -- Name of first column
FROM #tbl T FOR XML PATH(''), TYPE
).value('.', 'nvarchar(max)'), 1, 1, '');
SELECT #rows=#rows+','+QUOTENAME(Name)
FROM
(SELECT Name,ROW_NUMBER() OVER(ORDER BY column_id) AS 'RowNum'
FROM tempdb.sys.Columns
WHERE Object_ID = OBJECT_ID('tempdb..#tbl')
) AS A WHERE A.RowNum>1
SET #rows=STUFF(#rows,1,1,'')
SET #query='SELECT *
from #tbl
unpivot (value for name in ('+#rows+')) up
pivot (max(value) for color in ('+#Cols+')) p'
EXEC (#query)

Show ordered Articles per User

I have the following:
customerID | OrderID | OrderPosition | Articlenumber|
-----------+---------+---------------+--------------+
101 | 1 | 1 | 123 |
101 | 1 | 2 | 799 |
102 | 2 | 1 | 111 |
103 | 3 | 1 | 456 |
101 | 4 | 1 | 789 |
103 | 5 | 1 | 444 |
103 | 5 | 2 | 999 |
101 | 6 | 1 | 555 |
101 | 6 | 2 | 777 |
101 | 6 | 3 | 222 |
and I want the following (sum up max 4 articles (ordered by orderID (higher first) and OrderPosition (lower first)) per customerID):
customerID |articelnumber 1|articelnumber 2|articelnumber 3|articelnumber 4|
-------+-------------------+---------------+---------------+---------------+
101 |555 |777 |222 | 789 |
102 |111 |NULL |NULL |NULL |
103 |444 |999 |456 |NULL |
Tried it like this this, but this is not working correct:
select *
from
(
select customerid, OrderID , OrderPosition, Articlenumber FROM table
) src
pivot
(
avg (Articlenumber)
for OrderPosition in ([articelnumber 1], [articelnumber 2], [articelnumber 3], [articelnumber 4])
) piv
It took me a while to get to the same results as your expected output, but I managed to get it right eventually:
if exists (select 1 from sysobjects where name = 'TempTable')
drop table TempTable;
if exists (select 1 from sysobjects where name = 'TempTable2')
drop table TempTable2;
declare #cols nvarchar(max)
, #cols2 nvarchar(max)
, #sql nvarchar(max)
, #loop int;
create table TempTable (id int primary key identity, CustomerID int, OrderID int, OrderPosition int, ArticleNumber int)
insert into TempTable (CustomerID, OrderID, OrderPosition, ArticleNumber) values (101,1,1,123)
insert into TempTable (CustomerID, OrderID, OrderPosition, ArticleNumber) values (101,1,2,799)
insert into TempTable (CustomerID, OrderID, OrderPosition, ArticleNumber) values (102,2,1,111)
insert into TempTable (CustomerID, OrderID, OrderPosition, ArticleNumber) values (103,3,1,456)
insert into TempTable (CustomerID, OrderID, OrderPosition, ArticleNumber) values (101,4,1,789)
insert into TempTable (CustomerID, OrderID, OrderPosition, ArticleNumber) values (103,5,1,444)
insert into TempTable (CustomerID, OrderID, OrderPosition, ArticleNumber) values (103,5,2,999)
insert into TempTable (CustomerID, OrderID, OrderPosition, ArticleNumber) values (101,6,1,555)
insert into TempTable (CustomerID, OrderID, OrderPosition, ArticleNumber) values (101,6,2,777)
insert into TempTable (CustomerID, OrderID, OrderPosition, ArticleNumber) values (101,6,3,222);
create table TempTable2 (CustomerID int, OrderID int, OrderPosition int, ArticleNumber int, ArticleDesc varchar(100))
select #loop = min(id) from TempTable
while #loop is not null
begin
set #sql = ' insert into TempTable2 (CustomerID, OrderID, OrderPosition, ArticleNumber, ArticleDesc)
select top 4 CustomerID, OrderID, OrderPosition, ArticleNumber, ''ArticleNumber ''+cast(rank() over (order by orderID desc, OrderPosition)as varchar)
from TempTable where CustomerID = '+cast((select CustomerID from TempTable where id = #loop) as varchar)+
'order by orderID desc, OrderPosition'
exec (#sql)
select #loop = min(id) from TempTable where id > #loop
end
select
CustomerID
, sum(isnull([ArticleNumber 1],0)) [ArticleNumber 1]
, sum(isnull([ArticleNumber 2],0)) [ArticleNumber 2]
, sum(isnull([ArticleNumber 3],0)) [ArticleNumber 3]
, sum(isnull([ArticleNumber 4],0)) [ArticleNumber 4]
from
(
select
CustomerID, OrderID, OrderPosition, ArticleDesc, ArticleNumber
from TempTable2
group by CustomerID, OrderID, OrderPosition, ArticleDesc, ArticleNumber
) d
pivot
(sum(ArticleNumber) for ArticleDesc in ([ArticleNumber 1],[ArticleNumber 2],[ArticleNumber 3],[ArticleNumber 4])
) p
group by CustomerID
My answer might not be the best as the genius guys answered above, but I still get the same results...
If you're after just four columns and a quick output the same as listed above, you can always just use ROW_NUMBER, a CTE, and a few joins:
;with TopFour
as
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY [customerID] ORDER BY orderID DESC, OrderPosition ASC) as [TheOrder]
FROM [table]
)
SELECT
a.[customerID],
a.[Articlenumber] as [articlenumber 1],
b.[Articlenumber] as [articlenumber 2],
c.[Articlenumber] as [articlenumber 3],
d.[Articlenumber] as [articlenumber 4]
FROM
TopFour a
LEFT JOIN
TopFour b
ON
a.customerID = b.customerID
AND
b.TheOrder = 2
LEFT JOIN
TopFour c
ON
a.customerID = c.customerID
AND
c.TheOrder = 3
LEFT JOIN
TopFour d
ON
a.customerID = d.customerID
AND
d.TheOrder = 4
WHERE
a.TheOrder = 1
Modified Sample Data to meet Your Requirement
IF OBJECT_ID('tempdb..#TEMP ')IS NOT NULL
DROP TABLE #TEMP
;WITH CTE(customerID , OrderID , OrderPosition , Articlenumber)
AS
(
SELECT 101, 1 , 1 , 123 UNION ALL
SELECT 101, 1 , 2 , 799 UNION ALL
SELECT 102, 2 , 1 , 111 UNION ALL
SELECT 103, 3 , 1 , 456 UNION ALL
SELECT 101, 4 , 1 , 789 UNION ALL
SELECT 103, 5 , 1 , 444 UNION ALL
SELECT 103, 5 , 2 , 999 UNION ALL
SELECT 101, 6 , 1 , 555 UNION ALL
SELECT 101, 6 , 2 , 777 UNION ALL
SELECT 101, 6 , 3 , 222
)
SELECT * , SUM(ArticleNumber)OVER(PARTITION BY customerID ORDER BY customerID ) AS SumOfArticlePerCustomer
INTO #TEMP
FROM
(
SELECT * ,
ROW_NUMBER()OVER(PARTITION BY customerID ORDER BY OrderID DESC ) RNk,
'ArticleNumber_'+CAST(ROW_NUMBER()OVER(PARTITION BY customerID ORDER BY OrderID DESC ) AS VARCHAR(10)) AS ArticleNumberData
FROM CTE
)DT
WHERE RNk <= 4
SELECT * FROM #TEMP
Dynamic Sql ,which is used to get generate the column dynamically and meet the expected result
DECLARE #Sql nvarchar(max),
#DynamicColumn nvarchar(max),
#MaxDynamicColumn nvarchar(max)
SELECT #DynamicColumn = STUFF((SELECT DISTINCT', '+QUOTENAME(ArticleNumberData )
FROM #TEMP FOR XML PATH ('')),1,1,'')
SELECT #MaxDynamicColumn = STUFF((SELECT DISTINCT', '+'MAX('+QUOTENAME(ArticleNumberData )+') AS '+QUOTENAME(ArticleNumberData )+CHAR(13)+CHAR(10)
FROM #TEMP FOR XML PATH (''),TYPE).value('text()[1]','nvarchar(max)'),1,1,N'')
SET #Sql='SELECT CustomerID,'+ #MaxDynamicColumn+',SumOfArticlePerCustomer
FROM
(
SELECT *
FROM #TEMP
)AS src
PIVOT
(
MAX(Articlenumber) FOR [ArticleNumberData] IN ('+#DynamicColumn+')
) AS Pvt
GROUP BY customerID,SumOfArticlePerCustomer
ORDER BY CustomerID'
EXEC (#Sql)
PRINT #Sql

Get Total on SQL Dynamic (Calendar) Pivot Tables

I am trying to get an SQL Server Dynamic Pivot table to work that allows me to count and then sum the number of columns. The purpose of the pivot table is to create a report of all the days individuals were staying in a city and the total number of days(in a month). So, for example, Person A was staying everyday in June - the total will be 30.Person B only started staying on the 3rd of June - the total will be 27 etc. The data table only consists of Name, ArriveDate, DepartDate...the days of the month are created through an SQL query.
+------+------------+------------+-------+-------+-------+-----+-------+-------+-------+
| Name | ArriveDate | DepartDate | 06-01 | 06-02 | 06-03 | ... | 06-29 | 06-30 | Total |
+------+------------+------------+-------+-------+-------+-----+-------+-------+-------+
| A | 2014-06-01 | 2014-06-23 | 1 | 1 | 1 | ... | 1 | 1 | 30 |
| B | 2014-06-02 | 2014-06-23 | 0 | 1 | 1 | ... | 1 | 1 | 27 |
| C | 2014-06-02 | 2014-06-23 | 0 | 0 | 0 | ... | 1 | 1 | 16 |
+------+------------+------------+-------+-------+-------+-----+-------+-------+-------+
Here is the query I have so far:
DROP TABLE #tempDates
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
;WITH cte (datelist, maxdate) AS
(
SELECT MIN(ArriveDate) datelist,
EOMONTH(GETDATE()) AS maxdate
FROM Reservation
UNION ALL
SELECT dateadd(dd, 1, datelist), maxdate
FROM cte
WHERE datelist < maxdate
)
SELECT c.datelist
INTO #tempDates
FROM cte c
SELECT #cols = STUFF(( SELECT distinct ',' + QUOTENAME(convert(CHAR(10), datelist, 120))
FROM #tempDates
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = 'SELECT ID,
ArriveDate,
DepartDate,
' + #cols + '
FROM
(
SELECT r.ID,
r.ArriveDate,
r.DepartDate,
d.datelist,
convert(CHAR(10), datelist, 120) PivotDate
FROM
Reservation r
LEFT JOIN
#tempDates d
ON d.datelist BETWEEN rg.ArriveDate AND GETDATE()
) x
pivot
(
COUNT(datelist)
FOR PivotDate IN (' + #cols + ')
) p '
EXECUTE (#query)
Here is my attempt using a dynamic crosstab:
SQL Fiddle
Sample Data:
Name ArriveDate DepartDate
---- ---------- ----------
A 2015-07-01 2015-07-23
B 2015-07-02 2015-07-04
C 2015-07-03 2015-07-31
Dynamic Crosstab Solution:
DECLARE #minDate AS DATE,
#maxDate AS DATE
SELECT #minDate = DATEADD(DAY, 1, EOMONTH(GETDATE(), -1)),
#maxDate = EOMONTH(GETDATE())
CREATE TABLE #dates(dt DATE)
DECLARE #sql1 VARCHAR(MAX) = '',
#sql2 VARCHAR(MAX) = '',
#sql3 VARCHAR(MAX) = '';
WITH E1(N) AS(
SELECT 1 FROM(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(N)
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b),
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b),
E8(N) AS(SELECT 1 FROM E4 a CROSS JOIN E4 b),
Tally(N) AS(
SELECT TOP(DATEDIFF(DAY, #minDate, #maxDate) + 1)
ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM E8
)
INSERT INTO #dates
SELECT DATEADD(DAY, N - 1, #minDate)
FROM Tally
SELECT #sql1 =
'SELECT
r.Name
, r.ArriveDate
, r.DepartDate' + CHAR(10)
SELECT #sql2 = #sql2 +
' , SUM(CASE WHEN d.dt = CAST(''' + CONVERT(VARCHAR(8), dt, 112) + ''' AS DATE) THEN 1 ELSE 0 END) AS '
+ QUOTENAME(CONVERT(VARCHAR(10), dt, 120)) + CHAR(10)
FROM #dates
ORDER BY dt
SELECT #sql2 = #sql2 +
' , COUNT(d.dt) AS [total]' + CHAR(10)
SELECT #sql3 =
'FROM Reservation r
LEFT JOIN #dates d
ON d.dt BETWEEN r.ArriveDate AND r.DepartDate
GROUP BY
r.Name, r.ArriveDate, r.DepartDate'
PRINT (#sql1 + #sql2 + #sql3)
EXEC (#sql1 + #sql2 + #sql3)
DROP TABLE #dates
Result:
Name ArriveDate DepartDate 2015-07-01 2015-07-02 2015-07-03 ..... 2015-07-29 2015-07-30 2015-07-31 total
---- ---------- ---------- ----------- ----------- ----------- ..... ----------- ----------- ----------- -----------
A 2015-07-01 2015-07-23 1 1 1 ..... 0 0 0 23
B 2015-07-02 2015-07-04 0 1 1 ..... 0 0 0 3
C 2015-07-03 2015-07-31 0 0 1 ..... 1 1 1 29

Output in special format

May any one please help me how to code the below scenerio in sql.
DECLARE #T TABLE
(
Year VARCHAR (50),
Month VARCHAR (50),
GROUPS VARCHAR (50),
SALESPERNO VARCHAR (50),
Net VARCHAR (50)
)
INSERT #T
SELECT '2014','1','A','6607','109.34' UNION ALL
SELECT '2014','2','A','6607','13.42' UNION ALL
SELECT '2014','3','A', '6607','359.41' UNION ALL
SELECT '2014','1','A', '6608','99.52' UNION ALL
SELECT '2014','2','A','6608','95.62' UNION ALL
SELECT '2014','3','A', '6608','89.63' UNION ALL
SELECT '2014','1','B','8888','340.95' UNION ALL
SELECT '2014','2','B','8888','652.25' UNION ALL
SELECT '2014','3','B','8888','352.26'
SELECT * FROM #T
Here: month 1 = Jan, 2=feb, 3=mar
Output is sum of net for a particular salesperno and groups for jan feb mar and total like this:
January| February | March| QFY | SALES_GROUP |SALESPERSON_NUMBER
109.34 | 13.42 | 359.41| 482.17| A | 6607
99.52 | 95.62 | 89.63 | 284.77| A | 6608
340.95 | 652.25 | 352.26| 1345.46| B | 8888
You can do this in multiple ways. As Tab Alleman suggested one way is with the help of CASE and GROUP BY.
select
SUM(CASE WHEN [Month]=1 THEN [NET] END) [January],
SUM(CASE WHEN [Month]=2 THEN [NET] END) [February],
SUM(CASE WHEN [Month]=3 THEN [NET] END) [March],
SUM([NET]) [QFY],
[GROUPS] [SALES_GROUP],
[SALESPERNO] [SALESPERSON_NUMBER]
from #T
group by
[GROUPS],
[SALESPERNO]
This is another way, with PIVOT
SELECT
[1] [January],
[2] [February],
[3] [March],
[1]+[2]+[3] [QFY],
[GROUPS] [SALES_GROUP],
[SALESPERNO] [SALESPERSON_NUMBER]
FROM #T
PIVOT (
SUM(NET)
FOR [Month] in ([1],[2],[3])
) pvt

PIVOT with multiple columns

I am trying to pivot on two columns in SQL Server 2008 on an invoice table. So I have data like the follows:
+--------------+--------+---------+------+
| Invoice Date | Item # | Dollars | Lbs. |
+--------------+--------+---------+------+
| 1/1/14 | A | 1 | 1 |
| 1/2/14 | B | 2 | 2 |
| 1/3/14 | A | 3 | 3 |
| 1/4/14 | B | 4 | 4 |
| 2/1/14 | A | 5 | 5 |
| 2/1/14 | B | 6 | 6 |
+--------------+--------+---------+------+
I would like to display it as
+--------+--------------+-----------------+--------------+-----------------+
| Item # | 1/31/14 Lbs. | 1/31/14 Dollars | 2/28/14 Lbs. | 2/28/14 Dollars |
+--------+--------------+-----------------+--------------+-----------------+
| A | 4 | 4 | 5 | 5 |
| B | 6 | 6 | 6 | 6 |
+--------+--------------+-----------------+--------------+-----------------+
Note the column name is the last day of that month and either dollars or pounds. I can do it just fine one column (either pounds or dollars) however I can't do it on both.
Here is my example code for just pounds:
DECLARE
#v_Columns VARCHAR(MAX),
#v_Query VARCHAR(MAX)
--pivot and delimit values
SELECT #v_Columns = COALESCE(#v_Columns,'[') + convert(varchar(8), InvoiceDate, 1) + ' Lbs.' + '],['
FROM
( SELECT DISTINCT dbo.ufn_GetLastDayOfMonth(InvoiceDate) As InvoiceDate
FROM Invoice
WHERE InvoiceDate BETWEEN #BEGIN_DATE AND #END_DATE
ORDER BY InvoiceDate
--delete last two chars of string (the ending ',[')
SET #v_Columns = SUBSTRING(#v_Columns, 1, LEN(#v_Columns)-2)
PRINT #v_Columns
--construct sql statement
SET #v_Query =
'WITH AllOrders (LastInvoiceDate, Item, Pounds) AS
(
SELECT
CONVERT(varchar(8), dbo.ufn_GetLastDayOfMonth(Invoice.InvoiceDate), 1) + ''' + ' Lbs.' + ''' As LastInvoiceDate,
Item,
Pounds
FROM INVOICE
WHERE InvoiceDate BETWEEN #BEGIN_DATE AND #END_DATE
)
SELECT *
FROM AllOrders
PIVOT
(
SUM(QuantityShipped)
FOR LastInvoiceDate IN (' + #v_Columns + ')
) AS pivotview'
Thank you all in advance!
In order to get the result you are going to have to either PIVOT twice or UNPIVOT the Dollars and Lbs columns into a single column and then apply the PIVOT once. My preference would be to unpivot and then pivot because I find it to be much easier.
Instead of working dynamically first, you should write the query as a static or hard-coded version to get the logic correct, then convert it to dynamic SQL. The example that I have uses your final dates 201-01-31, etc because you are using a function to create those dates and should be able to apply that as needed.
Since you are using SQL Server 2005+, you can use CROSS APPLY to unpivot Dollars and Lbs. The code will be similar to the following:
select
t.ItemNo,
new_col = convert(varchar(10), t.[invoice date], 120) + '_'+ c.col,
c.value
from yourtable t
cross apply
(
select 'Dollars', Dollars union all
select 'Lbs', Lbs
) c (col, value);
See SQL Fiddle with Demo. This converts your data to the following format:
| ITEMNO | NEW_COL | VALUE |
|--------|--------------------|-------|
| A | 2014-01-31_Dollars | 1 |
| A | 2014-01-31_Lbs | 1 |
| B | 2014-01-31_Dollars | 2 |
| B | 2014-01-31_Lbs | 2 |
| A | 2014-01-31_Dollars | 3 |
I've concatenated into new_col the final column names that you'll need. Again you can format the date in whatever format you need, I just used 2014-01-31 and added the Dollars or Lbs to the end of it. Once you've got the data, you will PIVOT the values into your final desired result:
select ItemNo,
[2014-01-31_Lbs], [2014-01-31_Dollars],
[2014-02-28_Lbs], [2014-02-28_Dollars]
from
(
select
t.ItemNo,
new_col = convert(varchar(10), t.[invoice date], 120) + '_'+ c.col,
c.value
from yourtable t
cross apply
(
select 'Dollars', Dollars union all
select 'Lbs', Lbs
) c (col, value)
) d
pivot
(
sum(value)
for new_col in ([2014-01-31_Lbs], [2014-01-31_Dollars],
[2014-02-28_Lbs], [2014-02-28_Dollars])
) p;
See SQL Fiddle with Demo. Now you've got the result you want, so simply convert it to dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(convert(varchar(10), t.[invoice date], 120) + '_'+ c.col)
from yourtable t
cross apply
(
select 'Lbs', 0 union all
select 'Dollars', 1
) c (col, so)
group by [invoice date], col, so
order by [invoice date], so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ItemNo,' + #cols + '
from
(
select
t.ItemNo,
new_col = convert(varchar(10), t.[invoice date], 120) + ''_''+ c.col,
c.value
from yourtable t
cross apply
(
select ''Dollars'', Dollars union all
select ''Lbs'', Lbs
) c (col, value)
) d
pivot
(
sum(value)
for new_col in (' + #cols + ')
) p '
exec sp_executesql #query;
See SQL Fiddle with Demo. This give a final result of:
| ITEMNO | 2014-01-31_LBS | 2014-01-31_DOLLARS | 2014-02-28_LBS | 2014-02-28_DOLLARS |
|--------|----------------|--------------------|----------------|--------------------|
| A | 4 | 4 | 5 | 5 |
| B | 6 | 6 | 6 | 6 |
Here is your sample table
CREATE TABLE #TEMP([Invoice Date] DATE,[Item #] VARCHAR(10),[DollarS] NUMERIC(10,0),[Lbs.] NUMERIC(10,0))
INSERT INTO #TEMP VALUES ('1/1/14', 'A',1,1)
INSERT INTO #TEMP VALUES ('1/2/14', 'B',2,2)
INSERT INTO #TEMP VALUES ('1/3/14', 'A',3,3)
INSERT INTO #TEMP VALUES ('1/4/14', 'B',4,4)
INSERT INTO #TEMP VALUES ('2/1/14', 'A',5,5)
INSERT INTO #TEMP VALUES ('2/1/14', 'B',6,6)
Now you need to apply UNION ALL(instead of UNPIVOT) and bring columns to row and combine the columns, get the order of columns as Date+LBS/DOLLARS.
SELECT DISTINCT DENSE_RANK() OVER(ORDER BY CAST(LASTDAY AS DATE),UNIT DESC)RNO,*,
CAST(DATEPART(MONTH,LASTDAY)AS VARCHAR) +'/'+ CAST(DATEPART(DAY,LASTDAY)AS VARCHAR) +'/' +RIGHT(CAST(YEAR(LASTDAY)AS VARCHAR),2)+' ' +UNIT PIVOTCOL
INTO #NEWTABLE
FROM
(
SELECT [Item #],'DOLLARS' UNIT,
DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,[Invoice Date])+1,0))LASTDAY,
SUM([Dollars]) OVER(PARTITION BY [Item #],DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,[Invoice Date])+1,0))) VALUE
FROM #TEMP
UNION ALL
SELECT [Item #], 'LBS.',
DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,[Invoice Date])+1,0))LASTDAY,
SUM([Lbs.]) OVER(PARTITION BY [Item #],DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,[Invoice Date])+1,0))) DOLLARSUM
FROM #TEMP
)TAB
Now declare the query to get the columns dynamically and to set NULL to Zero
DECLARE #cols NVARCHAR (MAX)
DECLARE #NullToZeroCols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + PIVOTCOL + ']',
'[' + PIVOTCOL + ']')
FROM (SELECT DISTINCT RNO,PIVOTCOL FROM #NEWTABLE) PV
ORDER BY RNO
PRINT #COLS
SET #NullToZeroCols = SUBSTRING((SELECT ',ISNULL(['+PIVOTCOL+'],0) AS ['+PIVOTCOL+']'
FROM(SELECT DISTINCT RNO,PIVOTCOL FROM #NEWTABLE GROUP BY RNO,PIVOTCOL)TAB
ORDER BY RNO FOR XML PATH('')),2,8000)
Now pivot the query
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT [Item #],' + #NullToZeroCols + ' FROM
(
SELECT [Item #],VALUE,PIVOTCOL FROM #NEWTABLE
) x
PIVOT
(
SUM(VALUE)
FOR PIVOTCOL IN (' + #cols + ')
) p
ORDER BY [Item #];'
EXEC SP_EXECUTESQL #query
SQL FIDDLE
RESULT

Resources