| Author |
Topic |
|
SamC
White Water Yakist
3467 Posts |
Posted - 2006-01-22 : 18:19:56
|
I've got a column in every user record:Deleted BIT NOT NULL DEFAULT 0,But a new requirement to have knowledge of the date of Deletion came up. Something or other about pulling historic reports which include users who were not deleted at that time in history... I need to implement another Column which specifies the "Deleted Date" and phase out the "Deleted BIT" ASAP.There are two approaches to implementing a Deleted Date column: should and active user have a NULL deleted date or a MAXIMUM VALUE deleted date?First Case:DeletedDate SMALLDATETIME NULL DEFAULT NULL,Second Case"DeletedDate SMALLDATETIME NOT NULL DEFAULT 'December 31, 9999' -- Defaults to Max SMALLDATETIME valueHere's how I see WHERE clauses would reference the two types of definitions:First Case:WHERE @ParmOfDesiredReportingDate < Users.DateDeletedI struck the where because if Users.DateDeleted is NULL (not deleted), it doesn't return those rows. To return NOT deleted users, plus those users who were deleted at a later time than the report period longer...WHERE Users.DateDeleted IS NULL OR @ParmOfDesiredReportingDate < Users.DateDeletedSecond CaseWHERE @ParmOfDesiredReportingDate < Users.DateDeletedThe second case WHERE is simpler and preferable (to me) in this case.. The only other test I can see coming is searching for Deleted or NOT Deleted users...First CaseWHERE Users.DateDeleted IS NULL -- Search for Deleted UsersWHERE Users.DateDeleted IS NOT NULL -- Search for NOT Deleted UsersSecond CaseWHERE Users.DateDeleted = 'December 31, 9999' -- Search for Deleted UsersWHERE Users.DateDeleted < 'December 31, 9999' -- Search for NOT Deleted UsersSeems the second case solution is a better overall deal for programming, readability and performance.Have I overlooked any consideration which would make the first case preferable?SamFootnote: The minimum date can be coded as CASE('' AS SMALLDATETIME)...Is there any similar method to code the maximum SMALLDATETIME without coding it explicitly as 'Dec 31, 9999'? |
|
|
SamC
White Water Yakist
3467 Posts |
Posted - 2006-01-22 : 20:21:05
|
| smalldatetime max value is 'June 6, 2079', not 'Dec 31 9999'. |
 |
|
|
khtan
In (Som, Ni, Yak)
17689 Posts |
Posted - 2006-01-22 : 20:37:17
|
quote: The smalldatetime data type stores dates and times of day with less precision than datetime. SQL Server stores smalldatetime values as two 2-byte integers. The first 2 bytes store the number of days after January 1, 1900. The other 2 bytes store the number of minutes since midnight. Dates range from January 1, 1900, through June 6, 2079, with accuracy to the minute.
select dateadd(day, 0, 65535) as [Max of smalldatetime]-----------------'KH' |
 |
|
|
SamC
White Water Yakist
3467 Posts |
Posted - 2006-01-22 : 20:39:44
|
| I noticed that the maximum SMALLDATETIME in binary is 0xFFFF0000 (the 0s are midnight...)65535 is not much easier to recall than Jun 6 2079, and it's seems more cryptic. |
 |
|
|
khtan
In (Som, Ni, Yak)
17689 Posts |
Posted - 2006-01-22 : 21:56:02
|
| What is your consideration of choosing smalldatetime over datetime ?For me, I will definately go for datetime. 9999-12-31 and definately easier to understand than 65535 or Jun 6 2079.On your Deleted Bit & Deleted Date, why you have to phase out the bit. Can't you have both in a table, although there might be an slight overlapping of functionality in there. At least the changes is not that drastic and only Apps / screen that need to display the deleted date will be affected-----------------'KH' |
 |
