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

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?

Related

SQLSVR - not able to create index with computed GETDATE column

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.

SQL Server Add Default datetime column for existing table

I want to have a new column in the table that will show the date and time of the inserts, but without modifying the queries to include the column itself.
I have added the new column in the following way:
ALTER TABLE DBO.HOURLYMODULETIMES
ADD CreateTime datetime DEFAULT NOT NULL getdate()
This adds the values to previous entries, but when I try to INSERT INTO the table without including the new column
INSERT INTO DBO.HOURLYMODULETIMES VAlUES
(99999999,11111,2222,'JA')
Table has 5 columns ID, AVGMODULETIME, SUMHOURS, USERNAME, CreateTime(newly added). I get the following error:
Column name or number of supplied values does not match table definition.
Is it possible to create such a column without modifying the queries?
You have to specify the columns now when you want to omit one of them when doing INSERT:
INSERT INTO DBO.HOURLYMODULETIMES (ID, AVGMODULETIME, SUMHOURS, USERNAME)
VALUES (99999999,11111,2222,'TEST')
It's good programming practice to always do this, since table definitions may change over time - as you have noticed!

How do I change a column data type from varchar(255) to date?

I am using a reporting database which consists of 20 tables on SQL Server. In marketing table I have a column report_date which is currently a varchar(255). It is basically a date formatted in a way 2017-12-12. I want to change the type of this column to a date. I’m running this script but getting errors. The script is down below:
USE [reporting].[dbo].[marketing]
GO
SELECT CONVERT(date, 'report_date');
These are the errors I’m getting.
Msg 911, Level 16, State 1, Line 1
Database 'dbo' does not exist. Make sure that the name is entered correctly.
Msg 241, Level 16, State 1, Line 3
Conversion failed when converting date and/or time from character string.
How should I adjust the script?
If you want to change the column's data type (and you should) then you need to use an alter table statement. Your first error message is because of the USE directive - it should be
USE [reporting]
GO
Your second error message is because 'report_date' is a string constant, not a column name.
The select statement should be
SELECT CAST(report_date as date) -- Don't use Convert without the style argument....
FROM [dbo].[marketing]
Note that if you have even a single value that can't be converted to date you will get the second error again.
Basically I would recommend first making sure that the select statement completes without any exceptions, and only then alter the table:
USE reporting
GO
ALTER TABLE [dbo].[marketing]
ALTER COLUMN report_date DATE
GO
TRY THIS:
SELECT CONVERT(DATE, report_date) --If only for comparison
ALTER TABLE marketing ALTER COLUMN report_date DATE --If want to change in the table
The proper way to do this is like;
ALTER TABLE reportin.dbo.marketing ALTER COLUMN 'report_date' date
And you can check this How do you change the datatype of a column in MS SQL?
"Convert" is used for conversion from one datatype to other in select queries, you need to use alter statement for altering database columns and also
USE [databasename] is enough, so rewriting your query here :
USE [reporting]
GO
ALTER TABLE marketing ALTER COLUMN ReportDate DATE
Slow, but safe way is to:
Create a new column (ALTER TABLE dbo.MyTable ADD MyNewColumn DATE NULL;)
Update the new column using the old one (UPDATE dbo.MyTable SET MyNewColumn = CONVERT(DATE, MyColumn);)
Drop the old column (ALTER TABLE dbo.MyTable DROP MyColumn;) - Alternatively, you can rename this column instead and keep it as is)
Rename the new column (EXEC sp_rename 'dbo.MyTable.MyNewColumn', 'MyColumn', 'COLUMN';)
You might have to drop indexes beforehand, but this method (and it's alterations) help to prevent data loss.
If you encounter an error during casting, you should eliminate those values from the update (by for example adding a WHERE clause) and investigate them manually.
If you are using SQL Server 2012 or newer, you can use TRY_CONVERT() to ignore the values which cannot be converted to DATE. In this case you will have NULL in your new column.
Before you do anything, make sure, that all applications and code which is working with this column can handle the changes.
Notes
You might want to rebuild the table/indexes after a change like this.

How do I set a value for existing rows when creating a new timestamp column?

I have the following table:
Study id
Pepsi 1
Coke 2
Sprite 3
I need to add a new column timestamp in the above table. i.e, study creation time and date will be stored in this column. What value should I have set for existing rows? Or should the "Timestamp" column have a value only for newly created rows?
I have used the following query to add the new column:
alter table Study add Timestamp datetime
There is no way to tell you what value you should set for existing rows - that is up to you to decide. If you can somehow retrieve the creation time by piecing together other information, then perhaps you can do this one by one, or you could just leave the existing rows to NULL.
Setting a default like GETDATE() for the column, and setting it to NOT NULL, forces all of the existing rows to inherit the current date and time - and you won't be able to set those back to NULL. I'm quite opposed to using garbage token values like 1900-01-01 to represent unknown, and I also don't believe in modifying the code to say something like "if the date is October 8, 2013 then that's because we just didn't know." So I would suggest adding a NULLable column with a default:
ALTER TABLE dbo.Study ADD CreationTime DATETIME NULL DEFAULT CURRENT_TIMESTAMP;
GO
Note that if you leave the column nullable, then the DEFAULT constraint is only useful if DML never sets it to NULL. If an INSERT statement, for example, explicitly places NULL there, the default is ignored. A way around this is to use a trigger (just like you would handle an update):
CREATE TRIGGER dbo.StudyCreationTime
ON dbo.Study
FOR INSERT
AS
BEGIN
SET NOCOUNT ON;
UPDATE s
SET s.CreationTime = CURRENT_TIMESTAMP
FROM dbo.Study AS s
INNER JOIN inserted AS i
ON s.StudyID = i.StudyID;
END
GO
what value should i have to set for previous studies
This would have to be defined by your business. This doesn't require a technical answer, so no one here can tell you what is right.
I have used below query to adding new column:
alter table Study add Timestamp datetime
This will work just fine, though this will allow nulls. I might suggest making this column non-null, adding a default, and changing the name of the column slightly since timestamp is a reserved word in SQL Server (a datatype that has not much to do with dates or times):
alter table Study add CreateDate datetime not null default current_timestamp;
Note that this will set all rows to the current date and time, so you may want to update them if you have more accurate data. Alternatively, simply create the column as nullable and existing rows won't get the default value, but rather null instead.
Another choice you might have to make is whether to use local time or UTC time (e.g. default getutcdate()). You might want to use the same time that your servers use or that other "CreateDate" columns use.

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()))