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
Related
I have a query that is similar to this:
SELECT dateadd(DAY,0, datediff(day,0, archivedate)) as day,
COUNT(*) item_value,
item_metadata.item_label
FROM item_values
join item_metadata on item_values.md_ID= item_metadata.Id
group by dateadd(DAY,0, datediff(day,0, archivedate)), item_label
order by dateadd(DAY,0, datediff(day,0, archivedate)) desc
This results in data that looks like this:
day item_value item_label
2020-03-31 8 orange
2020-03-31 2 black
2020-03-30 28 green
2020-03-30 1 blue
2020-03-30 59 orange
2020-03-29 11 black
2020-03-29 1 blue
2020-03-29 15 green
2020-03-29 4 orange
I want to have it look like this though:
day orange black green blue
2020-03-31 8 2 0 0
2020-03-30 59 0 28 1
2020-03-29 4 11 15 1
Is this possible in SQL Server?
I'm basically wanting to eventually visualize the data as a stacked chart.
SOLVED
Based on the other links posted below the solution i found was:
declare
#cols nvarchar(max),
#stmt nvarchar(max)
select #cols = isnull(#cols + ', ', '') + '[' + T.item_label + ']' from (select distinct item_label from item_metadata where Active=1) as T
select #stmt = '
select * from (SELECT dateadd(DAY,0, datediff(day,0, archivedate)) as [day],
COUNT(*) as item_value,
item_label
FROM item_values
join item_metadata on item_values.md_ID= item_metadata.Id
where Ignored=0 group by dateadd(DAY,0, datediff(day,0, archivedate)),item_label ) Visits
pivot(sum(item_value) for item_metadata
in (' + #cols + ') ) as pivottable order by day desc
'
exec sp_executesql #stmt = #stmt
Demo on db<>fiddle
You might be looking for Dynamic Pivot query
DECLARE
#columns NVARCHAR(MAX) = '',
#sql NVARCHAR(MAX) = '';
SELECT #columns += QUOTENAME(item_label) + ','
From (SELECT DISTINCT item_label
FROM #a) A
-- remove the last comma
SET #columns = LEFT(#columns, LEN(#columns) - 1);
SET #sql = 'SELECT *
FROM ( SELECT day,item_value,item_label
FROM #a) src
PIVOT( MAX([item_value]) FOR item_label IN ('+ #columns +')) AS pivot_table;';
-- execute the dynamic SQL
EXECUTE sp_executesql #sql;
Output
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)
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
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.
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.