August 17, 2006 at 8:02 am
I need to generate a select statement that contains dates on a weekly basis. I do not want to put these in a table since the user could run this at any time, not necessarily based on a calendar week.
After that I need to bring in data based on if this date matches a date from a particular table.
This is what I have so far. Example:
SELECT
CASE WHEN dateadd(d, +7,'2006-07-01') = dateadd(d, +7,'2006-07-01') Then dateadd(d, +7,'2006-07-01')
WHEN dateadd(d, +13,'2006-07-01') = dateadd(d, +13,'2006-07-01') Then dateadd(d, +13,'2006-07-01')
WHEN dateadd(d, +20,'2006-07-01') = dateadd(d, +20,'2006-07-01') Then dateadd(d, +20,'2006-07-01')
WHEN dateadd(d, +27,'2006-07-01') = dateadd(d, +27,'2006-07-01') Then dateadd(d, +27,'2006-07-01')
WHEN dateadd(d, +34,'2006-07-01') = dateadd(d, +34,'2006-07-01') Then dateadd(d, +34,'2006-07-01')
WHEN dateadd(d, +41,'2006-07-01') = dateadd(d, +41,'2006-07-01') Then dateadd(d, +41,'2006-07-01')
WHEN dateadd(d, +48,'2006-07-01') = dateadd(d, +48,'2006-07-01') Then dateadd(d, +48,'2006-07-01')
WHEN dateadd(d, +55,'2006-07-01') = dateadd(d, +55,'2006-07-01') Then dateadd(d, +55,'2006-07-01')
WHEN dateadd(d, +62,'2006-07-01') = dateadd(d, +62,'2006-07-01') Then dateadd(d, +62,'2006-07-01')
WHEN dateadd(d, +69,'2006-07-01') = dateadd(d, +69,'2006-07-01') Then dateadd(d, +69,'2006-07-01')
WHEN dateadd(d, +76,'2006-07-01') = dateadd(d, +76,'2006-07-01') Then dateadd(d, +76,'2006-07-01')
WHEN dateadd(d, +83,'2006-07-01') = dateadd(d, +83,'2006-07-01') Then dateadd(d, +83,'2006-07-01')
WHEN dateadd(d, +90,'2006-07-01') = dateadd(d, +90,'2006-07-01') Then dateadd(d, +90,'2006-07-01')
else '' end as sortdate
The problem with this is that it only sees the first 'when' and ignores the rest. I want 12 rows of data that show 07/08/06, 07/15/06, 07/22/06, etc(see below)
07/08/06 123 99% 1 2
07/15/06 456 89% 0 1
07/22/06 789 87% 2 0
I have tried to use a union statement but I can't bring in any other columns in another union statement.
Any help is appreciated. Thanks
August 17, 2006 at 10:43 am
Here is a starter
DECLARE @BaseDate DATETIME
SELECT @BaseDate = '20060814'
SELECT DATEADD(week, b3.i + b2.i + b1.i + b0.i, @BaseDate)
FROM (SELECT 0 i UNION ALL SELECT 1) b0
CROSS JOIN (SELECT 0 i UNION ALL SELECT 2) b1
CROSS JOIN (SELECT 0 i UNION ALL SELECT 4) b2
CROSS JOIN (SELECT 0 i UNION ALL SELECT 8) b3
WHERE b3.i + b2.i + b1.i + b0.i < 12
N 56°04'39.16"
E 12°55'05.25"
August 17, 2006 at 12:37 pm
Thanks a lot. That works great for the weeks. I have been trying to incorporate a count (basically) in another column into that statement. How would I do something like this?
if the moveindate falls between week1 and week2 then count 1. Then if
moveindate falls betwen week2 and week3 then count 1 and so on...
Then sum that up in a total.
August 17, 2006 at 12:45 pm
DECLARE @BaseDate DATETIME
SELECT @BaseDate = '20060814'
SELECT DATEADD(week, b3.i + b2.i + b1.i + b0.i, @BaseDate),
DATEADD(day, 6, DATEADD(week, b3.i + b2.i + b1.i + b0.i, @BaseDate)),
count(*) as SomethingCounting
FROM (SELECT 0 i UNION ALL SELECT 1) b0
CROSS JOIN (SELECT 0 i UNION ALL SELECT 2) b1
CROSS JOIN (SELECT 0 i UNION ALL SELECT 4) b2
CROSS JOIN (SELECT 0 i UNION ALL SELECT 8) b3
INNER JOIN SomeOtherTable sot ON sot.SomeDateColumn BETWEEN DATEADD(week, b3.i + b2.i + b1.i + b0.i, @BaseDate) AND DATEADD(day, 6, DATEADD(week, b3.i + b2.i + b1.i + b0.i, @BaseDate))
WHERE b3.i + b2.i + b1.i + b0.i < 12
GROUP BY b3.i + b2.i + b1.i + b0.i
N 56°04'39.16"
E 12°55'05.25"
August 17, 2006 at 10:33 pm
Tammy,
Peter's solution, as usual, is spot on. His ingenious crosstabs return the sequence of numbers of 0 through 15 in less than a heartbeat and then he applies 0-11 (<12) as weeks to add to a base date.
There are a great number of queries that benefit greatly from having a sequence of numbers including CSV splits, other date prestidigitation, Title casing, data cleansing, and a whole lot more including the replacement of Cursors and While loops as Peter did with his code. With that in mind, allow me to introduce you to the unassuming yet powerful Tally or Numbers table (different names for the same thing).
A Tally or Numbers table is nothing more than a permanent table with a single column containing a sequence of numbers from 1 to some number. Most will make this table run from 1 to 8000 for VARCHAR splits and the like. I make mine from 1 to 11000 because I also use it for simple 1, 2, 3, or 4 digit random number generation and because there are 10957.5 days (based on 365.25 days in a year) in 30 years.
Here’s how to make a permanent Tally or Numbers table… it also uses the same very high speed crosstab method that Peter used in his good solution…
... As you can see, it only has 1 column called "N" (for "number") and that column has a clustered primary key on it (which fully covers the table) for maximum speed.
Now, once you have a Tally table, you can simplify queries like Peter's first as follows...
--===== Declare and set the "base date"
DECLARE @BaseDate DATETIME
SET @BaseDate = '20060814'
--===== Produce a list of 12 weeks starting from the base date
SELECT DATEADD(wk,N-1,@BaseDate) AS ThisWeekStart,
DATEADD(wk,N ,@BaseDate) AS NextWeekStart
FROM dbo.Tally
WHERE N <= 12
Now, let's just say you wanted to change Peter's first query to give you, say, 52 weeks... here's how it would look...
DECLARE @BaseDate DATETIME
SELECT @BaseDate = '20060813'
SELECT DATEADD(week, b5.i + b4.i + b3.i + b2.i + b1.i + b0.i, @BaseDate)
FROM (SELECT 0 i UNION ALL SELECT 1) b0
CROSS JOIN (SELECT 0 i UNION ALL SELECT 2) b1
CROSS JOIN (SELECT 0 i UNION ALL SELECT 4) b2
CROSS JOIN (SELECT 0 i UNION ALL SELECT 8) b3
CROSS JOIN (SELECT 0 i UNION ALL SELECT 16) b4
CROSS JOIN (SELECT 0 i UNION ALL SELECT 32) b5
WHERE b5.i + b4.i + b3.i + b2.i + b1.i + b0.i < 52
... it's gotten a wee bit more complicated. Check out how much more complicated (not) the Tally table method would get...
--===== Declare and set the "base date"
DECLARE @BaseDate DATETIME
SET @BaseDate = '20060814'
--===== Produce a list of 52 weeks starting from the base date
SELECT DATEADD(wk,N-1,@BaseDate) AS ThisWeekStart,
DATEADD(wk,N ,@BaseDate) AS NextWeekStart
FROM dbo.Tally
WHERE N <= 52
... two characters in the whole query changed and one of those was in a comment!
Needless to say, Peter's second query would get quite a bit more complicated to change to 52 weeks...
DECLARE @BaseDate DATETIME
SELECT @BaseDate = '20060814'
SELECT DATEADD(week, b5.i + b4.i + b3.i + b2.i + b1.i + b0.i, @BaseDate),
DATEADD(day, 6, DATEADD(week, b5.i + b4.i + b3.i + b2.i + b1.i + b0.i, @BaseDate)),
count(*) as SomethingCounting
FROM (SELECT 0 i UNION ALL SELECT 1) b0
CROSS JOIN (SELECT 0 i UNION ALL SELECT 2) b1
CROSS JOIN (SELECT 0 i UNION ALL SELECT 4) b2
CROSS JOIN (SELECT 0 i UNION ALL SELECT 8) b3
CROSS JOIN (SELECT 0 i UNION ALL SELECT 16) b4
CROSS JOIN (SELECT 0 i UNION ALL SELECT 32) b5
INNER JOIN SomeOtherTable sot ON sot.SomeDateColumn BETWEEN DATEADD(week, b5.i + b4.i + b3.i + b2.i + b1.i + b0.i, @BaseDate) AND DATEADD(day, 6, DATEADD(week, b5.i + b4.i + b3.i + b2.i + b1.i + b0.i, @BaseDate))
WHERE b5.i + b4.i + b3.i + b2.i + b1.i + b0.i < 12
GROUP BY b5.i + b4.i + b3.i + b2.i + b1.i + b0.i
Here's what the same query would look like using the Tally table...
DECLARE @BaseDate DATETIME
SET @BaseDate = '20060814'
SELECT DATEADD(wk,t.N-1,@BaseDate) AS ThisWeekStart,
DATEADD(wk,t.N,@BaseDate) AS NextWeekStart,
COUNT(*) AS SomethingCounting
FROM dbo.Tally t,
dbo.SomeOtherTable sot
WHERE sot.SomeDateColumn >= DATEADD(wk,t.N-1,@BaseDate)
AND sot.SomeDateColumn < DATEADD(wk,t.N,@BaseDate)
AND t.N <= 52
...no... nothing's missing... it does the same thing (although it does allow for times on the dates where Peter's will miss anything that has a time on the last day of the week) ...
Let's go one step further (not that you would in this case, but bear with me... trying to make a point here). Let's say you turn in the 52 week solution using Peter's crosstab method. QA tests it and says it works fine for 52 weeks but the requirement has changed to 1040 weeks (20 years instead of just 1). You would have to add 4 more crosstabs for 64, 128, 512, and 1024 and 4 more additions to each formula...
... using the Tally table method, here's all that you would do (I highlighted the change) ...
DECLARE @BaseDate DATETIME
SET @BaseDate = '20060814'
SELECT DATEADD(wk,t.N-1,@BaseDate) AS ThisWeekStart,
DATEADD(wk,t.N,@BaseDate) AS NextWeekStart,
COUNT(*) AS SomethingCounting
FROM dbo.Tally t,
dbo.SomeOtherTable sot
WHERE sot.SomeDateColumn >= DATEADD(wk,t.N-1,@BaseDate)
AND sot.SomeDateColumn < DATEADD(wk,t.N,@BaseDate)
AND t.N <= 1040
... sorry that took so long...
Let's go just a bit further... let's say that the requirement stayed at 20 years BUT, the reporting periods changed from week long spans to DAILY (again, just trying to make a point)! That's 7305 days (if you calc a year as 365.25 days)... Using Peter's method, you would have to add yet 2 more crosstabs for 2048 and 4096 and do yet two more additions to each formula!!!
Here's all that you would need to change using the Tally table method (again, highlighted the changes)...
DECLARE @BaseDate DATETIME
SET @BaseDate = '20060814'
SELECT DATEADD(dd,t.N-1,@BaseDate) AS TodayStart,
DATEADD(dd,t.N,@BaseDate) AS TomorrowStart,
COUNT(*) AS SomethingCounting
FROM dbo.Tally t,
dbo.SomeOtherTable sot
WHERE sot.SomeDateColumn >= DATEADD(dd,t.N-1,@BaseDate)
AND sot.SomeDateColumn < DATEADD(dd,t.N,@BaseDate)
AND t.N <= 7305
Do you understand my point? You NEED to make a permanent Tally table.
Write back if you have any questions...
--Jeff Moden
Change is inevitable... Change for the better is not.
August 18, 2006 at 3:39 am
I am happy you filled in the blanks, Jeff
N 56°04'39.16"
E 12°55'05.25"
August 18, 2006 at 6:20 am
Thanks so much for the reply. Both methods work wonderfully!
August 18, 2006 at 1:38 pm
Peter, can I ask another question I just encountered. My dates are as follows:
8/18/06 - 8/24/06,
8/25/06 - 8/31/06,
9/01/06 - 9/07/06, etc.
What if I wanted to start the next week with the last day of the previous week? 8/25 would 8/24
Thanks
August 21, 2006 at 2:55 pm
O.k. Peter,
I am stuck now. I have the following: I am counting moveins and moveouts. For my beginning number I am getting repetitious counts. For example, if someone moves in at the beginning of a week, such as 9/4 it is counted in the 'weekbegin' twice.
SELECT
DATEADD(week, b3.i + b2.i + b1.i + b0.i, '2006-08-21') sortdate,
DATEADD(day,6,DateAdd(week,b3.i + b2.i + b1.i + b0.i,'2006-08-21')) as sortdate2,
count(t.scode) as weekbegin,
count(t2.scode) as movein,
count(t3.scode) as moveout,
count(u.scode) as uscode
FROM (SELECT 0 i UNION ALL SELECT 1) b0
CROSS JOIN (SELECT 0 i UNION ALL SELECT 2) b1
CROSS JOIN (SELECT 0 i UNION ALL SELECT 4) b2
CROSS JOIN (SELECT 0 i UNION ALL SELECT 8) b3
CROSS JOIN property p
INNER JOIN unit u on u.hProperty = p.hMY
LEFT OUTER JOIN tenant t on (u.hmy = t.hUnit and t.istatus <= 6 and DATEADD(week, b3.i + b2.i + b1.i + b0.i, '2006-08-21') between
t.dtmovein and isnull(t.dtMoveOut,DATEADD(week, b3.i + b2.i + b1.i + b0.i, '2006-08-21'))
and t.dtMovein = (Select max(dtMovein) from tenant (nolock)
where hunit = u.hmy and t.istatus <= 6 and DATEADD(week, b3.i + b2.i + b1.i + b0.i, '2006-08-21')
between dtmovein and isnull(dtmoveout,DATEADD(week, b3.i + b2.i + b1.i + b0.i, '2006-08-21'))))
LEFT OUTER JOIN tenant t2 on (t2.hUnit = u.hMY and t2.dtMoveIN Between DATEADD(week,b3.i + b2.i + b1.i + b0.i,'2006-08-21') and
DATEADD(day, 6,DATEADD(week,b3.i + b2.i + b1.i + b0.i,'2006-08-21'))and t2.istatus in (2,6))
LEFT OUTER join tenant t3 on (u.hMy = t3.hUnit and t3.dtMoveOut Between DATEADD(week,b3.i + b2.i + b1.i + b0.i,'2006-08-21') and
DATEADD(day, 6,DATEADD(week,b3.i + b2.i + b1.i + b0.i,'2006-08-21'))and t3.istatus = 4)
WHERE b3.i + b2.i + b1.i + b0.i < 12
GROUP BY b3.i, b2.i, b1.i, b0.i
How can I get around showing this twice?
Thanks
Viewing 9 posts - 1 through 8 (of 8 total)
You must be logged in to reply to this topic. Login to reply