Direct access slower than using functions? - sql

I was conducting some performance testing and have discovered something quite strange. I have set up a short script to time how long it takes to perform certain actions.
declare #date date
declare #someint int
declare #start datetime
declare #ended datetime
set #date = GETDATE()
DECLARE #count INT
SET #count = 0
set #start = GETDATE()
WHILE (#count < 1000)
BEGIN
--Insert test script here
END
set #ended = GETDATE()
select DATEDIFF( MILLISECOND, #start, #ended)
The table I was running tests againsts contains 3 columns, MDay, and CalDate. Every calendar date has a corresponding M(Manufacturing)Day. The table may look something like this:
MDay | CalDate
1 | 1970-01-01
2 | 1970-01-02
I wanted to test how efficient one of our functions was. This function simply takes in a date and returns the int MDay value. I used direct access, basically the same thing without the function, and tests resulted in this method take twice as long! Code I inserted into the loop is provided below. I used a random date in an attempt to eliminate caching (if exist).
Function
select #someint = Reference.GetMDay(DATEADD( D, convert(int, RAND() * 1000) , #date))
Definition for above
create Function [Reference].[GetMDay]
(#pCaLDate smalldatetime
)
Returns int
as
Begin
Declare #Mday int
Select #Mday = Mday
from Reference.MDay
where Caldate = #pCaLDate
Direct
select #someint = MDay from Reference.MDay where CalDate = DATEADD( D, convert(int, RAND() * 1000) , #date)
I even tried using a static #date for my direct code and the difference in times are negligible, so I know the convert call isn't holding it back.
What the heck is going on here?

Take a look at http://msdn.microsoft.com/en-us/library/ms178071%28v=sql.105%29.aspx is the execution plan the same on your sql server for both methods?

Related

Create a procedure to return 32bit ID(nvarchar) with date time when user logs in to a web application

I am trying to write a Microsoft SQL server procedure to return a 32bit ID and date time when the user logs in to an application through a website.I have PHP on the front end to call for the procedure. The Id would be the session used to evaluate the session of the user and update that in the table.
I am having a problem coming up with a procedure that can generate a 32bit id and date time to return to PHP call.I am new to MS SQL I need help.
For your Integer:
You can use the built in RAND() function to generate a positive decimal between 0 and 1 and multiply that by the max 32 bit INT value 2147483647.
Example:
If you're using a Scalar-Valued Function:
...
DECLARE #return INT
SET #return = FLOOR(RAND() * 2147483647)
RETURN(#return)
For your Datetime:
You would have to use a date range that you would want to generate a date for. Additionally, you would have to decide the increment you would want to generate that datetime in (by DAY, SECOND, etc.). You can also use RAND() similar to the previous example with INT.
Example:
Also assuming you're using a Scalar-Valued Function:
...
DECLARE #beginDate DATETIME
DECLARE #endDate DATETIME
DECLARE #return DATETIME
SET #beginDate = '2000-01-01 00:00:00'
-- Gets current Datetime
SET #endDate = GETDATE()
-- Days difference in the range
-- Using RAND again, generate a random date in the range
DECLARE #days INT = DATEDIFF(DAY, #beginDate, #endDate)
DECLARE #random INT = ROUND(((#days - 1) * RAND()), 0)
SET #return = DATEADD(DAY, #random, #endDate)
RETURN(#return)
You can simply use NEWID() and GETDATE() functions for that and convert to the desired format afterwards:
DECLARE #myID uniqueidentifier = NEWID();
DECLARE #myDate datetime = GETDATE();
SELECT #myID, #myDate;

Analyising Implict CAST

I have an academic scenario, which I would like to know how to analyse.
DECLARE #date DATETIME
SET #date = getDate()
SET #date = DATEADD(DAY, DATEDIFF(DAY, 0, #date-3), 3)
This will round the date down to a Thursday.
What I have been challenged on is to evidence where there are implicit CASTs.
The are three places where I presume that this must be occuring...
DATEADD(
DAY,
DATEDIFF(
DAY,
0, -- Implicitly CAST to a DATETIME?
#date-3 -- I presume the `3` is being implicitly cast to a DATETIME?
),
3 -- Another implicit CAST to a DATETIME?
)
Perhaps, however, as the 0 and 3's are are constants, this is done during compilation to an execution plan?
But if the 3's were INT variables, would that be different?
Is there a way to analyse an execution plan, or some other method, to be able to determine this imperically?
To make matters more complicated, I'm currently off site. I'm trying to remotely assist a colleague with this. Which means I do not have direct access to SSMS, etc.
For the queries
DECLARE #date DATETIME = getDate()
DECLARE #N INT = 3
SELECT DATEADD(DAY, DATEDIFF(DAY, 0, #date-3), 3)
FROM master..spt_values
SELECT DATEADD(DAY, DATEDIFF(DAY, 0, #date-#N), #N)
FROM master..spt_values
And looking at the execution plans the compute scalars show the following.
Query 1
[Expr1003] = Scalar Operator(dateadd(day,datediff(day,'1900-01-01 00:00:00.000',[#date]-'1900-01-04 00:00:00.000'),'1900-01-04 00:00:00.000'))
Query 2
[Expr1003] = Scalar Operator(dateadd(day,datediff(day,'1900-01-01 00:00:00.000',[#date]-CONVERT_IMPLICIT(datetime,[#N],0)),CONVERT_IMPLICIT(datetime,[#N],0)))
showing that your suspicion is correct that it happens at compile time for the literal values but needs a CONVERT_IMPLICIT at run time for the int variables

UNIX_TIMESTAMP in SQL Server

I need to create a function in SQL Server 2008 that will mimic mysql's UNIX_TIMESTAMP().
If you're not bothered about dates before 1970, or millisecond precision, just do:
-- SQL Server
SELECT DATEDIFF(s, '1970-01-01 00:00:00', DateField)
Almost as simple as MySQL's built-in function:
-- MySQL
SELECT UNIX_TIMESTAMP(DateField);
Other languages (Oracle, PostgreSQL, etc): How to get the current epoch time in ...
If you need millisecond precision (SQL Server 2016/13.x and later):
SELECT DATEDIFF_BIG(ms, '1970-01-01 00:00:00', DateField)
Try this post:
https://web.archive.org/web/20141216081938/http://skinn3r.wordpress.com/2009/01/26/t-sql-datetime-to-unix-timestamp/
CREATE FUNCTION UNIX_TIMESTAMP (
#ctimestamp datetime
)
RETURNS integer
AS
BEGIN
/* Function body */
declare #return integer
SELECT #return = DATEDIFF(SECOND,{d '1970-01-01'}, #ctimestamp)
return #return
END
or this post:
http://mysql.databases.aspfaq.com/how-do-i-convert-a-sql-server-datetime-value-to-a-unix-timestamp.html
code is as follows:
CREATE FUNCTION dbo.DTtoUnixTS
(
#dt DATETIME
)
RETURNS BIGINT
AS
BEGIN
DECLARE #diff BIGINT
IF #dt >= '20380119'
BEGIN
SET #diff = CONVERT(BIGINT, DATEDIFF(S, '19700101', '20380119'))
+ CONVERT(BIGINT, DATEDIFF(S, '20380119', #dt))
END
ELSE
SET #diff = DATEDIFF(S, '19700101', #dt)
RETURN #diff
END
Sample usage:
SELECT dbo.DTtoUnixTS(GETDATE())
-- or
SELECT UnixTimestamp = dbo.DTtoUnixTS(someColumn)
FROM someTable
Sql Server 2016 and later have a DATEDIFF_BIG function that can be used to get the milliseconds.
SELECT DATEDIFF_BIG(millisecond, '1970-01-01 00:00:00', GETUTCDATE())
Create a function
CREATE FUNCTION UNIX_TIMESTAMP()
RETURNS BIGINT
AS
BEGIN
RETURN DATEDIFF_BIG(millisecond, '1970-01-01 00:00:00', GETUTCDATE())
END
And execute it
SELECT dbo.UNIX_TIMESTAMP()
I often need a unix timestamp with millisecond precision. The following will give you the current unixtime as FLOAT; wrap per answers above to get a function or convert arbitrary strings.
The DATETIME datatype on SQL Server is only good to 3 msec, so I have different examples for SQL Server 2005 and 2008+. Sadly there is no DATEDIFF2 function, so various tricks are required to avoid DATEDIFF integer overflow even with 2008+. (I can't believe they introduced a whole new DATETIME2 datatype without fixing this.)
For regular old DATETIME, I just use a sleazy cast to float, which returns (floating point) number of days since 1900.
Now I know at this point, you are thinking WHAT ABOUT LEAP SECONDS?!?! Neither Windows time nor unixtime really believe in leap seconds: a day is always 1.00000 days long to SQL Server, and 86400 seconds long to unixtime. This wikipedia article discusses how unixtime behaves during leap seconds; Windows I believe just views leap seconds like any other clock error. So while there is no systematic drift between the two systems when a leap second occurs, they will not agree at the sub-second level during and immediately following a leap second.
-- the right way, for sql server 2008 and greater
declare #unixepoch2 datetime2;
declare #now2 Datetime2;
declare #days int;
declare #millisec int;
declare #today datetime2;
set #unixepoch2 = '1970-01-01 00:00:00.0000';
set #now2 = SYSUTCDATETIME();
set #days = DATEDIFF(DAY,#unixepoch2,#now2);
set #today = DATEADD(DAY,#days,#unixepoch2);
set #millisec = DATEDIFF(MILLISECOND,#today,#now2);
select (CAST (#days as float) * 86400) + (CAST(#millisec as float ) / 1000)
as UnixTimeFloatSQL2008
-- Note datetimes are only accurate to 3 msec, so this is less precise
-- than above, but works on any edition of SQL Server.
declare #sqlepoch datetime;
declare #unixepoch datetime;
declare #offset float;
set #sqlepoch = '1900-01-01 00:00:00';
set #unixepoch = '1970-01-01 00:00:00';
set #offset = cast (#sqlepoch as float) - cast (#unixepoch as float);
select ( cast (GetUTCDate() as float) + #offset) * 86400
as UnixTimeFloatSQL2005;
-- Future developers may hate you, but you can put the offset in
-- as a const because it isn't going to change.
declare #sql_to_unix_epoch_in_days float;
set #sql_to_unix_epoch_in_days = 25567.0;
select ( cast (GetUTCDate() as float) - #sql_to_unix_epoch_in_days) * 86400.0
as UnixTimeFloatSQL2005MagicNumber;
FLOATs actually default to 8-byte doubles on SQL Server, and therefore superior to 32-bit INT for many use cases. (For example, they won't roll over in 2038.)
For timestamp with milliseconds result I found this solution from here https://gist.github.com/rsim/d11652a8336137832df9:
SELECT (cast(DATEDIFF(s, '1970-01-01', GETUTCDATE()) as bigint)*1000+datepart(ms,getutcdate()))
Answer from #Rafe didn't work for me correctly (MSSQL 20212) - I got 9 seconds of difference.
Necromancing.
The ODBC-way:
DECLARE #unix_timestamp varchar(20)
-- SET #unix_timestamp = CAST({fn timestampdiff(SQL_TSI_SECOND,{d '1970-01-01'}, CURRENT_TIMESTAMP)} AS varchar(20))
IF CURRENT_TIMESTAMP >= '20380119'
BEGIN
SET #unix_timestamp = CAST
(
CAST
(
{fn timestampdiff(SQL_TSI_SECOND,{d '1970-01-01'}, {d '2038-01-19'})}
AS bigint
)
+
CAST
(
{fn timestampdiff(SQL_TSI_SECOND,{d '2038-01-19'}, CURRENT_TIMESTAMP)}
AS bigint
)
AS varchar(20)
)
END
ELSE
SET #unix_timestamp = CAST({fn timestampdiff(SQL_TSI_SECOND,{d '1970-01-01'}, CURRENT_TIMESTAMP)} AS varchar(20))
PRINT #unix_timestamp
Here's a single-line solution without declaring any function or variable:
SELECT CAST(CAST(GETUTCDATE()-'1970-01-01' AS decimal(38,10))*86400000.5 as bigint)
If you have to deal with previous versions of SQL Server (<2016) and you only care for positive timestamps, I post here the solution I found for very distant dates (so you can get rid of the IF from #rkosegi's answer.
What I did was first calculating the difference in days and then adding the difference in seconds left.
CREATE FUNCTION [dbo].[UNIX_TIMESTAMP]
(
#inputDate DATETIME
)
RETURNS BIGINT
AS
BEGIN
DECLARE #differenceInDays BIGINT, #result BIGINT;
SET #differenceInDays = DATEDIFF(DAY, '19700101', #inputDate)
IF #differenceInDays >= 0
SET #result = (#differenceInDays * 86400) + DATEDIFF(SECOND, DATEADD(DAY, 0, DATEDIFF(DAY, 0, #inputDate)), #inputDate)
ELSE
SET #result = 0
RETURN #result
END
When called to Scalar-valued Functions can use following syntax
Function Script :
USE [Database]
GO
/****** Object: UserDefinedFunction [dbo].[UNIX_TIMESTAMP] ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[UNIX_TIMESTAMP] (
#ctimestamp datetime
)
RETURNS integer
AS
BEGIN
/* Function body */
declare #return integer
SELECT #return = DATEDIFF(SECOND,{d '1970-01-01'}, #ctimestamp)
return #return
END
GO
Call Function :
SELECT dbo.UNIX_TIMESTAMP(GETDATE());

Transforming nvarchar day duration setting into datetime

I have a SQL Server function which converts a nvarchar day duration setting into a datetime value.
The day duration format is >days<.>hours<:>minutes<, for instance 1.2:00 for one day and two hours.
The format of the day duration setting can not be changed, and we can be sure that all data is correctly formatted and present.
Giving the function a start time and the day duration setting it should return the end time.
For instance: 2010-01-02 13:30 ==> 2010-01-03 2:00
I'm using a combination of charindex, substring and convert methods to calculate the value,
which is kind of slow and akward. Is there any other way to directly convert this day duration setting into a datetime value?
Not from what I can see. I would end up with a similar bit of SQL like you, using charindex etc. Unfortunately it's down to the format the day duration is stored in. I know you can't change it, but if it was in a different format then it would be a lot easier - the way I'd usually do this for example, is to rationalise the duration down to a base unit like minutes.
Instead of storing 1.2:00 for 1 day and 2 hours, it would be (1 * 24 * 60) + (2 * 60) = 1560. This could then be used in a straightforward DATEADD on the original date (date part only).
With the format you have, all approaches I can think of involve using CHARINDEX etc.
One alternative would be to build a string with the calculation. Then you can run the generated SQL with sp_executesql, specifying #enddate as an output parameter:
declare #startdate datetime
declare #duration varchar(10)
declare #enddate datetime
set #startdate = '2010-01-02 13:30'
set #duration = '0.12:30'
declare #sql nvarchar(max)
set #sql = 'set #enddate = dateadd(mi,24*60*' +
replace(replace(#duration,'.','+60*'),':','+') + ', #startdate)'
exec sp_executesql #sql,
N'#startdate datetime, #enddate datetime out',
#startdate, #enddate out
This creates a string containing set #enddate = dateadd(mi,24*60*0+60*12+30, #startdate) and then runs it.
I doubt this is faster than the regular charindex way:
declare #pos_dot int
declare #day int
declare #hour int
declare #minute int
select
#pos_dot = charindex('.',#duration),
#day = cast(left(#duration, #pos_dot-1) as int),
#hour = cast(left(right(#duration, 5), 2) as int),
#minute = cast(right(#duration, 2) as int),
#enddate = dateadd(mi, 24*60*#day + 60*#hour + #minute, #startdate)

How can I compare time in SQL Server?

I'm trying to compare time in a datetime field in a SQL query, but I don't know if it's right. I don't want to compare the date part, just the time part.
I'm doing this:
SELECT timeEvent
FROM tbEvents
WHERE convert(datetime, startHour, 8) >= convert(datetime, #startHour, 8)
Is it correct?
I'm asking this because I need to know if 08:00:00 is less or greater than 07:30:00 and I don't want to compare the date, just the time part.
Thanks!
Your compare will work, but it will be slow because the dates are converted to a string for each row. To efficiently compare two time parts, try:
declare #first datetime
set #first = '2009-04-30 19:47:16.123'
declare #second datetime
set #second = '2009-04-10 19:47:16.123'
select (cast(#first as float) - floor(cast(#first as float))) -
(cast(#second as float) - floor(cast(#second as float)))
as Difference
Long explanation: a date in SQL server is stored as a floating point number. The digits before the decimal point represent the date. The digits after the decimal point represent the time.
So here's an example date:
declare #mydate datetime
set #mydate = '2009-04-30 19:47:16.123'
Let's convert it to a float:
declare #myfloat float
set #myfloat = cast(#mydate as float)
select #myfloat
-- Shows 39931,8244921682
Now take the part after the comma character, i.e. the time:
set #myfloat = #myfloat - floor(#myfloat)
select #myfloat
-- Shows 0,824492168212601
Convert it back to a datetime:
declare #mytime datetime
set #mytime = convert(datetime,#myfloat)
select #mytime
-- Shows 1900-01-01 19:47:16.123
The 1900-01-01 is just the "zero" date; you can display the time part with convert, specifying for example format 108, which is just the time:
select convert(varchar(32),#mytime,108)
-- Shows 19:47:16
Conversions between datetime and float are pretty fast, because they're basically stored in the same way.
convert(varchar(5), thedate, 108) between #leftTime and #rightTime
Explanation:
if you have varchar(5) you will obtain HH:mm
if you have varchar(8) you obtain HH:mm ss
108 obtains only the time from the SQL date
#leftTime and #rightTime are two variables to compare
If you're using SQL Server 2008, you can do this:
WHERE CONVERT(time(0), startHour) >= CONVERT(time(0), #startTime)
Here's a full test:
DECLARE #tbEvents TABLE (
timeEvent int IDENTITY,
startHour datetime
)
INSERT INTO #tbEvents (startHour) SELECT DATEADD(hh, 0, GETDATE())
INSERT INTO #tbEvents (startHour) SELECT DATEADD(hh, 1, GETDATE())
INSERT INTO #tbEvents (startHour) SELECT DATEADD(hh, 2, GETDATE())
INSERT INTO #tbEvents (startHour) SELECT DATEADD(hh, 3, GETDATE())
INSERT INTO #tbEvents (startHour) SELECT DATEADD(hh, 4, GETDATE())
INSERT INTO #tbEvents (startHour) SELECT DATEADD(hh, 5, GETDATE())
--SELECT * FROM #tbEvents
DECLARE #startTime datetime
SET #startTime = DATEADD(mi, 65, GETDATE())
SELECT
timeEvent,
CONVERT(time(0), startHour) AS 'startHour',
CONVERT(time(0), #startTime) AS '#startTime'
FROM #tbEvents
WHERE CONVERT(time(0), startHour) >= CONVERT(time(0), #startTime)
Just change convert datetime to time that should do the trick:
SELECT timeEvent
FROM tbEvents
WHERE convert(time, startHour) >= convert(time, #startHour)
if (cast('2012-06-20 23:49:14.363' as time) between
cast('2012-06-20 23:49:14.363' as time) and
cast('2012-06-20 23:49:14.363' as time))
One (possibly small) issue I have noted with the solutions so far is that they all seem to require a function call to process the comparison. This means that the query engine will need to do a full table scan to seek the rows you are after - and be unable to use an index. If the table is not going to get particularly large, this probably won't have any adverse affects (and you can happily ignore this answer).
If, on the other hand, the table could get quite large, the performance of the query could suffer.
I know you stated that you do not wish to compare the date part - but is there an actual date being stored in the datetime column, or are you using it to store only the time? If the latter, you can use a simple comparison operator, and this will reduce both CPU usage, and allow the query engine to use statistics and indexes (if present) to optimise the query.
If, however, the datetime column is being used to store both the date and time of the event, this obviously won't work. In this case if you can modify the app and the table structure, separate the date and time into two separate datetime columns, or create a indexed view that selects all the (relevant) columns of the source table, and a further column that contains the time element you wish to search for (use any of the previous answers to compute this) - and alter the app to query the view instead.
Using float does not work.
DECLARE #t1 datetime, #t2 datetime
SELECT #t1 = '19000101 23:55:00', #t2 = '20001102 23:55:00'
SELECT CAST(#t1 as float) - floor(CAST(#t1 as float)), CAST(#t2 as float) - floor(CAST(#t2 as float))
You'll see that the values are not the same (SQL Server 2005). I wanted to use this method to check for times around midnight (the full method has more detail) in which I was comparing the current time for being between 23:55:00 and 00:05:00.
Adding to the other answers:
you can create a function for trimming the date from a datetime
CREATE FUNCTION dbo.f_trimdate (#dat datetime) RETURNS DATETIME AS BEGIN
RETURN CONVERT(DATETIME, CONVERT(FLOAT, #dat) - CONVERT(INT, #dat))
END
So this:
DECLARE #dat DATETIME
SELECT #dat = '20080201 02:25:46.000'
SELECT dbo.f_trimdate(#dat)
Will return
1900-01-01 02:25:46.000
Use Datepart function: DATEPART(datepart, date)
E.g#
SELECT DatePart(#YourVar, hh)*60) +
DatePart(#YourVar, mi)*60)
This will give you total time of day in minutes allowing you to compare more easily.
You can use DateDiff if your dates are going to be the same, otherwise you'll need to strip out the date as above
You can create a two variables of datetime, and set only hour of date that your need to compare.
declare #date1 datetime;
declare #date2 datetime;
select #date1 = CONVERT(varchar(20),CONVERT(datetime, '2011-02-11 08:00:00'), 114)
select #date2 = CONVERT(varchar(20),GETDATE(), 114)
The date will be "1900-01-01" you can compare it
if #date1 <= #date2
print '#date1 less then #date2'
else
print '#date1 more then #date2'
SELECT timeEvent
FROM tbEvents
WHERE CONVERT(VARCHAR,startHour,108) >= '01:01:01'
This tells SQL Server to convert the current date/time into a varchar using style 108, which is "hh:mm:ss". You can also replace '01:01:01' which another convert if necessary.
I believe you want to use DATEPART('hour', datetime).
Reference is here:
http://msdn.microsoft.com/en-us/library/ms174420.aspx
I don't love relying on storage internals (that datetime is a float with whole number = day and fractional = time), but I do the same thing as the answer Jhonny D. Cano. This is the way all of the db devs I know do it. Definitely do not convert to string. If you must avoid processing as float/int, then the best option is to pull out hour/minute/second/milliseconds with DatePart()
I am assuming your startHour column and #startHour variable are both DATETIME; In that case, you should be converting to a string:
SELECT timeEvent
FROM tbEvents
WHERE convert(VARCHAR(8), startHour, 8) >= convert(VARCHAR(8), #startHour, 8)
below query gives you time of the date
select DateAdd(day,-DateDiff(day,0,YourDateTime),YourDateTime) As NewTime from Table
#ronmurp raises a valid concern - the cast/floor approach returns different values for the same time. Along the lines of the answer by #littlechris and for a more general solution that solves for times that have a minute, seconds, milliseconds component, you could use this function to count the number of milliseconds from the start of the day.
Create Function [dbo].[MsFromStartOfDay] ( #DateTime datetime )
Returns int
As
Begin
Return (
( Datepart( ms , #DateTime ) ) +
( Datepart( ss , #DateTime ) * 1000 ) +
( Datepart( mi , #DateTime ) * 1000 * 60 ) +
( Datepart( hh , #DateTime ) * 1000 * 60 * 60 )
)
End
I've verified that it returns the same int for two different dates with the same time
declare #first datetime
set #first = '1900-01-01 23:59:39.090'
declare #second datetime
set #second = '2000-11-02 23:56:39.090'
Select dbo.MsFromStartOfDay( #first )
Select dbo.MsFromStartOfDay( #second )
This solution doesn't always return the int you would expect. For example, try the below in SQL 2005, it returns an int ending in '557' instead of '556'.
set #first = '1900-01-01 23:59:39.556'
set #second = '2000-11-02 23:56:39.556'
I think this has to do with the nature of DateTime stored as float. You can still compare the two number, though. And when I used this approach on a "real" dataset of DateTime captured in .NET using DateTime.Now() and stored in SQL, I found that the calculations were accurate.
TL;DR
Separate the time value from the date value if you want to use indexes in your search (you probably should, for performance). You can: (1) use function-based indexes or (2) create a new column for time only, index this column and use it in you SELECT clause.
Keep in mind you will lose any index performance boost if you use functions in a SQL's WHERE clause, the engine has to do a scan search. Just run your query with EXPLAIN SELECT... to confirm this. This happens because the engine has to process EVERY value in the field for EACH comparison, and the converted value is not indexed.
Most answers say to use float(), convert(), cast(), addtime(), etc.. Again, your database won't use indexes if you do this. For small tables that may be OK.
It is OK to use functions in WHERE params though (where field = func(value)), because you won't be changing EACH field's value in the table.
In case you want to keep use of indexes, you can create a function-based index for the time value. The proper way to do this (and support for it) may depend on your database engine. Another option is adding a column to store only the time value and index this column, but try the former approach first.
Edit 06-02
Do some performance tests before updating your database to have a new time column or whatever to make use of indexes. In my tests, I found out the performance boost was minimal (when I could see some improvement) and wouldn't be worth the trouble and overhead of adding a new index.