|
|
Michael Valentine Jones
Yak DBA Kernel (pronounced Colonel)
7020 Posts |
Posted - 2006-01-22 : 23:28:25
|
I don't see the point of coding the not deleted date as 'Dec 31 9999'. Why not just let it be NULL? It seems like a good use for it.Using 'Dec 31 9999' is just overloading a value. The deleted date doesn't exist, so why put a fake value there?Especially when someone writes a query like this, and then wonders why so many of the deletions are in December:select [Deleted Month] = datename(month,DeletedDate), count(*)from MyTable CODO ERGO SUM |
 |
|
|
byrmol
Shed Building SQL Farmer
1591 Posts |
Posted - 2006-01-22 : 23:43:04
|
| >>Using 'Dec 31 9999' is just overloading a value.But at least it's a value. :-)You could use NULL and then chuck a view around it. Or vice versa! It just depends on what how you want to implement it. Just ensure that the eventual implmentation satifies the logical requirement.One thing that worries me about this design is that it is a partial solution. What is stoppping someone from flipping the BIT/datetime back to "Active"? You would lose 2 data timepoints...DavidMIntelligent Design is NOT science.A front-end is something that tries to violate a back-end. |
 |
|
|
SamC
White Water Yakist
3467 Posts |
Posted - 2006-01-23 : 00:20:39
|
quote: Originally posted by khtan What is your consideration of choosing smalldatetime over datetime ?Sam: I can't imagine the importance of tracking the deleted date to a finer granularity than SMALLDATETIME. Give me a good reason to use DATETIME.For me, I will definately go for datetime. 9999-12-31 and definately easier to understand than 65535 or Jun 6 2079.Sam: This may be a good enough reason to go with DATETIME over SMALLDATETIME. It's easier to recall the MAX date.On your Deleted Bit & Deleted Date, why you have to phase out the bit. Can't you have both in a table, although there might be an slight overlapping of functionality in there.Sam: Ancient Chinese Proverb: Man who wear two Seikos never sure what time it is.
|
 |
|
|
SamC
White Water Yakist
3467 Posts |
Posted - 2006-01-23 : 00:29:57
|
quote: Originally posted by Michael Valentine Jones I don't see the point of coding the not deleted date as 'Dec 31 9999'. Why not just let it be NULL?
That's the question I was asking in the first post. It seems easier to test for NOT DELETED users when NULL is not involved. Don't you agree?quote: Originally posted by Michael Valentine Jones Especially when someone writes a query like this, and then wonders why so many of the deletions are in December:select [Deleted Month] = datename(month,DeletedDate), count(*)from MyTable
If I were going to argue this, I'd say that query doesn't account for deleted and not deleted users. To count the deletions in December, I'd writeSELECT DATENAME(month, DeletedDate) FROM MyTable WHERE DeletedDate < 'Dec 31 9999' -- All users who are not deleted This isn't an academic point. Every single reporting procedure I have reports on deleted or NOT deleted, rarely either. There's almost never a query that doesn't check deleted status. |
 |
|
|
SamC
White Water Yakist
3467 Posts |
Posted - 2006-01-23 : 00:33:45
|
quote: Originally posted by byrmol One thing that worries me about this design is that it is a partial solution. What is stoppping someone from flipping the BIT/datetime back to "Active"? You would lose 2 data timepoints...
This must be a good point because I am in denial about it's relevancy. |
 |
|
|
byrmol
Shed Building SQL Farmer
1591 Posts |
Posted - 2006-01-23 : 01:47:01
|
| >>This must be a good point because I am in denial about it's relevancy.Do you need to know that something was ever deleted? Ie. A row moves from Active->Deleted->Active. The current design misses this scenario.DavidMIntelligent Design is NOT science.A front-end is something that tries to violate a back-end. |
 |
|
|
Kristen
Test
22859 Posts |
Posted - 2006-01-23 : 02:19:38
|
Where did my post just go to? Out of time to retype it all now ... 1) Use NULL - no difference between "MyDeleteDate IS NULL" and "MyDeleteDate = 2999/12/31"2a) Retain the MyDeletedBit column so that all your existing code will still work2b) Create a trigger that sets MyDeletedDate to GetDate if "MyDeletedBit = 1 AND MyDeletedDate IS NULL" and resets it to NULL if "MyDeletedBit = 0 AND MyDeletedDate IS NOT NULL"3) Just add your new report for "Was not deleted at the time in question" and all existing "Deleted this" and "Is this deleted" logic, based on the old MyDeletedBit, will still work just fine!Kristen |
 |
