Exception when updating row with rowversion? - sql-server

I have a table that looks like this :
CREATE TABLE [dbo].[akut_prioritering]
(
[behandling_id] [int] NOT NULL,
[akutstatus] [int] NOT NULL,
[nasta_dag] [bit] NOT NULL,
[sort_order] [bigint] NOT NULL,
[rowversion] [timestamp] NOT NULL,
CONSTRAINT [XPKakut_prioritering]
PRIMARY KEY CLUSTERED ([behandling_id] ASC)
) ON [PRIMARY]
And then I have this stored procedure that tries to update rows in this table :
ALTER PROCEDURE [dbo].[akutlistaSave]
#behandlingSortOrder dbo.akutlista_sortorder_tabletype READONLY
AS
BEGIN
SET NOCOUNT ON;
DECLARE #behandlingId INT;
DECLARE #sortOrder BIGINT;
DECLARE #rowversion ROWVERSION;
DECLARE sortOrderCursor CURSOR LOCAL SCROLL STATIC FOR
SELECT behandling_id, sort_order FROM #behandlingSortOrder
OPEN sortOrderCursor
BEGIN TRAN
FETCH NEXT FROM sortOrderCursor INTO #behandlingId, #sortOrder, #rowversion
WHILE ##FETCH_STATUS = 0
BEGIN
IF EXISTS(SELECT *
FROM akut_prioritering ap
WHERE ap.behandling_id = #behandlingId
AND ap.rowversion = #rowversion)
BEGIN
UPDATE akut_prioritering
SET sort_order = #sortOrder
WHERE behandling_id = #behandlingId;
END
ELSE
BEGIN
RAISERROR ('Rowversion not correct.', 16, 1);
END
FETCH NEXT FROM sortOrderCursor INTO #behandlingId, #sortOrder, #rowversion
END
CLOSE sortOrderCursor
SELECT
ap.behandling_id, ap.rowversion
FROM
akut_prioritering ap
INNER JOIN
#behandlingSortOrder bso ON ap.behandling_id = bso.behandling_id;
DEALLOCATE sortOrderCursor
END
The inparameter type looks like this :
CREATE TYPE [dbo].[akutlista_sortorder_tabletype] AS TABLE
(
[behandling_id] [int] NULL,
[sort_order] [bigint] NULL,
[rowversion] [timestamp] NULL
)
When running this I get a SqlException :
Cannot insert an explicit value into a timestamp column. Use INSERT with a column list to exclude the timestamp column, or insert a DEFAULT into the timestamp column.
From what I understand the rowversion column should be updated with a new value automatically, there is no reason in my case to set it manual.

You can't set the rowversion value in dbo.akutlista_sortorder_tabletype because it is not updateable: it is auto generated
However, rowversion (a.k.a deprecated timestamp) is simply a (var)binary(8) with some special rules. You can define and set a (var)binary(8) in dbo.akutlista_sortorder_tabletype and compare on that in the UPDATE
From the first link
A nonnullable rowversion column is semantically equivalent to a binary(8) column. A nullable rowversion column is semantically equivalent to a varbinary(8) column.

It looks like you are trying to insert a timestamp value in a custom table type and then passing that to your stored procedure. As your error suggests, you cannot insert explicit timestamp values into timestamp columns.
You will need to find a different way of passing you table values to this stored procedure to work.

Related

SQL Server variable datetime with array

Can we define a variable of type array in SQL Server?
I have a table and this pk type is datetime.
CREATE TABLE [dbo].[tblTest]
(
[TestDate] [datetime] NOT NULL, -- this column is the primary key
[UserId] [int] NOT NULL,
[RotationType] [int] NOT NULL
)
And I need a query to update userId
UPDATE TABLE dbo.tblTest
SET RotationType = 1
WHERE TestDate IN (#testDates)
How can I declare this #testDates variable?
you can use table variables:
declare #tblvar as table (TestDate datetime)
insert into #tblvar values (('2021-09-01'),...);
UPDATE TABLE dbo.tblTest
SET RotationType = 1
WHERE TestDate IN (select TestDate from #tblvar)
or you can declare it as type so you can pass it to procs:
declaration phase :
create type DateTimeArray as table (TestDate datetime);
CREATE PROCEDURE funcname (#inputDates DateTimeArray)
as
UPDATE TABLE dbo.tblTest
SET RotationType = 1
WHERE TestDate IN (select TestDate from #inputDates)
end
calling:
declare #tblvar as DateTimeArray;
insert into #tblvar values (('2021-09-01'),...);
exec funcname #tblvar

Inserts into a SQL database table that has a varbinary(max) column can be slow

We have the following stored procedure:
ALTER procedure [dbo].[spMergePPObjectBlobProperty]
(
#values dbo.udtPPObjectBlobProperty readonly
)
as
begin
begin try
declare #updatetime datetime
set #updatetime = GetUTCDate()
merge tblPPObjectBlobProperty as t
using (select * from #values) as s
on t.InsertionId = s.InsertionId and t.PropertyMapNameId = s.PropertyMapNameId
when matched then
update set UpdateTime = #updatetime, [Value] = s.BlobValue, UpdateId = s.UpdateId
when not matched then
insert (PropertyMapNameId, Value, UpdateId, UpdateTime, InsertionId)
values(s.PropertyMapNameId, s.BlobValue, s.UpdateId, #updatetime, s.InsertionId)
option (loop join);
end try
begin catch
declare #errormessage varchar(256)
-- Get the error message
select #errormessage = ERROR_MESSAGE()
-- Raise an error and return
raiserror('Error updating entries in the tblPPObjectBlobProperty Table. %s', 16, 1, #errormessage)
end catch
end
Occasionally, this sp can take a few seconds to run with only several rows to insert. Other times it is extremely fast.
We have a number of these sps and this is the only one that appears to be slow sometimes and it is the only one that inserts into a table with a varbinary(max) column.
The type used here is defined as follows:
CREATE TYPE [dbo].[udtPPObjectBlobProperty] AS TABLE(
[InsertionId] [bigint] NOT NULL,
[PropertyMapNameId] [int] NOT NULL,
[BlobValue] [varbinary](max) NULL,
[UpdateId] [bigint] NULL,
PRIMARY KEY CLUSTERED
(
[InsertionId] ASC,
[PropertyMapNameId] ASC
)WITH (IGNORE_DUP_KEY = OFF)
)
GO
Can this stored procedure be optimised in any way?

MSSQL DateTime2 Fail to Update

Im running stored procedure which in charges to insert, update and delete table's entries.
While both insert and delete runs smoothly, the update operation updates all columns except DATETIME2 one.
The scenario - I test my Repository pattern (using C# code) in the following way:
delete the entire [BackgroundTaskAttachtment] table
Create 4 new entries
delete single entry created on step 2
Wait for 5 seconds
modify one of the entries
the result is having 3 entries in [BackgroundTaskAttachtment] table, with all properties set as expected, except the [UpdatedOnUtc] which not updated (it is equal to [CreatedOnUtc]
I marked the updated row (as you can see [FilePath] was successfully updated):
Would appreciate community insights,
Thank you
This is the stored procedure code:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[SP_ArrangeBackgroundTaskAttachtments]
(
#backgroundTaskId BIGINT,
#taskAttchs [dbo].[BackgroundTaskAttachtmentType] READONLY
)
AS
BEGIN
SET NOCOUNT ON;
--delete all removed attachtments
DELETE FROM [BackgroundTaskAttachtment]
WHERE [BackgroundTaskId] = #backgroundTaskId AND [Id] NOT IN (SELECT [Id] FROM #taskAttchs)
----Update exist key-value pairs
UPDATE [dbo].[BackgroundTaskAttachtment]
SET
[IsPrimary] = attachs.[IsPrimary],
[FilePath] = attachs.[FilePath],
[Bytes] = attachs.[Bytes],
[UpdatedOnUtc] = GETUTCDATE()
FROM #taskAttchs AS attachs
WHERE attachs.[Id] = [BackgroundTaskAttachtment].[Id]
--insert new records
SELECT #backgroundTaskId AS [BackgroundTaskId], [FilePath], [IsPrimary], [Bytes], GETUTCDATE() AS [CreatedOnUtc], GETUTCDATE() AS [UpdatedOnUtc]
INTO #Temp FROM #taskAttchs as atcs
WHERE atcs.[Id] NOT IN (SELECT [Id] FROM [BackgroundTaskAttachtment] AS bta WHERE bta.[BackgroundTaskId] = #backgroundTaskId )
INSERT INTO [BackgroundTaskAttachtment]([BackgroundTaskId], [IsPrimary], [Bytes], [FilePath], [CreatedOnUtc], [UpdatedOnUtc] )
SELECT [BackgroundTaskId], [IsPrimary], [Bytes], [FilePath], [CreatedOnUtc], [UpdatedOnUtc]
FROM #Temp
END
This is the table type (sent from CLR to SQL)
CREATE TYPE [dbo].[BackgroundTaskAttachtmentType] AS TABLE(
[Id] [BIGINT] NOT NULL,
[FilePath] [NVARCHAR](MAX) NULL,
[IsPrimary] [BIT] NOT NULL,
[BackgroundTaskId] [BIGINT] NULL,
[Bytes] [VARBINARY](MAX) NULL
)
GO
this is the table definition
CREATE TABLE [dbo].[BackgroundTaskAttachtment]
(
[Id] BIGINT IDENTITY(1,1) NOT NULL,
[BackgroundTaskId] BIGINT NOT NULL,
[IsPrimary] BIT NOT NULL DEFAULT 0,
[FilePath] NVARCHAR(MAX) NULL,
[Bytes] VARBINARY(MAX) NULL,
[CreatedOnUtc] DATETIME2 NOT NULL,
[UpdatedOnUtc] DATETIME2 NOT NULL,
[RowVersion] ROWVERSION NOT NULL,
CONSTRAINT [PK_dbo.BackgroundTaskAttachtment] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_dbo.BackgroundTaskAttachtment_BackgroundTask_Id]
FOREIGN KEY ([BackgroundTaskId])
REFERENCES [dbo].[BackgroundTask] ([Id])
ON DELETE CASCADE
);
Please try using SYSUTCDATETIME which returns datetime2.
The GETUTCDATE which you are using, returns datetime.

Alter scalar function in SQL server 2008 that referring in the computed column of a table

I have created a scalar function in SQL Server 2008 and the same I am referring in a computed column in few of my tables. Now I want to alter the function without dropping the table. But it throws an error:
Cannot ALTER 'dbo.GetStatus' because it is being referenced by object
'Order'.
Is is possible to alter the function? Or do I drop and create all dependable table first and then alter the function?
Here is my function:
CREATE FUNCTION [dbo].[GetStatus]
(
#FromDate datetime,
#ToDate datetime
)
RETURNS tinyint
AS
BEGIN
declare #ret tinyint;
if(#FromDate<=GETDATE() and (#ToDate>=GETDATE() or #ToDate is null))
set #ret= 1
else
set #ret= 0
return #ret
END
And it is referring in a table:
CREATE TABLE [dbo].[Order](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](200) NOT NULL,
[EffectiveFromDate] [datetime] NOT NULL,
[EffectiveToDate] [datetime] NULL,
[Status] AS ([dbo].[GetStatus]([EffectiveFromDate],[EffectiveToDate]))
)
This is by design. You should first drop all defaults/constraints, then alter your function and the add those constraints back. No need to drop the tables.
But you can work around this by the following trick:
add intermediate function that will call your actual function;
alter computed columns to call intermediate function instead of actual.
Example:
CREATE FUNCTION dbo.fnActual ( #p INT )
RETURNS INT
AS
BEGIN
RETURN #p + 1
END
GO
CREATE FUNCTION dbo.fnIntermediate ( #p INT )
RETURNS INT
AS
BEGIN
RETURN dbo.fnActual(#p)
END
GO
CREATE TABLE TestTable(id INT, fn AS dbo.fnIntermediate(id))
GO
Insert some value:
INSERT INTO dbo.TestTable VALUES ( 1 )
SELECT * FROM dbo.TestTable --selects 2
--throws exception
ALTER FUNCTION dbo.fnIntermediate ( #p INT )
RETURNS INT
AS
BEGIN
RETURN dbo.fnActual(#p)
END
GO
--succseeds
ALTER FUNCTION dbo.fnActual ( #p INT )
RETURNS INT
AS
BEGIN
RETURN #p + 2
END
GO
SELECT * FROM dbo.TestTable --selects 3
ALTER TABLE [dbo].[Order]
DROP COLUMN [Status]
GO
ALTER FUNCTION [dbo].[GetStatus] ...
GO
ALTER TABLE [dbo].[Order]
ADD [Status] AS ([dbo].[GetStatus]([EffectiveFromDate],[EffectiveToDate]))
GO
or even
ALTER TABLE [dbo].[Order]
DROP COLUMN [Status]
GO
ALTER TABLE [dbo].[Order]
ADD [Status] AS CAST(CASE WHEN [EffectiveFromDate] <= GETDATE() AND ([EffectiveToDate] >= GETDATE() OR [EffectiveToDate] IS NULL) THEN 1 ELSE 0 END as tinyint)
GO

How do I create this stored procedure?

I have two tables:
CREATE TABLE [NEWS]
(
[ID] INT IDENTITY(1,1) NOT NULL,
[TITLE] VARCHAR(500) NULL,
[CONTENT] VARCHAR(800) NULL,
[CREATED] DATETIME DEFAULT(GETDATE())
PRIMARY KEY ([ID])
)
CREATE TABLE [LOG]
(
[ID] INT IDENTITY(1,1) NOT NULL,
[ACTION] VARCHAR(500) NULL,
[CREATED] DATETIME DEFAULT(GETDATE())
PRIMARY KEY ([ID])
)
I want to do the following procedure:
I have an input parameter #NewsId.
STEP 1
If NewsId is NULL : I want to save the row into the table (NEWS) .
If newsid is defined then I want to update the row.
STEP 2
I want to do step 1 and then save the record into a table named LOG.
INSERT INTO LOG ("Action") VALUES ("insert or update")
How can I do these two steps using stored procedure?
How can I make one step after the successful completion and go to step 2?
Here is a simple sample to get you going.
create procedure MyProc (#NewsId int) as
Begin
-- you should really pass these in?
declare #title varchar(500) = 'A title'
declare #content varchar(800) = 'A piece of content'
if #NewsId is null
begin
Begin Try
insert into News (Title, Content) values (#title, #content)
-- get the new id just inserted
set #NewsId = SCOPE_IDENTITY()
insert into Log (Action) values ('insert')
End Try
Begin Catch
.... handle error
end catch
end
else
begin
update News set Title = #title, Content = #content
where id = #NewsId
insert into Log (Action) values ('update')
end
end
from CodeProject:
Begin Try
The_Query_for_which_we_need_to_do_the_ Error_Handling
End Try
Begin Catch
If there is some error in the query within the Try block, this flow
will be passed to this Catch block.
End catch

Resources