SQLSVR - not able to create index with computed GETDATE column - indexing

What I want to achieve: To create an index with an existing date column data converted into UTC timing (Query is written below)
Issue: There's a column in table with local server date values, I need to convert them into UTC date value and then create an Index over it.
Why I need this: I have the same thing done in oracle, and I am trying to migrate stuff along with queries into Sql Server for a new client
Problem: Index doesn't take variables or user defined functions. But only takes table columns as parameters.
Only Work around: is to make a computed column on table and use it to create the index.
Steps I followed:
Ran the below queries at first
ALTER TABLE dbo.tableClient ADD tempComp1 AS DATEADD(minute, datediff(minute, GETUTCDATE(), getdate()), [svrDate])
GO
create index idx1 on dbo.tableClient([key] asc, [tempComp1] desc, [type])
GO
It gives the below error:
Column 'tempComp1' in table 'dbo.tableClient' cannot be used in an index or statistics or as a partition key because it is non-deterministic.
So I tried making the column as PERSISTED
ALTER TABLE dbo.tableClient ADD tempComp1 AS DATEADD(minute, datediff(minute, GETUTCDATE(), getdate()), [svrDate]) PERSISTED
it now giving the error:
Computed column 'tempComp1' in table 'tableClient' cannot be persisted because the column is non-deterministic.
Now, the funny thing is, if I do
SELECT datediff(minute, GETUTCDATE(), getdate())
it gives result: 330
Now if I try the same commands with 330
ALTER TABLE dbo.tableClient ADD tempComp1 AS DATEADD(minute, 330, [svrDate]) PERSISTED
GO
create index idx1 on dbo.tableClient([key] asc, [tempComp1] desc, [type])
GO
it works absolutely fine.

Related

In an existing table with records, how do I create a new datetime2(2) column and populate it with values based on another column?

I have a VARCHAR column that looks like this:
Created_DTM
-----------
2020-02-05 16:26:45.00Z
2020-02-16 08:55:52.00Z
2020-04-24 15:24:01.00Z
2020-06-13 22:14:18.00Z
How do I write a script that will create a new column called Created_DTM_Converted and fill each record with a value so that the zulu (Z) character is gone whilst subtracting 5 hours to match my timezone?
What I've tried:
This query gives me the exact values I need:
SELECT dateadd(hh, -5, convert(datetime2(2), Created_DTM, 120)) AS Created_DTM_Converted
FROM dbo.table
This is the output:
Created_DTM_Converted
-----------
2020-02-05 11:26:45.00
2020-02-16 03:55:52.00
2020-04-24 10:24:01.00
2020-06-13 17:14:18.00
But I don't know how to write a script that can be executed once and will make this a permanently added column. How do I go about doing so?
You could alter your table and add this new column as a computed column:
ALTER TABLE dbo.table
ADD COLUMN Created_DTM_Converted AS DATEADD(hh, -5, CONVERT(datetime2(2), Created_DTM, 120));
Note that the above computed column is virtual, meaning that it is not actually stored in the database, but rather is computed on the fly when it is accessed.
By the way, a potentially easier way of subtracting 5 hours might just be to take the first 22 characters of the timestamp:
SELECT DATEADD(hh, -5, LEFT(Created_DTM, 22))
FROM dbo.table;
This also seems to be working.
Edit:
I recommend against dropping the original Created_DTM column for several reasons. First, it represents original source data, and it is a generally good idea to keep as much state as possible. Second, assuming new records would be getting inserted in this original format, then you would want to maintain this column. In this case, building some sort of view on top of your table would be appropriate.
Since you mentioned in the commentary that you intend to calculate the new column from the old column, and intend to drop the old column later, you won't be able to use Tim's answer of a computed column.
Therefore you will need to execute this process in two steps. First add the column, then populate it with values. You can drop the original column later as a third step.
alter table dbo.table add Created_DTM_Converted datetime2(2) null;
go
update dbo.table
set Created_DTM_Converted = dateadd(hh, -5, convert(datetime2(2), Created_DTM, 120));
Once you have populated the column, you can optionally alter its definition to not allow nulls:
alter table dbo.table alter column Created_DTM_Converted datetime2(2) not null;

Convert existing table to temporal table with computed column (User-defined Function)

I want to convert my existing table to a temporal table and it has 2 computed columns (user-defined function).
I've already added the 3 columns to my table using the script below and it was successful.
Alter Table Trips
Add StartTime DateTime2 (2) GENERATED ALWAYS AS ROW START HIDDEN
constraint df_StartTime default DateAdd(second, -1, SYSUTCDATETIME()),
EndTime DateTime2 (2) GENERATED ALWAYS AS ROW END HIDDEN
constraint df_EndTime default '9999.12.31 23:59:59.99',
Period For System_Time (StartTime, EndTime)
Go
But when I'm trying to execute the script below
Alter Table Trips
SET(SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.Trips_History))
Go
I'm getting the error below
Msg 13585, Level 16, State 1, Line 19
Computed column is defined with a user-defined function which is not allowed with system-versioned table 'AccessBetaX.dbo.Trips' because it performs user or system data access, or is assumed to perform this access. A function is assumed by default to perform data access if it is not schemabound.
Did I miss something important?

