June 2, 2011 at 8:50 am
Hi
Can anyone help me write a store procedure to increment a month
For example
The current month is January 10 I want to show the last 12 months so from January 10 - December 10.
Now if the current month is February 10 I want to increment it to show from February 10 - January 11 and so on
Jan 10, Feb 10, Mar 10, April 10 , May 10, June 10, July 10, Aug 10, Sep 10, Oct 10, Nov 10, Dec 10
Can anyone help me or provide me with similar examples??
Thanks in advance
__________________________________________________________________________________
Steve J
Jnr Developer
BSc(Hon)
June 2, 2011 at 9:07 am
Is this a homework question? I ask because I answered something almost identical to this a couple of days ago (here). . .
One way would be to do it like this: -
DECLARE @date DATETIME, @number INT
SET @date = '2011-06-06 00:00:00'
SET @number = 12
;WITH CTE AS (
SELECT 0 AS months
UNION ALL
SELECT months + 1
FROM CTE
WHERE months < @number-1)
SELECT DATEADD(MONTH,months,@date)
FROM CTE
You would need to format the result as you require.
June 2, 2011 at 9:29 am
Hi skcadavre,
Thanks for your reply, its not a homework question i'm in the process of designing a SSRS report and i need this section in the store procedure in order to filter the data been trying to figure it out for ages.
Question I'm assuming you would need to include a convert @set = date to remove the time??
__________________________________________________________________________________
Steve J
Jnr Developer
BSc(Hon)
June 2, 2011 at 9:36 am
Like so: -
DECLARE @date DATETIME, @number INT
SET @date = '2011-06-06 00:00:00'
SET @number = 12
;WITH CTE AS (
SELECT 0 AS months
UNION ALL
SELECT months + 1
FROM CTE
WHERE months < @number-1)
SELECT DATEADD(MONTH,months,@date),
CONVERT(VARCHAR(10),DATEADD(MONTH,months,@date), 101) AS mmddyyyy,
CONVERT(VARCHAR(10),DATEADD(MONTH,months,@date), 103) AS ddmmyyyy,
CONVERT(VARCHAR(12),DATEADD(MONTH,months,@date), 107) AS MMMddyyyy,
CONVERT(VARCHAR(12),DATEADD(MONTH,months,@date), 113) AS ddMMMyyyy
FROM CTE
June 2, 2011 at 9:41 am
Brilliant i can work with that
__________________________________________________________________________________
Steve J
Jnr Developer
BSc(Hon)
June 2, 2011 at 9:59 am
Jnrstevej (6/2/2011)
Hi skcadavre,Thanks for your reply, its not a homework question i'm in the process of designing a SSRS report and i need this section in the store procedure in order to filter the data been trying to figure it out for ages.
Question I'm assuming you would need to include a convert @set = date to remove the time??
Since you are using SSRS, you should use the formatting available within SSRS to format the datetime field the way you want it.
If you want to eliminate the time for filtering purposes (before it gets to SSRS), using CONVERT() is one of the worst possible ways to approach it. You should use the date/time functions to manipulate date/time data. Specifically, the following two expressions will give you midnight today and midnight on the first of the current month.
SELECT
Cast(Datediff(Day, 0, GetDate()) as datetime) AS MidnightToday
, DateAdd(Month, DateDiff(Month, 0, GetDate()), 0) AS FirstOfCurrentMonth -- at midnight
Drew
J. Drew Allen
Business Intelligence Analyst
Philadelphia, PA
June 3, 2011 at 6:10 am
drew.allen (6/2/2011)
Jnrstevej (6/2/2011)
Hi skcadavre,Thanks for your reply, its not a homework question i'm in the process of designing a SSRS report and i need this section in the store procedure in order to filter the data been trying to figure it out for ages.
Question I'm assuming you would need to include a convert @set = date to remove the time??
Since you are using SSRS, you should use the formatting available within SSRS to format the datetime field the way you want it.
I agree with using SSRS formatting. Any data formatting is a presentation layer task, so I never like to do it in DB.
drew.allen (6/2/2011)
If you want to eliminate the time for filtering purposes (before it gets to SSRS), using CONVERT() is one of the worst possible ways to approach it. You should use the date/time functions to manipulate date/time data. Specifically, the following two expressions will give you midnight today and midnight on the first of the current month.
SELECT
Cast(Datediff(Day, 0, GetDate()) as datetime) AS MidnightToday
, DateAdd(Month, DateDiff(Month, 0, GetDate()), 0) AS FirstOfCurrentMonth -- at midnight
Drew
When you post sweeping comments, you should probably include a test script that proves what you're claiming. Note, I'm not saying you're wrong. As I said before, I use the presentation layer when formatting data so I've never really done any formatting on large data sets, but your sweeping comment without any evidence doesn't change the opinions of anyone.
June 3, 2011 at 11:45 am
skcadavre (6/3/2011)
When you post sweeping comments, you should probably include a test script that proves what you're claiming. Note, I'm not saying you're wrong. As I said before, I use the presentation layer when formatting data so I've never really done any formatting on large data sets, but your sweeping comment without any evidence doesn't change the opinions of anyone.
I'm not getting paid to post here. I don't feel the need to post test scripts for something that is documented elsewhere and widely known, but if you want to pay me to do so, I will gladly post the requested scripts.
Drew
J. Drew Allen
Business Intelligence Analyst
Philadelphia, PA
June 3, 2011 at 11:17 pm
skcadavre (6/3/2011)
When you post sweeping comments, you should probably include a test script that proves what you're claiming. {snip}... but your sweeping comment without any evidence doesn't change the opinions of anyone.
Ah... ya got me between a rock and a hard spot here. 😉 I've dedicated a huge amount of time to dispelling SQL myths on the internet, especially when someone claims performance, so I'm right there with you when it comes to any claims of performance. I like to see people prove such statements.
However... not only has it been proven that conversion of dates to VARCHARs is comparatively dreadfully slow, but the method that Drew used is one of the faster methods there is and it will absolutely blow the doors off of any conversion to a VARCHAR using CONVERT. So I can understand why he's reluctant to spend any additional time on what has become well known previously proven fact.
That not withstanding, I'll be happy to prove it because it's a question asked so often that I got tired of writing ad hoc code for it and built a canned example. Drew's method is called DATEDIFF/DATETIME in the following code. Compare it to any of the conversions to VARCHAR and see why he was a bit adamant about it.
/**********************************************************************************************************************
Purpose:
Create a voluminous test table with highly randomized DATETIME data.
--Jeff Moden
**********************************************************************************************************************/
--===== Do this test in a nice safe place that everyone has.
USE TempDB
;
--===== Conditionally drop the test table to make reruns easier
IF OBJECT_ID('dbo.JBMTest','U') IS NOT NULL
DROP TABLE dbo.JBMTest
;
--===== Create and populate a 1,000,000 row test table.
-- "SomeID" has a range of 1 to 1,000,000 unique numbers
-- "SomeDateTimeTime" has a range of >=01/01/2000 and <01/01/2020 non-unique date/times
SELECT TOP 1000000
SomeID = IDENTITY(INT,1,1),
SomeDateTime = RAND(CHECKSUM(NEWID())) * DATEDIFF(dd,'2000','2020') + CAST('2000' AS DATETIME)
INTO dbo.JBMTest
FROM sys.all_columns ac1
CROSS JOIN sys.all_columns ac2
;
--===== This will take the display out of the picture so we
-- can measure the true processing time in memory.
DECLARE @BitBucketDATETIME DATETIME
PRINT '========== BASELINE =========='
SET STATISTICS TIME ON
SELECT @BitBucketDATETIME = SomeDateTime
FROM dbo.JBMTest WITH (NOLOCK)
SET STATISTICS TIME OFF
PRINT REPLICATE('=',80)
PRINT '========== DATEADD/DATEDIFF 1 =========='
SET STATISTICS TIME ON
SELECT @BitBucketDATETIME = DATEADD(d, 0, DATEDIFF(d, 0, SomeDateTime))
FROM dbo.JBMTest WITH (NOLOCK)
SET STATISTICS TIME OFF
PRINT REPLICATE('=',80)
PRINT '========== DATEADD/DATEDIFF 2 =========='
SET STATISTICS TIME ON
SELECT @BitBucketDATETIME = DATEADD(dd,DATEDIFF(dd,0,SomeDateTime),0)
FROM dbo.JBMTest WITH (NOLOCK)
SET STATISTICS TIME OFF
PRINT REPLICATE('=',80)
PRINT '========== DATEDIFF Implicit =========='
SET STATISTICS TIME ON
SELECT @BitBucketDATETIME = DATEDIFF(d, 0, SomeDateTime)
FROM dbo.JBMTest WITH (NOLOCK)
SET STATISTICS TIME OFF
PRINT REPLICATE('=',80)
PRINT '========== DATEDIFF/DATETIME =========='
SET STATISTICS TIME ON
SELECT @BitBucketDATETIME = CAST(DATEDIFF(d, 0, SomeDateTime) AS DATETIME)
FROM dbo.JBMTest WITH (NOLOCK)
SET STATISTICS TIME OFF
PRINT REPLICATE('=',80)
PRINT '========== CONVERT =========='
SET STATISTICS TIME ON
SELECT @BitBucketDATETIME = CONVERT(DATETIME,CONVERT(VARCHAR,SomeDateTime,100))
FROM dbo.JBMTest WITH (NOLOCK)
SET STATISTICS TIME OFF
PRINT REPLICATE('=',80)
PRINT '========== CONVERT Implicit=========='
SET STATISTICS TIME ON
SELECT @BitBucketDATETIME = CONVERT(VARCHAR,SomeDateTime,100)
FROM dbo.JBMTest WITH (NOLOCK)
SET STATISTICS TIME OFF
PRINT REPLICATE('=',80)
PRINT '===== FLOOR ====='
SET STATISTICS TIME ON
SELECT @BitBucketDATETIME = CAST(FLOOR(CONVERT(FLOAT, SomeDateTime)) AS DATETIME)
FROM dbo.JBMTest WITH (NOLOCK)
SET STATISTICS TIME OFF
PRINT REPLICATE('=',80)
PRINT '===== FLOOR Implicit ====='
SET STATISTICS TIME ON
SELECT @BitBucketDATETIME = FLOOR(CONVERT(FLOAT, SomeDateTime))
FROM dbo.JBMTest WITH (NOLOCK)
SET STATISTICS TIME OFF
PRINT REPLICATE('=',80)
PRINT '===== ROUNDING 1 ====='
SET STATISTICS TIME ON
SELECT @BitBucketDATETIME = CAST(CAST(SomeDateTime - 0.50000004 AS INT) AS DATETIME)
FROM dbo.JBMTest WITH (NOLOCK)
SET STATISTICS TIME OFF
PRINT REPLICATE('=',80)
PRINT '===== ROUNDING 1 Implicit ====='
SET STATISTICS TIME ON
SELECT @BitBucketDATETIME = CAST(SomeDateTime - 0.50000004 AS INT)
FROM dbo.JBMTest WITH (NOLOCK)
SET STATISTICS TIME OFF
PRINT REPLICATE('=',80)
PRINT '===== ROUNDING 2 ======'
SET STATISTICS TIME ON
SELECT @BitBucketDATETIME = CAST(ROUND(CAST(SomeDateTime AS FLOAT),0,1) AS DATETIME)
FROM dbo.JBMTest WITH (NOLOCK)
SET STATISTICS TIME OFF
PRINT REPLICATE('=',80)
PRINT '===== ROUNDING 2 Implicit ======'
SET STATISTICS TIME ON
SELECT @BitBucketDATETIME = ROUND(CAST(SomeDateTime AS FLOAT),0,1)
FROM dbo.JBMTest WITH (NOLOCK)
SET STATISTICS TIME OFF
PRINT REPLICATE('=',80)
For those that may have an interest, here's the output created by my 9 year old, single 1.8 GHz P4 CPU with 1GB of ram running Windows XP (sp3) and SQL Server 2005 (sp3).
(1000000 row(s) affected)
========== BASELINE ==========
SQL Server Execution Times:
CPU time = 640 ms, elapsed time = 668 ms.
================================================================================
========== DATEADD/DATEDIFF 1 ==========
SQL Server Execution Times:
CPU time = 984 ms, elapsed time = 1026 ms.
================================================================================
========== DATEADD/DATEDIFF 2 ==========
SQL Server Execution Times:
CPU time = 797 ms, elapsed time = 810 ms.
================================================================================
========== DATEDIFF Implicit ==========
SQL Server Execution Times:
CPU time = 719 ms, elapsed time = 715 ms.
================================================================================
========== DATEDIFF/DATETIME ==========
SQL Server Execution Times:
CPU time = 734 ms, elapsed time = 737 ms.
================================================================================
========== CONVERT ==========
SQL Server Execution Times:
CPU time = 5500 ms, elapsed time = 5515 ms.
================================================================================
========== CONVERT Implicit==========
SQL Server Execution Times:
CPU time = 5375 ms, elapsed time = 5389 ms.
================================================================================
===== FLOOR =====
SQL Server Execution Times:
CPU time = 1016 ms, elapsed time = 1018 ms.
================================================================================
===== FLOOR Implicit =====
SQL Server Execution Times:
CPU time = 969 ms, elapsed time = 970 ms.
================================================================================
===== ROUNDING 1 =====
SQL Server Execution Times:
CPU time = 718 ms, elapsed time = 720 ms.
================================================================================
===== ROUNDING 1 Implicit =====
SQL Server Execution Times:
CPU time = 719 ms, elapsed time = 718 ms.
================================================================================
===== ROUNDING 2 ======
SQL Server Execution Times:
CPU time = 1453 ms, elapsed time = 1465 ms.
================================================================================
===== ROUNDING 2 Implicit ======
SQL Server Execution Times:
CPU time = 1532 ms, elapsed time = 1566 ms.
================================================================================
--Jeff Moden
Change is inevitable... Change for the better is not.
June 3, 2011 at 11:50 pm
As a bit of a sidebar, here's a blanket statement...
[font="Arial Black"]NEVER EVER USE A RECURSIVE CTE TO BUILD SEQUENTIAL SETS OF NUMBERS NO MATTER HOW SMALL THE SET MAY BE!!! [/font]:-P
Here's the proof... I created some code to test and measure several simple counting methods including that of the Recursive CTE's. To make a very long story short, here're the performance curves measured to the nearest millisecond for counts of 1 to 100...
Whoa! Wait a minute! I see that the fat red line is the Recursive CTE performance curve but where are the performance curves for the rest of the sequential counter methods? Well, take a look way down in the right hand corner of the chart. See the almost imperceptible rise of color above the "Zero" line? That's the beginning of the linear trendline of all of the rest of them. I had to use the trendline just to get them to show up on this chart because they were all flat 0 milliseconds (again, rounded to the nearest millisecond).
You'll have to trust me for a bit... I'm not going to post the code or the other charts here because this problem of people using Recursive CTE's to generate simple numeric sequences has become so rampant that I'm writing an article on it. 😀
--Jeff Moden
Change is inevitable... Change for the better is not.
June 4, 2011 at 12:00 am
Jnrstevej (6/2/2011)
Brilliant i can work with that
My recommendation is that you don't so that you don't get into any bad habits. If you have one, format the dates in the report generator (or Excel). Either way, replace that Recursive CTE with a Tally Table ( http://www.sqlservercentral.com/articles/T-SQL/62867/ ) or other suitable method.
--Jeff Moden
Change is inevitable... Change for the better is not.
June 4, 2011 at 2:00 am
Jeff Moden (6/3/2011)
You'll have to trust me for a bit... I'm not going to post the code or the other charts here because this problem of people using Recursive CTE's to generate simple numeric sequences has become so rampant that I'm writing an article on it. 😀
Not trusting Jeff when he posts tested statistics is like not trusting Socrates(sp?) when he discusssed logical proofing. It's just silly.
If Jeff is willing to put his name on the line, something he considers quite valuable, to prove a point with numbers... it's worth listening to.
Never stop learning, even if it hurts. Ego bruises are practically mandatory as you learn unless you've never risked enough to make a mistake.
For better assistance in answering your questions[/url] | Forum Netiquette
For index/tuning help, follow these directions.[/url] |Tally Tables[/url]
Twitter: @AnyWayDBA
June 4, 2011 at 9:39 am
Thanks for the endorsement, Craig. :blush: Yeah... this one is bad and it's getting out of hand. I see more and more people using recursive CTEs to generate sequences of numbers and they just don't understand how something without an explicit loop and something that MS advertises as a feature can be so bad.
--Jeff Moden
Change is inevitable... Change for the better is not.
June 4, 2011 at 11:14 am
drew.allen (6/3/2011)
skcadavre (6/3/2011)
When you post sweeping comments, you should probably include a test script that proves what you're claiming. Note, I'm not saying you're wrong. As I said before, I use the presentation layer when formatting data so I've never really done any formatting on large data sets, but your sweeping comment without any evidence doesn't change the opinions of anyone.I'm not getting paid to post here. I don't feel the need to post test scripts for something that is documented elsewhere and widely known, but if you want to pay me to do so, I will gladly post the requested scripts.
Drew
*shrugs* A link would've been enough 😉
Jeff Moden (6/3/2011)
skcadavre (6/3/2011)
When you post sweeping comments, you should probably include a test script that proves what you're claiming. {snip}... but your sweeping comment without any evidence doesn't change the opinions of anyone.However... not only has it been proven that conversion of dates to VARCHARs is comparatively dreadfully slow, but the method that Drew used is one of the faster methods there is and it will absolutely blow the doors off of any conversion to a VARCHAR using CONVERT. So I can understand why he's reluctant to spend any additional time on what has become well known previously proven fact.
Awesome. I'm glad I've always pushed this sort of task into the presentation layer 🙂
Jeff Moden (6/3/2011)
You'll have to trust me for a bit... I'm not going to post the code or the other charts here because this problem of people using Recursive CTE's to generate simple numeric sequences has become so rampant that I'm writing an article on it. 😀
I look forward to it. Generally I'd use a tally table when I need a list of numbers, although I am guilty of using recursive CTEs when the list size is small enough.
June 4, 2011 at 11:44 am
skcadavre (6/4/2011)
although I am guilty of using recursive CTEs when the list size is small enough.
I guess that's the point I'm trying to make. The list is never small enough. 🙂 I know you personally know not to use such a thing to build larger lists but I'm concerned (especially since lot's of people have started to use rCTE's for lists) that those who don't know about the problems with rCTE's will see that the code works and use it for something else where the performance will absolutely crush any notion of scalability in their code.
--Jeff Moden
Change is inevitable... Change for the better is not.
Viewing 15 posts - 1 through 15 (of 24 total)
You must be logged in to reply to this topic. Login to reply