Month wise aging report by SQL query - sql

6 month aging report of the customers from today date.
Data in the table is like following:
Customer Date Amount
AAA 3-Sep-13 1000
BBB 4-Jan-14 4000
BBB 5-Feb-14 1000
AAA 3-Dec-13 3000
CCC 7-Nov-13 800
DDD 15-Nov-13 1000
DDD 25-Jan-13 1000
CCC 8-Nov-13 1000
I need a SQL query to get the below results and heading should be in month and year
Party name Sep-13 Oct-13 Nov-13 Dec-13 Jan-14 Feb-14 Total Amount
AAA 1000 0 0 3000 0 0 4000
BBB 0 0 0 4000 1000 0 5000
CCC 0 0 1800 0 0 0 1800
DDD 0 0 1000 0 1000 0 2000

This query will dynamically generate the output that you want. However, because you don't have values for all periods, a NULL is inserted (which is valid). If you want to make a value 0 instead of null, you'll have to modify the query and use the ISNULL function for each field.
CREATE TABLE testTable (
Party NVARCHAR(20),
[Date] SMALLDATETIME, --done like this to get a TOTAL heading
Amount DECIMAL
)
INSERT INTO testTable VALUES
('AAA','3-Sep-13',1000),
('BBB','4-Jan-14',4000),
('BBB','5-Feb-14',1000),
('AAA','3-Dec-13',3000),
('CCC','7-Nov-13',800),
('DDD','15-Nov-13',1000),
('DDD','25-Jan-13',1000)
INSERT INTO testTable
SELECT Party, '2054-12-31', SUM(Amount) [Amount]
FROM testTable
GROUP BY Party
DECLARE #columnHeadings NVARCHAR(MAX)
SELECT #columnHeadings = COALESCE(
#columnHeadings + ',[' + CONVERT(NVARCHAR, [Date], 6) + ']',
'[' + CONVERT(NVARCHAR, [Date], 6)+ ']'
)
FROM testTable
GROUP BY [Date]
ORDER BY [Date]
DECLARE #sql NVARCHAR(MAX)
SET #sql = N'SELECT [party], ' + #columnHeadings + ' INTO ##output FROM ' +
'( SELECT [Party], [Amount], CONVERT(NVARCHAR, [Date], 6) [Date] FROM testTable ) [source]' +
' PIVOT ' +
'( SUM(Amount) FOR [Date] IN (' + #columnHeadings +')) [pvt];'
EXEC(#sql)
EXEC tempdb.sys.sp_rename '##output.[31 Dec 54]', 'Total', 'COLUMN'
SELECT * FROM ##output
DROP TABLE testTable
DROP TABLE ##output
Disclaimer: I'm not fond of this code. But it works.

Related

SQL metric rows to date columns-

Suppose I have the following table:
Client ContainerID Year Month NumberOfViews
bar 116025 2019 1 2
dandy 2753 2020 1 3
dandy 2753 2020 2 2
dandy 4247 2020 1 1
demo 20037 2019 1 1
I want it to transform to the following table:
Client ContainerID Jan-2019 Jan-2020 Feb-2020
bar 116025 2 0 0
dandy 2753 0 3 2
dandy 4247 0 1 0
demo 20037 1 0 0
meaning: year + month cell rows turns into columns and the value for 'NumberOfViews' goes to the right date column.
There you go.
Replace your table name:
WITH cte AS(
SELECT Client
,ContainerId
,NumberOfViews
,FORMAT(CAST(CAST([Year] AS CHAR(4)) + '-' + CAST([Month] AS CHAR(2)) + '-01' AS DATE), 'MMM-yyyy') AS [DateCol]
FROM dbo.Test)
SELECT Client
,ContainerId
,ISNULL([Jan-2019], 0) AS [Jan-2019]
,ISNULL([Jan-2020], 0) AS [Jan-2020]
,ISNULL([Feb-2020], 0) AS [Feb-2020]
FROM cte
PIVOT
(
SUM(NumberOfViews)
FOR DateCol IN ([Jan-2019], [Jan-2020], [Feb-2020])
) AS PivotTable;
I think you need a Dynamic PIVOT as the month-year is not static. Please try this below logic-
Please use your original table name where ever you found "your_table" in the script.
DECLARE #cols AS NVARCHAR(MAX),
#sqlCommand AS NVARCHAR(MAX);
SELECT #cols =
STUFF((SELECT ( '],[' + A.YM)
FROM
(
SELECT
CASE Month
WHEN 1 THEN 'Jan-' WHEN 2 THEN 'Feb-' WHEN 3 THEN 'Mar-'
WHEN 4 THEN 'Apr-' WHEN 5 THEN 'May-' WHEN 6 THEN 'Hun-'
WHEN 7 THEN 'Jul-' WHEN 8 THEN 'Aug-' WHEN 9 THEN 'Sep-'
WHEN 10 THEN 'Oct-' WHEN 11 THEN 'Nov-' WHEN 12 THEN 'Dec-'
END
+ CAST(Year AS VARCHAR) YM
FROM your_table
) A
ORDER BY A.YM
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')+']'
FROM your_table
SET #sqlCommand=
N'SELECT Client,ContainerID,'+SUBSTRING(#cols,2,LEN(#cols))+'
FROM
(
SELECT Client,ContainerID,YM,NumberOfViews
FROM
(
SELECT Client,ContainerID,NumberOfViews,
CASE Month
WHEN 1 THEN ''Jan-'' WHEN 2 THEN ''Feb-'' WHEN 3 THEN ''Mar-''
WHEN 4 THEN ''Apr-'' WHEN 5 THEN ''May-'' WHEN 6 THEN ''Hun-''
WHEN 7 THEN ''Jul-'' WHEN 8 THEN ''Aug-'' WHEN 9 THEN ''Sep-''
WHEN 10 THEN ''Oct-'' WHEN 11 THEN ''Nov-'' WHEN 12 THEN ''Dec-''
END
+ CAST(Year AS VARCHAR) YM
FROM your_table
)A
) AS P
PIVOT
(
SUM(NumberOfViews)
FOR YM IN('+SUBSTRING(#cols,2,LEN(#cols))+')
) PVT'
--PRINT #sqlCommand
EXEC (#sqlCommand)

SQL query date into new columns with unique ID

I have the following columns, a 5 digit ID, date and a value. The ID repeats only when a new date is present.
ID Date Value
11111 2014-12-31 45
22222 2014-12-31 435
33333 2014-12-31 11
11111 2014-12-30 5
22222 2014-12-30 2245
33333 2014-12-30 86
11111 2014-12-29 43
22222 2014-12-29 4678
33333 2014-12-29 2494
I am trying to create an SQL query that will display the following (dates are column names):
ID 2014-12-31 2014-12-30 2014-12-29
11111 45 5 43
22222 435 2245 4678
33333 11 86 2494
What is the best way of doing this using MS SQL.
Thanks
As pointed out by the comments, you need to PIVOT your data. Here is one way using a Dynamic Crosstab.
Read this article by Jeff Moden for reference: http://www.sqlservercentral.com/articles/Crosstab/65048
CREATE TABLE temp(
ID INT,
[Date] DATE,
Value INT
)
INSERT INTO temp VALUES
(11111, '2014-12-31', 45),
(22222, '2014-12-31', 435),
(33333, '2014-12-31', 11),
(11111, '2014-12-30', 5),
(22222, '2014-12-30', 2245),
(33333, '2014-12-30', 86),
(11111, '2014-12-29', 43),
(22222, '2014-12-29', 4678),
(33333, '2014-12-29', 2494);
DECLARE #sql1 VARCHAR(2000) = ''
DECLARE #sql2 VARCHAR(2000) = ''
DECLARE #sql3 VARCHAR(2000) = ''
SELECT #sql1 =
'SELECT
ID
'
SELECT #sql2 = #sql2 +
' ,MIN(CASE WHEN [Date] = CAST(''' + CONVERT(VARCHAR(10), [Date], 120) + ''' AS Date) THEN Value END) AS ['
+ CONVERT(VARCHAR(10), [Date], 120) + ']'+ CHAR(10)
FROM(
SELECT DISTINCT [Date] FROM temp
)t
ORDER BY [Date] DESC
SELECT #sql3 =
'FROM temp
GROUP BY ID
ORDER BY ID'
PRINT (#sql1 + #sql2 + #sql3)
EXEC (#sql1 + #sql2 + #sql3)
DROP TABLE temp
EDIT:
If you want to use fixed name, you'll want to assign a number for each Date. This can be done using ROW_NUMBER():
SELECT #sql2 = #sql2 +
' ,MIN(CASE WHEN [Date] = CAST(''' + CONVERT(VARCHAR(10), [Date], 120) + ''' AS Date) THEN Value END) AS [Date' + CONVERT(VARCHAR, rn) + ']'+ CHAR(10)
FROM(
SELECT
[Date],
rn = ROW_NUMBER() OVER(ORDER BY [Date])
FROM (
SELECT DISTINCT [Date]FROM temp
)x
)t
ORDER BY [Date] DESC

Dynamic PIVOT in SQL Server 2005

i rare use PIVOT in sql server but now requirement is something that i have to use PIVOT.
my table structure is something like
CurDate Warranty_Info
------- -------------
01/01/2009 50
01/05/2009 30
01/03/2009 220
01/01/2010 40
01/06/2010 10
01/02/2010 0
01/01/2011 10
01/05/2012 420
01/05/2013 130
now i have to show the data in this way
Month 2009 2010 2011 2012 2013
----- ---- ---- ---- ---- -----
JAN 10 0 11 32 98
FEB 20 10 21 11 44
MAR 0 224 33 77 31
UPTO
DEC
1) data should display month wise order....so jan first
2) if no data exist in any month then month name will come with 0 as value for that month.
i tried but fail. here is my sql by which i tried.
SELECT *
FROM (SELECT DateName(month,DateAdd(month,Month(CurDate),-1)) as [Month],
YEAR(CurDate) AS WarrantyYear,
Warranty_Info
FROM eod_main) AS D
PIVOT (
SUM(Warranty_Info)
FOR WarrantyYear IN (
[2009],[2010],[2011],[2012],[2013]
)
) AS P
ORDER BY DATENAME(MONTH,DATEADD(MONTH, [Month] - 1, 0))
and i tried to generate sql dynamically this way.
DECLARE #cols AS NVARCHAR(MAX)
DECLARE #PivotTableSQL NVARCHAR(MAX)
DECLARE #StartYear AS INT,
#EndYear AS INT
SET #StartYear=2009
SET #EndYear=2013
select #cols = STUFF((SELECT ',' + QUOTENAME(Year(CurDate))
from eod_main
WHERE Year(CurDate)>=#StartYear AND Year(CurDate) <=#EndYear
group by Year(CurDate)
order by Year(CurDate)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #PivotTableSQL = N'
SELECT * FROM (
SELECT Month(CurDate), YEAR(CurDate) AS WarrantyYear,Warranty_Info FROM eod_main) AS D
PIVOT (
SUM(Warranty_Info)
FOR WarrantyYear IN (
' + #cols + '
)
) AS PivotTable
'
print #PivotTableSQL
but some where i am facing problem like
1) display month name
2) order by month no
3) null value show show 0 instead of NULL
4) if no data exist for any month then month name should display with 0 value.
please guide me how to achieve it. thanks
UPDATE
DECLARE #query varchar(max)
DECLARE #StartYr INT
DECLARE #ENDYr INT
declare #years varchar(max), #yearsColumns varchar(max)
SET #StartYr=2011
SET #EndYr=2013
SELECT 1 mID, 'January' as month into #tempMonths UNION ALL
SELECT 2,'February' as month UNION ALL
SELECT 3,'March' as month UNION ALL
SELECT 4,'April' as month UNION ALL
SELECT 5,'May' as month UNION ALL
SELECT 6,'June' as month UNION ALL
SELECT 7,'July' as month UNION ALL
SELECT 8,'August' as month UNION ALL
SELECT 9,'September' as month UNION ALL
SELECT 10,'October' as month UNION ALL
SELECT 11,'November' as month UNION ALL
SELECT 12,'December' as month
SELECT #years=COALESCE(#years+',','') +'['+ cast(years as varchar(4))+']',
#yearsColumns=COALESCE(#yearsColumns+',','') +'isnull(['+ cast(years as varchar(4))+'],0)
as ['+cast(years as varchar(4))+']'
from (select distinct YEAR(CurDate) years from EOD_Main
WHERE YEAR(CurDate)>=#StartYr AND YEAR(CurDate)<=#EndYr
) as x
SET #query = 'Select months,'+#yearsColumns+' from (
select distinct mID, YEAR(CurDate) years,[MONTH] months,
isnull(Warranty_Info,0) as Warranty_Info from EOD_Main
right join #tempMonths on datename(month,CurDate ) =[month]
) as xx
PIVOT
(
SUM(xx.Warranty_Info) FOR years IN ('+#years+')
)
as pvt ORDER BY mID'
--PRINT #query
EXEC(#query)
drop table #tempMonths
Hi Find the Below Solution, i hope that it is help full to you
SELECT CAST('01/01/2009' AS date) CurDate , 50 Warranty_Info INto #temp UNION all SELECT
CAST('05/01/2009' AS date) , 30 UNION all SELECT
CAST('03/01/2009' AS date) , 220 UNION all SELECT
CAST('01/01/2010' AS date) , 40 UNION all SELECT
CAST('06/01/2010' AS date) , 10 UNION all SELECT
CAST('02/01/2010' AS date) , 0 UNION all SELECT
CAST('01/01/2011' AS date) , 10 UNION all SELECT
CAST('05/01/2012' AS date) , 420 UNION all SELECT
CAST('05/01/2013' AS date) , 130
SELECT 1 mID, 'January' as month into #tempMonths UNION ALL
SELECT 2,'February' as month UNION ALL
SELECT 3,'March' as month UNION ALL
SELECT 4,'April' as month UNION ALL
SELECT 5,'May' as month UNION ALL
SELECT 6,'June' as month UNION ALL
SELECT 7,'July' as month UNION ALL
SELECT 8,'August' as month UNION ALL
SELECT 9,'September' as month UNION ALL
SELECT 10,'October' as month UNION ALL
SELECT 11,'November' as month UNION ALL
SELECT 12,'December' as month
declare #years varchar(max), #yearsColumns varchar(max)
SELECT #years=COALESCE(#years+',','') +'['+ cast(years as varchar(4))+']',
#yearsColumns=COALESCE(#yearsColumns+',','') +'isnull(['+ cast(years as varchar(4))+'],0) as ['+cast(years as varchar(4))+']'
from (select distinct YEAR(CurDate) years from #temp) as x
print #years
DECLARE #query varchar(max)= '
select months,'+#yearsColumns+' from (
select distinct mID, YEAR(CurDate) years,[MONTH] months, isnull(Warranty_Info,0) as Warranty_Info from #temp
right join #tempMonths on datename(month,CurDate ) =[month]
) as xx
PIVOT
(
SUM(xx.Warranty_Info) FOR years IN ('+#years+')
)
as pvt ORDER BY mID'
PRINT #query
EXEC(#query)
output islike below
months 2009 2010 2011 2012 2013
--------- ----------- ----------- ----------- ----------- -----------
January 50 40 10 0 0
February 0 0 0 0 0
March 220 0 0 0 0
April 0 0 0 0 0
May 30 0 0 420 130
June 0 10 0 0 0
July 0 0 0 0 0
August 0 0 0 0 0
September 0 0 0 0 0
October 0 0 0 0 0
November 0 0 0 0 0
December 0 0 0 0 0
if it is give accurate result don't forget to make a vote.

data between two dates in column for each day

I have table worker
id name
----------- -------------------
5 Артур Петрович
6 Дмитрий Белов
7 Казарян Артур
and another table
id date amount id_worker
----------- ---------- ----------- -----------
27 2013-09-12 1500 5
28 2013-09-12 100 6
29 2013-09-12 500 5
30 2013-09-12 500 6
31 2013-09-14 1000 7
32 2013-09-15 100 5
33 2013-09-15 200 5
I want to write stored procedure which on input gets start and end dates
and on output I want to get this table if:
start date:2013-09-10
end date :2013-09-15
Name 2013-09-10 2013-09-11 2013-09-12 2013-09-13 2013-09-14 2013-09-15
_______________ __________ __________ __________ __________ __________ __________
Артур Петрович 0 0 2000 0 0 300
Дмитрий Белов 0 0 600 0 0 0
Казарян Артур 0 0 0 0 1000 0
The only way I konw to do this is using Dynamic SQL, IMO there is no risk of SQL Injection if the tables structures are known ahead
DECLARE #DateList VARCHAR(MAX), #DateListCoalesced VARCHAR(MAX)
SELECT #DateList = '', #DateListCoalesced = ''
;WITH DateLimits AS (
SELECT CAST('2013-9-10' AS DATE) AS dt
UNION ALL
SELECT DATEADD(dd, 1, dt)
FROM DateLimits s
WHERE DATEADD(dd, 1, dt) <= CAST('2013-9-15' AS DATE))
SELECT #DateList = #DateList + '[' + CAST(dt AS VARCHAR)+ '], ' ,
#DateListCoalesced = #DateListCoalesced + ' COALESCE( [' + CAST(dt AS VARCHAR)+ '] , 0) as [' + CAST(dt AS VARCHAR)+ '], '
FROM DateLimits
;SET #DateList = LEFT(#DateList, LEN(#DateList) - 1)
;SET #DateListCoalesced = LEFT(#DateListCoalesced, LEN(#DateListCoalesced) - 1)
DECLARE #query NVARCHAR(max)
SET #query = N'SELECT [Name], ' + #DateListCoalesced +'
FROM
(SELECT [Name], [Date], [Amount]
FROM WorkerAmount
INNER JOIN Worker ON WorkerAmount.id_worker = Worker.id
) p
PIVOT
(
Sum ([Amount] )
FOR [Date] IN ( '+ #DateList +' )
) AS pvt '
EXEC sp_executesql #Query
This answer uses a combination of few other questions
getting dates between range of dates
Pivots with dynamic columns in sql-server
replace null values in sql pivot

How to apply pivot in below scenarios

I have below table
Name Month Year Count
----------------------------
xxx 12 2012 24
xxx 1 2013 42
xxx 2 2013 23
yyy 12 2012 34
yyy 1 2013 12
yyy 2 2013 54
I would like to convert it into below format,
Name Dec-12 Jan-13 Feb-13
--------------------------------
xxx 24 42 23
yyy 34 12 54
How to apply pivot?
Since you are using SQL Server there are several ways that you can pivot the data from rows into columns.
If your values are limited or you have a known number of values, then you can hard-code the values with a static pivot:
select name, [Dec_12], [Jan_13], [Feb_13]
from
(
select name,
left(datename(month, dateadd(month, month, 0) -1), 3) +'_'+right(cast(year as varchar(4)), 2) MY,
[count]
from yourtable
) src
pivot
(
sum(count)
for my in ([Dec_12], [Jan_13], [Feb_13])
) piv;
See SQL Fiddle with Demo.
Now, if you have an unknown number of values, then you will need to implement dynamic SQL to generate the result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(my)
from
(
select left(datename(month, dateadd(month, month, 0) -1), 3) +'_'+right(cast(year as varchar(4)), 2) my,
CAST(
CAST(year AS VARCHAR(4)) +
RIGHT('0' + CAST(month AS VARCHAR(2)), 2) +
'01'
AS DATETIME) fulldate
from yourtable
) t
group by my, fulldate
order by fulldate
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT name, ' + #cols + '
from
(
select name,
left(datename(month, dateadd(month, month, 0) -1), 3) +''_''+right(cast(year as varchar(4)), 2) MY,
[count]
from yourtable
) x
pivot
(
sum(count)
for my in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo.
This difference with this and the static version is if you need an unknown number of dates or want this to automatically update with new dates when they are available, this will return the new data without changing the code.
The result of both queries is:
| NAME | DEC_12 | JAN_13 | FEB_13 |
-----------------------------------
| xxx | 24 | 42 | 23 |
| yyy | 34 | 12 | 54 |
Try this:
WITH CTE
AS
(
SELECT
Name,
CAST(Month AS VARCHAR(2)) + '-' + CAST(Year AS VARCHAR(4)) AS MonthYear,
[Count]
FROM tablename
)
SELECT
Name,
[12-2012] AS 'Dec-12',
[1-2013] AS 'Jan-13',
[2-2013] AS 'Feb-13'
FROM CTE
PIVOT
(
MAX([Count])
FOR MonthYear IN([12-2012],
[1-2013],
[2-2013])
) AS p;
SQL Fiddle Demo
SELECT t.name
, MAX(CASE
WHEN t.month=12 AND t.year = 2012
THEN count
ELSE NULL
END) AS "Dec_12"
, MAX(CASE
WHEN t.month=1 AND t.year = 2013
THEN count
ELSE NULL
END) AS "Jan_13"
, MAX(CASE
WHEN t.month=2 AND t.year = 2013
THEN count
ELSE NULL
END) AS "Feb_13"
FROM table t
GROUP BY t.name
;