|
|
SamC
White Water Yakist
3467 Posts |
Posted - 2006-01-23 : 07:39:57
|
quote: Originally posted by byrmol Do you need to know that something was ever deleted? Ie. A row moves from Active->Deleted->Active. The current design misses this scenario.
Oh Yeah... I completely see the point. My concern is that I don't see the need (today) or forsee the need (tomorrow), but that doesn't meet it won't raise it's ugly head sometime in the future.For now. creating a stack of deleted dates would complicate queries by adding a JOIN and worse, to the MAX or RANGE Deleted entry.This fuels the denial, I don't want to believe that it's necessary to stack up historic Delete / Undelete DATETIMEs. I'm 70% sure I'm right, but I have some doubts. |
 |
|
|
SamC
White Water Yakist
3467 Posts |
Posted - 2006-01-23 : 07:47:27
|
quote: Originally posted by Kristen 1) Use NULL - no difference between "MyDeleteDate IS NULL" and "MyDeleteDate = 2999/12/31"Sam: I don't know if I can write:WHERE MyDeleteDate IS NULL OR MyDeleteDate > @ReportDate -- Requires 2 tests at runtime...instead of WHERE MyDeleteDate > @ReportDate -- Ahh! That's much better!any chance the execution plan would be better on one or the other?2a) Retain the MyDeletedBit column so that all your existing code will still workSam: Yes, but eventually, I'd like to replace all references of MyDeletedBit to use MyDeleteDate. I usually kneejerk away from having the same flag twice anywhere (Ref: the Chinese proverb I posted above?). I'm not sure how or when, but "Murphy's Law" (or is it some other law?) says if I keep both the BIT and DATE that they'll contradict each other some day and I'll be debugging that mess. Then again, I could be over reacting.2b) Create a trigger that sets MyDeletedDate to GetDate if "MyDeletedBit = 1 AND MyDeletedDate IS NULL" and resets it to NULL if "MyDeletedBit = 0 AND MyDeletedDate IS NOT NULL"I'll be giving this some consideration. I haven't cast anything in stone.3) Just add your new report for "Was not deleted at the time in question" and all existing "Deleted this" and "Is this deleted" logic, based on the old MyDeletedBit, will still work just fine!Sam: Sorry, I wasn't clear, but I've gotta implement this on all reports. In my case, there's a drop-down for historic data... pick the year, do the report. The good news, is it's a migratable solution. I can evolve the reports over time in a non-disruptive way.
|
 |
|
|
jsmith8858
Dr. Cross Join
7423 Posts |
Posted - 2006-01-23 : 09:02:32
|
| Sam -- I agree with you about the need for an OR if you use a NULL; it is cleaner to just say "where DeleteDate > @ReportDate".I haven't tested this to see if there are any weird efficiency drawbacks (there shouldn't be), but one thing you can do is use a UDF to return the constant of "NotDeleted" as whatever date you want:create function NotDeletedreturns datetimeasreturn '99991231'something like that ... technically, though, I think no one will ever need to know this or use this date ... wouldn'tWHERE DeleteDate > (any date sufficiently in the future, i.e., getdate()+1 )always return all active rows? |
 |
|
|
SamC
White Water Yakist
3467 Posts |
Posted - 2006-01-23 : 09:19:34
|
quote: Originally posted by jsmith8858 I haven't tested this to see if there are any weird efficiency drawbacks (there shouldn't be), but one thing you can do is use a UDF to return the constant of "NotDeleted" as whatever date you want:
I'd never considered using functions to replace the lack of global symbolic constants in SQL. Not a bad idea.It seems to me the NULL / NOT NULL question on the DeleteDate boils down to minor trade-offs. I see no major weight to tilt the argument to an uncontestable position of either solution. |
 |
|
|
|