i have problem.., i want to display the null value as 'X'
i has been using the case statement but still failed,
i already using Isnull and failed too.
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
DECLARE #X AS NVARCHAR(MAX)
SELECT #ColumnName= ISNULL(#ColumnName + ',','') + QUOTENAME(code)
FROM (SELECT DISTINCT code FROM edrsDB..tbl_users where area='west' ) AS status
SET #DynamicPivotQuery =
N'SELECT date, '+ #ColumnName +'
FROM (
SELECT DISTINCT userid, status , DATEPART(dd, [date]) as Date
FROM edrsDB..tbl_status
WHERE DatePart(MM, [Date]) = 3
And DATEPART(YYYY, [Date]) = 2017
Union
SELECT DISTINCT userid, status , DATEPART(DD,[date]) as Date
FROM edrsDB..tbl_public_holiday
WHERE DatePart(MM, [Date]) = 3
And DATEPART(YYYY, [Date]) = 2017
Union
SELECT DISTINCT userid, status , DATEPART(DD,[Date]) as Date
FROM edrsDB..tbl_station_weekend
WHERE DatePart(MM, [Date]) = 3
And DATEPART(YYYY, [Date]) = 2017
And date IS NOT NULL
) AS x
PIVOT
(
max(status)
FOR userid IN (' + #ColumnName + ')
) AS PVTTable'
EXEC sp_executesql #DynamicPivotQuery
this is ouput.. , i want null to be display as 'X'
output
You can use ISNULL
ISNULL(<some_column/some_stuff>, 'X')
'X' would be the value displayed instead of the first parameter if first parameter result is null
Use ISNULL function in SQL Server :
SELECT ISNULL(Your_column,'X')
You can use isnull() or coalesce() in sql server.
select isnull(col,'X')
or
select coalesce(col,'X')
The main difference between the two is that coalesce() can support more than 2 parameters, and it selects the first one that is not null. More differences between the two are answered here.
select coalesce(col,col2,'X')
coalesce() is also standard ANSI sql, so you will find it in most RDBMS. isnull() is specific to sql server.
Reference:
isnull() - msdn
coalesce() - msdn
Related
While creating some report I need to come up with a query which takes the month and year as parameter and gives me a result where all the name of the days are as columns.
For example:
Idea is to cross join with my existing dataset and present that in the report. Again if there is any other way to show this kind of data in the RDL report that would be fine also.
You can use cte recursion make a Calendar table for this month.
datename get columns name.
day get month day number.
then connected condition aggregate function SQL string for the pivot.
final, dynamic SQL executed the SQL dynamically.
DECLARE #col AS NVARCHAR(MAX) ='',
#query AS NVARCHAR(MAX);
;WITH Calendar AS(
SELECT dateadd(month,datediff(month,0,getdate()),0) startdate,dateadd(month,datediff(month,0,getdate()),31) enddate
UNION ALL
SELECT startdate + 1 , enddate
FROM Calendar
WHERE startdate + 1 < enddate
), CalendarPivot as (
SELECT datename(weekday,startdate) dayname,
day(startdate) daynum
FROM Calendar
)
SELECT #col = #col + 'MAX(CASE WHEN daynum = '+cast(daynum as varchar(5))+' THEN daynum END) '+ dayname +','
FROM CalendarPivot t1
set #col = substring(#col,0,len(#col))
set #query = '
;WITH Calendar AS(
SELECT dateadd(month,datediff(month,0,getdate()),0) startdate,dateadd(month,datediff(month,0,getdate()),31) enddate
UNION ALL
SELECT startdate + 1 , enddate
FROM Calendar
WHERE startdate + 1 < enddate
), CalendarPivot as(
SELECT datename(weekday,startdate) dayname,
day(startdate) daynum
FROM Calendar
)
SELECT ' + #col + '
from CalendarPivot'
execute(#query)
sqlfiddle
NOTE
getdate() can change to use your parameter.
I have a stored procedure that accepts two Dates. In my stored procedure, I need to create a temp table with the months in between the two dates as columns.
For example,
If the user passes in
1/1/2016 , 8/1/2016
I need a temp table with the columns:
January February March April May June July August
How could I create this type of temp table with columns created in this manner? With the columns being based on the two dates passed in?
The following script should get you started (and almost there):
declare #start_date DATE = '20160101'
declare #end_date DATE = '20160801'
;WITH CTE AS
(
SELECT #start_date AS cte_start_date, DATENAME(month, #start_date) AS Name,
CAST(' ALTER TABLE #myTemp ADD ' + DATENAME(month, #start_date) + ' INT ' + CHAR(13) + CHAR(10) AS VARCHAR(8000)) AS SqlStr
UNION ALL
SELECT DATEADD(MONTH, 1, cte_start_date), DATENAME(month, DATEADD(MONTH, 1, cte_start_date)) AS Name,
CAST(SqlStr + ' ALTER TABLE #myTemp ADD ' + DATENAME(month, DATEADD(MONTH, 1, cte_start_date)) + ' INT ' + CHAR(13) + CHAR(10) AS VARCHAR(8000))
FROM CTE
WHERE DATEADD(MONTH, 1, cte_start_date) <= #end_date
)
SELECT cte_start_date, Name, SqlStr
FROM CTE
Using a recursive-CTE it generates a loop between start and end date, and for each month it computes its string representation and also creates a alter script to add the columns to a temporary table.
The CTE computes the SQL script gradually, so that the final script is on the last line.
Try This ....
declare #start_date DATE = '20160101'
declare #end_date DATE = '20160801'
;WITH CTE AS
(
SELECT #start_date AS cte_start_date, DATENAME(month, #start_date) AS NAME , 0 AS Coun
UNION ALL
SELECT DATEADD(MONTH, 1, cte_start_date), DATENAME(month, DATEADD(MONTH, 1, cte_start_date)) AS NAME , 0 AS Coun
FROM CTE
WHERE DATEADD(MONTH, 1, cte_start_date) <= #end_date
)
SELECT Coun,Name
INTO #tmp1
FROM CTE
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + Name
from #tmp1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ' + #cols + ' from
(
select Coun,Name
from #tmp1
) x
pivot
(
MAX(Coun)
for Name in (' + #cols + ')
) p '
execute(#query);
DROP TABLE #tmp1
It Will Return OutPut Like your expected output .......
Columns [today], [30days ago], [60days ago], [90 days ago]
I have a table the will provide me with the sales number fro today and 30days ago, 60 days ago and 90 days ago,
But the issue I have is to figure out what the actual date was 60 days ago.
I wanted to update my script not show me 60days ago but to show me the actual date 60days ago. I want to make my columns dynamic so i get the actual date 90days ago.
Can anyone help me here?
Please remember this is a long scripts and 10 columns and I want to change each column to show me the actual date and not the 90 days ago.
You can use GETDATE() and DATEADD() to get these values:
SELECT CONVERT(DATE, GETDATE()) AS Today,
CONVERT(DATE, DATEADD(DAY, -30, GETDATE())) Minus30
Converting to DATE simply takes off the time portion.
Produces:
Today Minus30
2015-06-08 2015-05-09
To use these values you can assign a few variables and set the values to be used later in your code:
DECLARE #today DATE, #Minus30 DATE
SELECT #today = CONVERT(DATE, GETDATE()),
#Minus30 = CONVERT(DATE, DATEADD(DAY, -30, GETDATE()))
PRINT #today
PRINT #Minus30
To use the values as column names, you'll need to use some dynamic SQL:
DECLARE #today NVARCHAR(15), #Minus30 NVARCHAR(15)
SELECT #today = CONVERT(NVARCHAR(15), CONVERT(DATE, GETDATE())),
#Minus30 = CONVERT(NVARCHAR(15), CONVERT(DATE, DATEADD(DAY, -30, GETDATE())))
EXEC ('SELECT ''30'' AS ''' + #today + ''', ''123'' AS ''' + #Minus30 + '''')
Produces:
2015-06-08 2015-05-09
30 123
This is best handled in the client code that retrieves the sql results. Using c# as an example:
string sql = String.Format(
"SELECT [today] as [{0}], [30days ago] as [{1}], [60days ago] as [{2}], [90 days ago] as [{3}] FROM [MyTable]",
DateTime.Today.ToShortDateString(),
DateTime.Today.AddDays(-30).ToShortDateString(),
DateTime.Today.AddDays(-60).ToShortDateString(),
DateTime.Today.AddDays(-90).ToShortDateString());
If you really need to, you could put the same string logic into procedural sql, and save that to a stored procedure. But don't do that.
I haven't actually tried doing this, but you can try to use an sp_rename statement using the variable names that #Tanner suggested:
DECLARE #Minus30 DATE
SELECT #Minus30 = CONVERT(DATE, DATEADD(DAY, -30, GETDATE()))
EXEC sp_rename 'Table.[30days ago]', #Minus30, 'COLUMN'
If you are trying to build a list of dynamic date columns, you could do this using dynamic pivot method something like this:
Creating a Test Table
create table pvtTbl(DateColumns varchar(50),sales money,employeeid int);
insert into pvtTbl
select cast(getdate() as date) datecol,1249.00 as sales,123 employeeid
UNION
select cast(dateadd(day,-30,getdate()) as date) datecol,15615.00 as sales,456 employeeid
UNION
select cast(dateadd(day,-60,getdate()) as date) datecol,125583.00 sales,356 employeeid
UNION
select cast(dateadd(day,-90,getdate()) as date) datecol,25571.00 sales,859 employeeid
This part, I am just creating as an example to build the pivot table. But, you could actually create this step as an insert statement to a temp table in your stored proc, so that it will be updated each time to dynamically build your date columns using this query:
Query to build a list of needed date values
select cast(getdate() as date) datecol
UNION
select cast(dateadd(day,-30,getdate()) as date) datecol
UNION
select cast(dateadd(day,-60,getdate()) as date) datecol
UNION
select cast(dateadd(day,-90,getdate()) as date) datecol
After that, all you will need to do is build a dynamic pivot using STUFF function and XML path to first build a comma separated list of date values and then pivot it dynamically to output it as your columns like this:
Dynamic Pivot to build date columns
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.DateColumns)
FROM pvtTbl c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT employeeid,' + #cols + ' from
(
select sales,employeeid,DateColumns
from pvtTbl
) x
pivot
(
sum(sales)
for DateColumns in (' + #cols + ')
) p '
execute(#query)
Anyway, this would be a good idea if you build this as a stored procedure. Let me know if you need any more help.
Demo
I need to write a stored procedure that takes 2 date parameters, sums up some data for that date, and returns a row with the dates inbetween as columns.
Im not sure where to start.
Lets say my stored procedure looks like this:
spGetAccountBalanceByDay(DateTime startDate, DateTime endDate)
I would like the column name to be formatted like this: F_{0}{1}{2} where {0} = year, {1} = Month, and {2} = Day.
so for the date 13/12/2014, my column would be called f_2014_12_13. I have a datasource that has dynamic properties which match ( as the grid in question can be run for any date range )
So in the SQL stored procedure, I want to loop between the 2 dates, sum the account balance for each date, and put the data in the column for that day.
So my table would look something like this returned by the stored procedure:
Account Ref | F_2014_12_13 | F_2014_12_14 | F_2014_12_15
------------------------------------------
ABB001 100 150 0
These queries can return one or more rows, I just need to know what function In SQL i should be looking to use, I know its possible to select columns dynamically just not sure how to do it.
Any advice would be appreciated.
Combining a few things I have found across the internet this is the solution I have come up with:
DECLARE #Columns VARCHAR(MAX)
DECLARE #StartDate AS DATETIME
DECLARE #EndDate AS DATETIME
DECLARE #Query AS VARCHAR(MAX)
SET #StartDate = '01 Jan 2012'
SET #EndDate = '31 Mar 2012'
;WITH dateRange as
(
SELECT [Date] = DATEADD(dd, 1, DATEADD(dd, -1,#startDate))
WHERE DATEADD(dd, 1, #startDate) < DATEADD(dd, 1, #endDate)
UNION ALL
SELECT DATEADD(dd, 1, [Date])
FROM dateRange
WHERE DATEADD(dd, 1, [Date]) < DATEADD(dd, 1,#endDate)
)
SELECT #Columns = COALESCE(#Columns, '[') + CONVERT(VARCHAR, [Date], 111) + '],['
FROM dateRange
OPTION (maxrecursion 0)
--delete last two chars of string (the ending ',[') and store columns in variable
SET #Columns = SUBSTRING(#Columns, 1, LEN(#Columns)-2)
SELECT #Columns
SET #Query =
'
SELECT *
FROM
(
SELECT
[PLSupplierAccount].[SupplierAccountNumber],
[PLSupplierAccount].[SupplierAccountName],
[PLPostedSupplierTran].[DueDate],
[PLPostedSupplierTran].[GoodsValueInAccountCurrency] * [PLPostedSupplierTran].[DocumentToBaseCurrencyRate] AS [Value]
FROM [PLPostedSupplierTran]
INNER JOIN [PLSupplierAccount]
ON [PLSupplierAccount].[PLSupplierAccountID]
= [PLPostedSupplierTran].[PLSupplierAccountID]
WHERE [PLPostedSupplierTran].[DueDate]>= ''' + CONVERT(VARCHAR(50), #StartDate, 111) + ''' AND [PLPostedSupplierTran].[DueDate]<= ''' + CONVERT(VARCHAR(50), #EndDate, 111) + '''
) src
PIVOT
(
SUM([Value])
FOR src.[DueDate] IN (' + #Columns + ')
) AS PivotView
'
EXEC (#Query)
At the end of my sql, I am using the following code. Is there any way of replacing the fixed strings [2011/07/14], [2011/07/16], etc, to GetDate() value?
PIVOT
(
count([AppointmentsBooked])
FOR [date] IN ([2011/07/14], [2011/07/16], [2011/07/17],[2011/07/18],[2011/07/21])
) as pivottable
Can you try to use BETWEEN and DATEADD in following manner:
DECLARE #dates TABLE(value DateTime)
INSERT INTO #dates VALUES
(GETDATE()),
('2011/07/9'),
('2011/07/17'),
('2011/07/18'),
('2011/07/21')
SELECT value FROM #dates
WHERE value BETWEEN GETDATE() AND DATEADD(day, 5, GETDATE())
You can create a string of all dates which is comma seperated with '[' and ']' before and after each date. assign this string to a string variable (#dates) and use the string spit method to split all dates inside the pivot query.
this question was posted about a year ago. i don't care. i have some code that might be exactly what the OP wanted.... i'm sure he'll never come back and chose an answer but still.... all i want to do is count the records by month with a pivot for a few tables and ultimately compare the number of records for each month for each table. however... in this code there is only one table (rt_taco_15m) but that doesn't matter. i just haven't written the rest to completely fit my needs. but i think it fits the needs of the OP or at least gets him on a good start.... if he truly has been waiting a year on this problem. lol.
if object_id('tempdb..#temp') is not null drop table #temp
if object_id('tempdb..#temp2') is not null drop table #temp2
if object_id('tempdb..#temp3') is not null drop table #temp3
declare #start_date as datetime
set #start_date = cast('1-1-2012' as datetime)
declare #end_date as datetime
set #end_date = cast('9-1-2012' as datetime)
;with cte as (
select #start_date as [start],
dateadd(month, 1, #start_date) as [end]
union all
select dateadd(month, 1, [start]) as [start],
dateadd(month, 1, dateadd(month, 1, [start])) as [end]
from cte
where dateadd(month, 1, [start]) <= #end_date
)
(select 'rt_taco_15m' as table_name,
convert(varchar(10), [start], 101) as [start],
convert(varchar(10), [end], 101) as [end],
datename(month, [start]) as month_name,
cast([start] as integer) as orderby,
count(taco.taco_record_id) as [range_count]
into #temp
from cte
left outer join rt_taco_15m as taco
on taco.period >= cte.[start] and
taco.period < cte.[end]
group by cte.[start], cte.[end])
select table_name as table_name,
convert(varchar(10), getdate(), 101) as [start],
convert(varchar(10), getdate(), 101) as [end],
'Total' as month_name,
cast(dateadd(month,2,#end_date) as integer) as orderby,
range_sum as [range_count]
into #temp2
from (select table_name, sum([range_count]) as range_sum
from #temp group by table_name) as summed_up
select *
into #temp3
from (select * from #temp
union all
select * from #temp2) as x
order by orderby
select * from #temp3
declare #cols nvarchar(2000)
select #cols = stuff(
(select '],[' + month_name
from #temp3
order by orderby
for xml path('') )
, 1, 2, '') + ']'
print #cols
if object_id('tempdb..#temp2') is not null drop table #temp2
declare #query varchar(max)
set #query = N'
select table_name, ' + #cols + N'
from (select table_name, month_name, range_count
from #temp3) p
pivot ( sum(range_count) for month_name in ( '+ #cols +' ) ) as pvt'
execute(#query