Using CASE Statement in a table

Is it possible to have a column in a table (not from view)(SQL SERVER 2008) to change according to a value in another column i.e. if I have a column called "DUEDATE" can I have a column called "STATUS" that will change the status to "Now Due" if the "DUEDATE" is > GetDate()? If so how do you add that to a table?
You can alter the table and add a computed column:
ALTER TABLE dbo.TheTable
ADD Status AS CASE WHEN ...
You can't persist it, because it's non-deterministic, so don't add PERSISTED or try to put an index on it.
From an indexing perspective, don't try to query it using WHERE Status = 'whatever', because it will have to consider every row in the table. Instead, use an index on DueDate and WHERE DueDate <= GETDATE()
Yes, you can create a computed column:
CREATE TABLE [dbo].[SampleTable](
[DueDate] [date] NULL,
[ComputedValue] AS (CASE WHEN [Duedate] > GETDATE() THEN 'Now Due' ELSE '' END)
) ON [PRIMARY]
As this is a non-deterministic column (because of the value of GETDATE() that is different every time you use the table), adding it to the table doesn't give you much benefit over returning the same in a select query.

sql server 2008 function on two date columns

I would like to create a function for a table that has three columns amongst others as follows:
insertDate datetime
updateDate datetime
activity integer
I want to update the activity column by taking the difference of the two date columns...basically updateDate - insertDate = how many days of activity in the activity column. I have no idea how to start this and it needs to run whenever a new insertDate or updateDate is inserted.
You can populate the [InsertDate] with a default value of GETDATE() and populate [UpdateDate] with the current date when you update the column (because you're using procedures (wink), this is really easy to control). If you aren't using procedures and want to control. the [UpdateDate] column, you can use a trigger to populate that column.
Let the Activity column be a calculated field:
DATEDIFF(day, [InsertDate], [UpdateDate])
DATEDIFF
Computed Columns
From MSDNabout computed columns:
Unless otherwise specified, computed columns are virtual columns that are
not physically stored in the table. Their values are recalculated every
time they are referenced in a query. The Database Engine uses the PERSISTED
keyword in the CREATE TABLE and ALTER TABLE statements to physically store
computed columns in the table. Their values are updated when any columns
that are part of their calculation change. By marking a computed column as
PERSISTED, you can create an index on a computed column that is
deterministic but not precise.
Since all the data required for this is in the same row of the table, you could create a computed column. If you want to have an actual column value that is updated whenever the row is updated then you need to look at triggers.
Place this code in a trigger.
update MyTable
set updateDate = GETDATE()
, activity = select (DATEDIFF(DAY, insertDate, GETDATE()))

SQL - how to check table for new data?

I need to create a stored procedure that upon exceution checks if any new rows have been added to a table within the past 12 hours. If not, an warning email must be sent to a recipient.
I have the procedures for sending the email, but the problem is the query itself. I imagine I'd have to make an sql command that uses current date and compares that to the dates in the rows. But I'm a complete beginner in SQL so I can't even use the right words to find anything on google.
Short version:
Using MS SQL Server 2005, how can I check against the dates, then return a result based on whether new rows were created within the last 12 hours, and use that result to decide whether or not to send email?
Something like this should do what you wish.
Select ID
from TableName
where CreatedDate >= dateadd(hour,-12,getDate())
Hope this is clear but please feel free to pose further questions.
Cheers, John
Say your date field in the table is 'CreateDate' and it's of type DateTime.
Your time to compare with is: GETDATE()
(which returns date + time)
To get the datetime value of 12 hours before that, is done using DATEADD:
DATEADD(hour, -12, GETDATE())
so if we want the # of rows added in the last 12 hours, we'll do:
SELECT COUNT(*)
FROM Table
WHERE CreateDate >= DATEADD(hour, -12, GETDATE())
in your proc, you've to store the result of this query into a variable and check if it's > 0, so:
DECLARE #amount int
SELECT #amount=COUNT(*)
FROM Table
WHERE CreateDate >= DATEADD(hour, -12, GETDATE())
and then you'll check the #amount variable if it's > 0.
You could use a trigger, this link has several examples: http://msdn.microsoft.com/en-us/library/aa258254(SQL.80).aspx
USE pubs
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'reminder' AND type = 'TR')
DROP TRIGGER reminder
GO
CREATE TRIGGER reminder
ON titles
FOR INSERT, UPDATE, DELETE
AS
EXEC master..xp_sendmail 'MaryM',
'Don''t forget to print a report for the distributors.'
GO
If you do not want something for each insert/update, you could copy data to a another table then examine that table every 12 hours, report on the rows in it, then delete them...
assuming you have on this table :
- either a unique id autoincrementing
- either a created_timestamp field containing the timestamp of creation of the row
-> have a new table
reported_rows
- report_timestamp
- last_id_seen
(OR)
- last_timestamp_seen
fill the reported row each time you send your email with the actual value
and before sending the email, check with the previous values, so you know what rows have been added
If the table has an identity field, you could also save the max value (as a bookmark) and next time check if there are any rows with an ID greater than your saved bookmark. May be faster if the key is the clustered key.