Related
I am using SQL Server. I created a query:
SELECT
p.[accountNumber],
pf.fundid
FROM
[dbo].[Property] p
LEFT JOIN
dbo.propertyfundassessment pf ON p.id = pf.propertyid
LEFT JOIN
dbo.fund f ON pf.fundid = f.id
WHERE
p.accountnumber = '238492348' AND p.taxyear = 2018
This shows the data as:
accountNumber fundid
--------------------------
1 238492348 1004
2 238492348 1005
3 238492348 1006
4 238492348 1007
5 238492348 1008
6 238492348 1009
7 238492348 1022
8 238492348 1339
I am trying to some how pivot the table in order to make the table look like this instead:
accountNumber adv1 adv2 adv3 adv4 adv5 adv6 adv7 adv8
-----------------------------------------------------------------
1 238492348 1004 1005 1006 1007 1008 1009 1022 1339
Can someone assist me in how I can do this with SQL Server?
I have found this:
SELECT *
FROM
(SELECT ColNbr = ROW_NUMBER() OVER(PARTITION BY accountNUmber ORDER BY fundid,accountNumber)
,accountNumber
,fundid
FROM
#tmpS a
) src PIVOT(MAX(src.fundid) FOR src.ColNbr IN( [1]
,[2]
,[3]
,[4]
,[5]
,[6]
,[7]
,[8]
,[9]
,[10]
,[11]
,[12]
,[13]
,[14]
,[15]
,[16]
,[17]
,[18]
,[19]
,[20]
,[21]
,[22]
,[23]
,[24]
,[25]
)) pvt
And I am trying to combine the two queries to have it do it on the fly. Instead of trying to create a #tmpS table.
Any help would be greatly appreciated!
You can combine both queries like the following:
;WITH StartingData AS
(
SELECT
[accountNumber] = p.[accountNumber],
fundid = pf.fundid,
FundRanking = ROW_NUMBER() OVER (PARTITION BY p.[accountNumber] ORDER BY pf.fundid ASC) -- The order by is crucial for the pivot ordering later
FROM
[dbo].[Property] p
left join dbo.propertyfundassessment pf on p.id = pf.propertyid
left join dbo.fund f on pf.fundid = f.id
where
p.taxyear = 2018
)
SELECT
P.accountNumber,
P.[1],
P.[2],
P.[3],
P.[4],
P.[5],
P.[6],
P.[7],
P.[8],
P.[9],
P.[10],
P.[11],
P.[12],
P.[13],
P.[14],
P.[15],
P.[16],
P.[17],
P.[18],
P.[19],
P.[20],
P.[21],
P.[22],
P.[23],
P.[24],
P.[25]
FROM
StartingData AS S
PIVOT (
MAX(S.fundid) FOR S.FundRanking IN (
[1], [2], [3], [4], [5], [6], [7], [8], [9], [10],
[11], [12], [13], [14], [15], [16], [17], [18],
[19], [20], [21], [22], [23], [24], [25])
) AS P
Keep in mind what Tim said, either you hard-code the number of fundid you will be pivoting (this example hard-codes from 1 to 25), or you will have to use dynamic SQL to generate a pivot statement that dynamically pivots up to the max amount of fundid you might have to a particular accountNumber. This will generate a column for each inicial record, by accountNumber.
To make it dynamic use the following:
IF OBJECT_ID('tempdb..#AccountFunds') IS NOT NULL
DROP TABLE #AccountFunds
SELECT
[accountNumber] = p.[accountNumber],
fundid = pf.fundid,
FundRanking = ROW_NUMBER() OVER (PARTITION BY p.[accountNumber] ORDER BY pf.fundid ASC) -- The order by is crucial for the pivot ordering later
INTO
#AccountFunds
FROM
[dbo].[Property] p
left join dbo.propertyfundassessment pf on p.id = pf.propertyid
left join dbo.fund f on pf.fundid = f.id
where
p.taxyear = 2018
AND p.[accountNumber] = '238492348'
DECLARE #PivotValues VARCHAR(MAX) = STUFF (
(
SELECT DISTINCT
',' + QUOTENAME(CONVERT(VARCHAR(10), A.FundRanking))
FROM
#AccountFunds AS A
ORDER BY
',' + QUOTENAME(CONVERT(VARCHAR(10), A.FundRanking)) ASC
FOR XML
PATH ('')
),
1, 1, '')
DECLARE #SelectColumnAlias VARCHAR(MAX) = STUFF (
(
SELECT
',P.' + QUOTENAME(CONVERT(VARCHAR(10), A.FundRanking)) + ' AS adv' + CONVERT(VARCHAR(10), A.FundRanking)
FROM
#AccountFunds AS A
GROUP BY
A.FundRanking
ORDER BY
A.FundRanking ASC
FOR XML
PATH ('')
),
1, 1, '')
DECLARE #DynamicSQL VARCHAR(MAX) = '
SELECT
P.AccountNumber,
' + #SelectColumnAlias + '
FROM
#AccountFunds AS A
PIVOT (
MAX(A.fundid) FOR A.FundRanking IN (
' + #PivotValues + ')
) AS P '
--PRINT (#DynamicSQL) -- Use Print to check the query
EXEC (#DynamicSQL)
If you check the value of #PivotValues it's something like the following:
[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23],[24],[25]
The value of #SelectColumnAlias is:
P.[1] AS adv1,P.[2] AS adv2,P.[3] AS adv3,P.[4] AS adv4,P.[5] AS adv5,P.[6] AS adv6,P.[7] AS adv7,P.[8] AS adv8,P.[9] AS adv9,P.[10] AS adv10,P.[11] AS adv11,P.[12] AS adv12,P.[13] AS adv13,P.[14] AS adv14,P.[15] AS adv15,P.[16] AS adv16,P.[17] AS adv17,P.[18] AS adv18,P.[19] AS adv19,P.[20] AS adv20,P.[21] AS adv21,P.[22] AS adv22,P.[23] AS adv23,P.[24] AS adv24,P.[25] AS adv25
And finally the full expression:
SELECT
P.AccountNumber,
P.[1] AS adv1,P.[2] AS adv2,P.[3] AS adv3,P.[4] AS adv4,P.[5] AS adv5,P.[6] AS adv6,P.[7] AS adv7,P.[8] AS adv8,P.[9] AS adv9,P.[10] AS adv10,P.[11] AS adv11,P.[12] AS adv12,P.[13] AS adv13,P.[14] AS adv14,P.[15] AS adv15,P.[16] AS adv16,P.[17] AS adv17,P.[18] AS adv18,P.[19] AS adv19,P.[20] AS adv20,P.[21] AS adv21,P.[22] AS adv22,P.[23] AS adv23,P.[24] AS adv24,P.[25] AS adv25
FROM
#AccountFunds AS A
PIVOT (
MAX(A.fundid) FOR A.FundRanking IN (
[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23],[24],[25])
) AS P
Try this dynamic sql
IF OBJECT_ID('Tempdb..#temp')IS NOT NULL
DROP TABLE #temp
;WITH CTE(Id,accountNumber,fundid)
AS
(
SELECT 1,238492348,1004 UNION ALL
SELECT 2,238492348,1005 UNION ALL
SELECT 3,238492348,1006 UNION ALL
SELECT 4,238492348,1007 UNION ALL
SELECT 5,238492348,1008 UNION ALL
SELECT 6,238492348,1009 UNION ALL
SELECT 7,238492348,1022 UNION ALL
SELECT 8,238492348,1339
)
SELECT * ,'adv'+CAST(ROW_NUMBER()OVER(ORDER BY (SELECT 1)) AS VARCHAR(10)) AS tcol INTO #temp FROM CTE
DECLARE #Columns nvarchar(max),#IsnullColumns nvarchar(max)
,#Sql nvarchar(max)
SELECT #Columns= STUFF((SELECT ', '+tcol FROM #temp FOR XML PATH ('')),1,1,'')
SELECT #IsnullColumns=STUFF((SELECT ', '+'MAX('+QUOTENAME(tcol)+') AS ' +QUOTENAME(tcol) FROM #temp FOR XML PATH ('')),1,1,'')
SET #Sql='
SELECT accountNumber ,'+#IsnullColumns+'
FROM
(SELECT * FROM #temp
) AS SRC
PIVOT
(MAX(fundid) FOR tcol IN ('+#Columns+')
) AS PVT
GROUP BY accountNumber'
PRINT #Sql
EXEC (#Sql)
Result
accountNumber adv1 adv2 adv3 adv4 adv5 adv6 adv7 adv8
-----------------------------------------------------------------
238492348 1004 1005 1006 1007 1008 1009 1022 1339
I have two queries. One query pulls the information based on the orders that have been scheduled to them in a day by employee. The other query pulls the number of orders that have been completed and paid in a day, along with the total amount of the revenue from the orders.
I have scraped the forums to get the code together to get these queries, but I need to have both queries joined together. I want to use this information in report builder once I get it done. It's probably simple for someone, but it's confusing me as I'm far from any sort of expert with SQL.
I only need one day at the moment, but found this code and modified for now and hope that I can use it in the future when needing week and month data.
Any help would be appreciated.
Query 1
declare #dt DATE = '20160823'
set DATEFIRST 7;
set #dt = dateadd(week, datediff(week, '19050101', #dt), '19050101');
;with dt as
(
select
Technician = (CASE emp_id
WHEN 'CW697' THEN 'Joe Biggs'
WHEN 'DZ663' THEN 'Mimi Cassidy'
END),
dw = datepart(weekday, DATE)
from
dbo.ordemps
where
date >= #dt and date <dateadd(day, 7, #dt)
),
x AS
(
select
Technician, dw = coalesce(dw,8),
c = convert(varchar(11), COUNT(*))
from
dt
group by
grouping sets((Technician), (Technician,dw))
)
select
Technician,
[Sun] = coalesce([1], '-'),
[Mon] = coalesce([2], '-'),
[Tue] = coalesce([3], '-'),
[Wed] = coalesce([4], '-'),
[Thu] = coalesce([5], '-'),
[Fri] = coalesce([6], '-'),
[Sat] = coalesce([7], '-'),
TOTAL =[8]
from
x
PIVOT
(MAX(c) FOR dw IN([1],[2],[3],[4],[5],[6],[7],[8])) as pvt;
Query 2
select
case
when grouping(d.m)=15 then 'Year ' + cast(max(d.y) as varchar(10))
when grouping(date)=15 then datename(m, max(DATE)) + ' ' + cast(max(d.y) as varchar(10))
else cast(cast([date] as date) as varchar(255))
end as DATE,
TotalOrders = /*sum(Amount)*/convert(varchar(11), COUNT(*)),
TotalSales = sum(Amount),
Technician = (CASE recv_by
WHEN 'CW697' THEN 'Joe Biggs'
WHEN 'DZ663' THEN 'Mimi Cassidy'
END)
from
ordpay
cross apply
(select
datepart(yy, [date]),
datepart(m, [date])
) d(y, m)
where
[DATE] >= dateadd(day, datediff(day, 1, getdate()), 0)
and [date] < dateadd(day, datediff(day, 0, getdate()), 0)
group by
recv_by, d.y, rollup (d.m, date)
order by
d.y desc, grouping(d.m), d.m, grouping(DATE), DATE
At the easiest level you can use can join sub-queries. Your example is a little trickier because you are using CTEs which can't go in the subquery. To get you going in the right direction it should generally look like this:
with cte1 as (),
cte2 as ()
select *
from (
select *
from tables
where criteria -- subquery 1 (cannot contain order by)
) a
join ( -- could be a left join
select *
from tables
where criteria -- subquery 2 (cannot contain order by)
) b
on b.x = a.x --join criteria
where --additional criteria
order by --final sort
where subquery 1 would be your query 1 from select Technician to the end
and subquery 2 would be everything from your query 2 except the order by.
For further information on joins see the MSDN documentation
Since you aren't going to research much it doesn't seem here's a clunky and quick way, and keeps it simple for you, without having to explain a lot if we were to join the queries together more elegantly.
The first set of code has 2 common table expressions (CTE). So, select those results into a temp. Notice the --addition here
....
select
Technician,
[Sun] = coalesce([1], '-'),
[Mon] = coalesce([2], '-'),
[Tue] = coalesce([3], '-'),
[Wed] = coalesce([4], '-'),
[Thu] = coalesce([5], '-'),
[Fri] = coalesce([6], '-'),
[Sat] = coalesce([7], '-'),
TOTAL =[8]
into #someTemp --Addition here
from
....
Do the same for your second query...
...
into #someOtherTemp --Addition here
from
ordpay
...
Then join them togehter...
select t1.someColumns, t2.someOtherColumns
from #someTemp t1
inner join #someOtherTemp t2 on t1.column = t2.column
I have a column that contains a CRON expression that represents how often a user has to perform a task. I would like to build a view that lists the date and things to do for a given user. But I need to calculate my next CRON occurrence in a datetime in T-SQL. How interpreted my expression CRON in SQL?
example:
column value = [0 30 8 1 *?]
I would write:
SELECT CrontabSchedule ( '0 30 8 1 *?', GETDATE ()) FROM dbo.UserTasks
Someone has a solution ?
There is no built in function within SQL server to genrate Next date based on Cron expression. Only way would be, implemeting cron exprssions in C# and genrate it as dll,then regsiter DLL with SQL server CLR.
Refernces:
https://social.msdn.microsoft.com/Forums/en-US/3e0ebe5a-d452-4893-bcef-0a1e2520c076/convert-cron-expression-to-datetime?forum=transactsql
https://github.com/atifaziz/NCrontab/wiki/SQL-Server-Crontab
Maybe this link can give you a hint: https://ask.sqlservercentral.com/questions/117285/tsql-sql-function-for-a-cron-expression.html
The UDF presented in the link is as follow:
CREATE PROCEDURE Q117285.CreateJobScheduleFromCronExpression
#CronExpression varchar(200)
, #FieldDelimiterCharacter char(1) -- any whitespace character
AS
BEGIN;
DECLARE #CronExpressionArray TABLE
(
Seconds varchar(50)
, Minutes varchar(50)
, Hours varchar(50)
, DayOfMonth varchar(50)
, Month varchar(50)
, DayOfWeek varchar(50)
, Year varchar(50)
);
INSERT #CronExpressionArray
SELECT
[Seconds] = [1]
, [Minutes] = [2]
, [Hours] = [3]
, [DayOfMonth] = [4]
, [Month] = [5]
, [DayOfWeek] = [6]
, [Year] = [7]
FROM
(
SELECT ds.ItemNumber, ds.Item
FROM dbo.DelimitedSplit8K(#CronExpression, #FieldDelimiterCharacter) ds
) pSrc
PIVOT
(
MAX(pSrc.Item)
FOR pSrc.ItemNumber IN
([1], [2], [3], [4], [5], [6], [7])
) pvt
;
SELECT * FROM #CronExpressionArray;
END;
The DelimitedSplit8K UDF was extracted from the article: http://www.sqlservercentral.com/articles/Tally+Table/72993/
CREATE FUNCTION [dbo].[DelimitedSplit8K]
--===== Define I/O parameters
(#pString VARCHAR(8000), #pDelimiter CHAR(1))
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE! IT WILL KILL PERFORMANCE!
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
-- enough to cover VARCHAR(8000)
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT TOP (ISNULL(DATALENGTH(#pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT 1 UNION ALL
SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(#pString,t.N,1) = #pDelimiter
),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
SELECT s.N1,
ISNULL(NULLIF(CHARINDEX(#pDelimiter,#pString,s.N1),0)-s.N1,8000)
FROM cteStart s
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(#pString, l.N1, l.L1)
FROM cteLen l
;
I needed to do something similar and eventually built my own tSQLcron
Currently it isn't SET based as it is a stored-procedure. An example usage is
DECLARE #out_is_cron_true BIT ;
EXEC tSQLcron.usp_is_date_in_cron_period
#cron_expression = N'* 0/15 * * * *' -- nvarchar(100)
, #validate_date = '2020-01-01 13:15:00' -- datetime
, #out_is_cron_true = #out_is_cron_true OUTPUT -- bit
IF (#out_is_cron_true = 1 )
BEGIN
PRINT 'DO SOMETHING';
END
I have the following query for Pivot, that works nicely.
WITH PivotData AS
(
SELECT fechaHora, idWatimetro, semana, hora, minuto,dia,mes ,ano,valor
FROM E_Registros_Watimetros
)
SELECT *
FROM PivotData
PIVOT(SUM(valor) FOR idWatimetro IN (
[1],[2],[3],[4],[5],[6],[7],[8],[9],[10]
) ) AS P
order by fechaHora, semana,dia,mes;
But now, I must be able to get Sum(valor) for every month or days or weeks or Year, but I´m trying without success, any help will be nice
Thank you for your time.
After suggestion of Pieter Geerkens, What I want to do is like this
The Sum(1),Sum(2),Sum(3)... Sum(10) for every Month.
The Month is an example, becuase this parameter can be, Year , day or weeks
Sorry for not to be very clear, my english is not very good.
You can do this with your query but you need to alter pivot source Query by remove the columns which not needed in final result. Try this.
WITH PivotData
AS (SELECT idWatimetro,
mes,
ano,
valor
FROM E_Registros_Watimetros)
SELECT *
FROM PivotData
PIVOT(Sum(valor)
FOR idWatimetro IN ( [1],
[2],
[3],
[4],
[5],
[6],
[7],
[8],
[9],
[10] ) ) AS P
You can add a second CTE to do the pivot and use the final SELECT to GROUP BY:
WITH PivotData AS
(
SELECT fechaHora, idWatimetro, semana, hora, minuto,dia,mes ,ano,valor
FROM E_Registros_Watimetros where ano = 2012
), tempData AS (
SELECT *
FROM PivotData
PIVOT(SUM(valor) FOR idWatimetro IN ([1],[2],[3],[4],[5]) ) AS P
)
SELECT MONTH(fechaHora) AS [Month],
SUM([1]) AS KW1,
SUM([2]) AS KW2,
SUM([3]) AS KW3
FROM tempData
GROUP BY MONTH(fechaHora)
Edit: The above query works on my SQL Server 2008 box.
My question is similar to this MySQL question, but intended for SQL Server:
Is there a function or a query that will return a list of days between two dates? For example, lets say there is a function called ExplodeDates:
SELECT ExplodeDates('2010-01-01', '2010-01-13');
This would return a single column table with the values:
2010-01-01
2010-01-02
2010-01-03
2010-01-04
2010-01-05
2010-01-06
2010-01-07
2010-01-08
2010-01-09
2010-01-10
2010-01-11
2010-01-12
2010-01-13
I'm thinking that a calendar/numbers table might be able to help me here.
Update
I decided to have a look at the three code answers provided, and the results of the execution - as a % of the total batch - are:
Rob Farley's answer : 18%
StingyJack's answer : 41%
KM's answer : 41%
Lower is better
I have accepted Rob Farley's answer, as it was the fastest, even though numbers table solutions (used by both KM and StingyJack in their answers) are something of a favourite of mine. Rob Farley's was two-thirds faster.
Update 2
Alivia's answer is much more succinct. I have changed the accepted answer.
this few lines are the simple answer for this question in sql server.
WITH mycte AS
(
SELECT CAST('2011-01-01' AS DATETIME) DateValue
UNION ALL
SELECT DateValue + 1
FROM mycte
WHERE DateValue + 1 < '2021-12-31'
)
SELECT DateValue
FROM mycte
OPTION (MAXRECURSION 0)
Try something like this:
CREATE FUNCTION dbo.ExplodeDates(#startdate datetime, #enddate datetime)
returns table as
return (
with
N0 as (SELECT 1 as n UNION ALL SELECT 1)
,N1 as (SELECT 1 as n FROM N0 t1, N0 t2)
,N2 as (SELECT 1 as n FROM N1 t1, N1 t2)
,N3 as (SELECT 1 as n FROM N2 t1, N2 t2)
,N4 as (SELECT 1 as n FROM N3 t1, N3 t2)
,N5 as (SELECT 1 as n FROM N4 t1, N4 t2)
,N6 as (SELECT 1 as n FROM N5 t1, N5 t2)
,nums as (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) as num FROM N6)
SELECT DATEADD(day,num-1,#startdate) as thedate
FROM nums
WHERE num <= DATEDIFF(day,#startdate,#enddate) + 1
);
You then use:
SELECT *
FROM dbo.ExplodeDates('20090401','20090531') as d;
Edited (after the acceptance):
Please note... if you already have a sufficiently large nums table then you should use:
CREATE FUNCTION dbo.ExplodeDates(#startdate datetime, #enddate datetime)
returns table as
return (
SELECT DATEADD(day,num-1,#startdate) as thedate
FROM nums
WHERE num <= DATEDIFF(day,#startdate,#enddate) + 1
);
And you can create such a table using:
CREATE TABLE dbo.nums (num int PRIMARY KEY);
INSERT dbo.nums values (1);
GO
INSERT dbo.nums SELECT num + (SELECT COUNT(*) FROM nums) FROM nums
GO 20
These lines will create a table of numbers containing 1M rows... and far quicker than inserting them one by one.
You should NOT create your ExplodeDates function using a function that involves BEGIN and END, as the Query Optimizer becomes unable to simplify the query at all.
This does exactly what you want, modified from Will's earlier post. No need for helper tables or loops.
WITH date_range (calc_date) AS (
SELECT DATEADD(DAY, DATEDIFF(DAY, 0, '2010-01-13') - DATEDIFF(DAY, '2010-01-01', '2010-01-13'), 0)
UNION ALL SELECT DATEADD(DAY, 1, calc_date)
FROM date_range
WHERE DATEADD(DAY, 1, calc_date) <= '2010-01-13')
SELECT calc_date
FROM date_range;
DECLARE #MinDate DATETIME = '2012-09-23 00:02:00.000',
#MaxDate DATETIME = '2012-09-25 00:00:00.000';
SELECT TOP (DATEDIFF(DAY, #MinDate, #MaxDate) + 1) Dates = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #MinDate)
FROM sys.all_objects a CROSS JOIN sys.all_objects b;
I'm an oracle guy, but I believe MS SQL Server has support for the connect by clause:
select sysdate + level
from dual
connect by level <= 10 ;
The output is:
SYSDATE+LEVEL
05-SEP-09
06-SEP-09
07-SEP-09
08-SEP-09
09-SEP-09
10-SEP-09
11-SEP-09
12-SEP-09
13-SEP-09
14-SEP-09
Dual is just a 'dummy' table that comes with oracle (it contains 1 row and the word 'dummy' as the value of the single column).
A few ideas:
If you need the list dates in order to loop through them, you could have a Start Date and Day Count parameters and do a while loop whilst creating the date and using it?
Use C# CLR Stored Procedures and write the code in C#
Do this outside the database in code
Would all these dates be in the database already or do you just want to know the days between the two dates? If it's the first you could use the BETWEEN or <= >= to find the dates between
EXAMPLE:
SELECT column_name(s)
FROM table_name
WHERE column_name
BETWEEN value1 AND value2
OR
SELECT column_name(s)
FROM table_name
WHERE column_name
value1 >= column_name
AND column_name =< value2
All you have to do is just change the hard coded value in the code provided below
DECLARE #firstDate datetime
DECLARE #secondDate datetime
DECLARE #totalDays INT
SELECT #firstDate = getDate() - 30
SELECT #secondDate = getDate()
DECLARE #index INT
SELECT #index = 0
SELECT #totalDays = datediff(day, #firstDate, #secondDate)
CREATE TABLE #temp
(
ID INT NOT NULL IDENTITY(1,1)
,CommonDate DATETIME NULL
)
WHILE #index < #totalDays
BEGIN
INSERT INTO #temp (CommonDate) VALUES (DATEADD(Day, #index, #firstDate))
SELECT #index = #index + 1
END
SELECT CONVERT(VARCHAR(10), CommonDate, 102) as [Date Between] FROM #temp
DROP TABLE #temp
A Bit late to the party, but I like this solution quite a bit.
CREATE FUNCTION ExplodeDates(#startDate DateTime, #endDate DateTime)
RETURNS table as
return (
SELECT TOP (DATEDIFF(DAY, #startDate, #endDate) + 1)
DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #startDate) AS DATE
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
)
Before you use my function, you need to set up a "helper" table, you only need to do this one time per database:
CREATE TABLE Numbers
(Number int NOT NULL,
CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
DECLARE #x int
SET #x=0
WHILE #x<8000
BEGIN
SET #x=#x+1
INSERT INTO Numbers VALUES (#x)
END
here is the function:
CREATE FUNCTION dbo.ListDates
(
#StartDate char(10)
,#EndDate char(10)
)
RETURNS
#DateList table
(
Date datetime
)
AS
BEGIN
IF ISDATE(#StartDate)!=1 OR ISDATE(#EndDate)!=1
BEGIN
RETURN
END
INSERT INTO #DateList
(Date)
SELECT
CONVERT(datetime,#StartDate)+n.Number-1
FROM Numbers n
WHERE Number<=DATEDIFF(day,#StartDate,CONVERT(datetime,#EndDate)+1)
RETURN
END --Function
use this:
select * from dbo.ListDates('2010-01-01', '2010-01-13')
output:
Date
-----------------------
2010-01-01 00:00:00.000
2010-01-02 00:00:00.000
2010-01-03 00:00:00.000
2010-01-04 00:00:00.000
2010-01-05 00:00:00.000
2010-01-06 00:00:00.000
2010-01-07 00:00:00.000
2010-01-08 00:00:00.000
2010-01-09 00:00:00.000
2010-01-10 00:00:00.000
2010-01-11 00:00:00.000
2010-01-12 00:00:00.000
2010-01-13 00:00:00.000
(13 row(s) affected)
Perhaps if you wish to go an easier way, this should do it.
WITH date_range (calc_date) AS (
SELECT DATEADD(DAY, DATEDIFF(DAY, 0, CURRENT_TIMESTAMP) - 6, 0)
UNION ALL SELECT DATEADD(DAY, 1, calc_date)
FROM date_range
WHERE DATEADD(DAY, 1, calc_date) < CURRENT_TIMESTAMP)
SELECT calc_date
FROM date_range;
But the temporary table is a very good approach also. Perhaps shall you also consider a populated calendar table.
Definately a numbers table, though tyou may want to use Mark Redman's idea of a CLR proc/assembly if you really need the performance.
How to create the table of dates (and a super fast way to create a numbers table)
/*Gets a list of integers into a temp table (Jeff Moden's idea from SqlServerCentral.com)*/
SELECT TOP 10950 /*30 years of days*/
IDENTITY(INT,1,1) as N
INTO #Numbers
FROM Master.dbo.SysColumns sc1,
Master.dbo.SysColumns sc2
/*Create the dates table*/
CREATE TABLE [TableOfDates](
[fld_date] [datetime] NOT NULL,
CONSTRAINT [PK_TableOfDates] PRIMARY KEY CLUSTERED
(
[fld_date] ASC
)WITH FILLFACTOR = 99 ON [PRIMARY]
) ON [PRIMARY]
/*fill the table with dates*/
DECLARE #daysFromFirstDateInTheTable int
DECLARE #firstDateInTheTable DATETIME
SET #firstDateInTheTable = '01/01/1998'
SET #daysFromFirstDateInTheTable = (SELECT (DATEDIFF(dd, #firstDateInTheTable ,GETDATE()) + 1))
INSERT INTO
TableOfDates
SELECT
DATEADD(dd,nums.n - #daysFromFirstDateInTheTable, CAST(FLOOR(CAST(GETDATE() as FLOAT)) as DateTime)) as FLD_Date
FROM #Numbers nums
Now that you have a table of dates, you can use a function (NOT A PROC) like KM's to get the table of them.
CREATE FUNCTION dbo.ListDates
(
#StartDate DATETIME
,#EndDate DATETIME
)
RETURNS
#DateList table
(
Date datetime
)
AS
BEGIN
/*add some validation logic of your own to make sure that the inputs are sound.Adjust the rest as needed*/
INSERT INTO
#DateList
SELECT FLD_Date FROM TableOfDates (NOLOCK) WHERE FLD_Date >= #StartDate AND FLD_Date <= #EndDate
RETURN
END
Declare #date1 date = '2016-01-01'
,#date2 date = '2016-03-31'
,#date_index date
Declare #calender table (D date)
SET #date_index = #date1
WHILE #date_index<=#date2
BEGIN
INSERT INTO #calender
SELECT #date_index
SET #date_index = dateadd(day,1,#date_index)
IF #date_index>#date2
Break
ELSE
Continue
END
-- ### Six of one half dozen of another. Another method assuming MsSql
Declare #MonthStart datetime = convert(DateTime,'07/01/2016')
Declare #MonthEnd datetime = convert(DateTime,'07/31/2016')
Declare #DayCount_int Int = 0
Declare #WhileCount_int Int = 0
set #DayCount_int = DATEDIFF(DAY, #MonthStart, #MonthEnd)
select #WhileCount_int
WHILE #WhileCount_int < #DayCount_int + 1
BEGIN
print convert(Varchar(24),DateAdd(day,#WhileCount_int,#MonthStart),101)
SET #WhileCount_int = #WhileCount_int + 1;
END;
In case you want to print years starting from a particular year till current date. Just altered the accepted answer.
WITH mycte AS
(
SELECT YEAR(CONVERT(DATE, '2006-01-01',102)) DateValue
UNION ALL
SELECT DateValue + 1
FROM mycte
WHERE DateValue + 1 < = YEAR(GETDATE())
)
SELECT DateValue
FROM mycte
OPTION (MAXRECURSION 0)
This query works on Microsoft SQL Server.
select distinct format( cast('2010-01-01' as datetime) + ( a.v / 10 ), 'yyyy-MM-dd' ) as aDate
from (
SELECT ones.n + 10 * tens.n + 100 * hundreds.n + 1000 * thousands.n as v
FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) hundreds(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) thousands(n)
) a
where format( cast('2010-01-01' as datetime) + ( a.v / 10 ), 'yyyy-MM-dd' ) < cast('2010-01-13' as datetime)
order by aDate asc;
Now let's look at how it works.
The inner query merely returns a list of integers from 0 to 9999. It will give us a range of 10,000 values for calculating dates. You can get more dates by adding rows for ten_thousands and hundred_thousands and so forth.
SELECT ones.n + 10 * tens.n + 100 * hundreds.n + 1000 * thousands.n as v
FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) hundreds(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) thousands(n)
) a;
This part converts the string to a date and adds a number to it from the inner query.
cast('2010-01-01' as datetime) + ( a.v / 10 )
Then we convert the result into the format you want. This is also the column name!
format( cast('2010-01-01' as datetime) + ( a.v / 10 ), 'yyyy-MM-dd' )
Next we extract only the distinct values and give the column name an alias of aDate.
distinct format( cast('2010-01-01' as datetime) + ( a.v / 10 ), 'yyyy-MM-dd' ) as aDate
We use the where clause to filter in only dates within the range you want. Notice that we use the column name here since SQL Server does not accept the column alias, aDate, within the where clause.
where format( cast('2010-01-01' as datetime) + ( a.v / 10 ), 'yyyy-MM-dd' ) < cast('2010-01-13' as datetime)
Lastly, we sort the results.
order by aDate asc;
if you're in a situation like me where procedures and functions are prohibited, and your sql user does not have permissions for insert, therefore insert not allowed, also "set/declare temporary variables like #c is not allowed", but you want to generate a list of dates in a specific period, say current year to do some aggregation, use this
select * from
(select adddate('1970-01-01',t4*10000 + t3*1000 + t2*100 + t1*10 + t0) gen_date from
(select 0 t0 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
(select 0 t1 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
(select 0 t2 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
(select 0 t3 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
(select 0 t4 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t4) v
where gen_date between '2017-01-01' and '2017-12-31'
WITH TEMP (DIA, SIGUIENTE_DIA ) AS
(SELECT
1,
CAST(#FECHAINI AS DATE)
FROM
DUAL
UNION ALL
SELECT
DIA,
DATEADD(DAY, DIA, SIGUIENTE_DIA)
FROM
TEMP
WHERE
DIA < DATEDIFF(DAY, #FECHAINI, #FECHAFIN)
AND DATEADD(DAY, 1, SIGUIENTE_DIA) <= CAST(#FECHAFIN AS DATE)
)
SELECT
SIGUIENTE_DIA AS CALENDARIO
FROM
TEMP
ORDER BY
SIGUIENTE_DIA
The detail is on the table DUAL but if your exchange this table for a dummy table this works.
SELECT dateadd(dd,DAYS,'2013-09-07 00:00:00') DATES
INTO #TEMP1
FROM
(SELECT TOP 365 colorder - 1 AS DAYS from master..syscolumns
WHERE id = -519536829 order by colorder) a
WHERE datediff(dd,dateadd(dd,DAYS,'2013-09-07 00:00:00'),'2013-09-13 00:00:00' ) >= 0
AND dateadd(dd,DAYS,'2013-09-07 00:00:00') <= '2013-09-13 00:00:00'
SELECT * FROM #TEMP1
Answer is avialbe here
How to list all dates between two dates
Create Procedure SelectDates(#fromDate Date, #toDate Date)
AS
BEGIN
SELECT DATEADD(DAY,number,#fromDate) [Date]
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY,number,#fromDate) < #toDate
END
DECLARE #StartDate DATE = '2017-09-13', #EndDate DATE = '2017-09-16'
SELECT date FROM ( SELECT DATE = DATEADD(DAY, rn - 1, #StartDate) FROM (
SELECT TOP (DATEDIFF(DAY, #StartDate, DATEADD(DAY,1,#EndDate)))
rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM sys.all_objects AS s1
CROSS JOIN sys.all_objects AS s2
ORDER BY s1.[object_id] ) AS x ) AS y
Result:
2017-09-13
2017-09-14
2017-09-15
2017-09-16