Cannot update a timestamp column - sql

I was recommended the following stored procedure to audit a login table.
CREATE PROCEDURE ApplicationLogin
#Username NVARCHAR(255),
#IpAddress NVARCHAR(255)
AS
BEGIN
DECLARE #UserID INT;
BEGIN TRANSACTION;
SET #UserID = (SELECT UserID FROM User WHERE Username = #Username);
IF #UserID > 0
BEGIN
UPDATE User
SET LastLogin = GETDATE()
WHERE UserID = #UserID;
END
INSERT INTO UserLogger (Username, UserID, TimeStamp)
VALUES (#Username, #UserID, #Timestamp);
COMMIT TRANSACTION;
SELECT #UserID
END
However I can't make it work for some syntax errors that I can't figure out.
The User table looks like this:
CREATE TABLE [dbo].[User]
(
[UserID] [INT] IDENTITY(1,1) NOT NULL,
[UserName] [VARCHAR](50) NOT NULL,
[Enabled] [BIT] NOT NULL,
[LastLogin] [TIMESTAMP] NOT NULL,
PRIMARY KEY CLUSTERED ([UserID] ASC)
)
The Audit table looks like this:
CREATE TABLE [dbo].[UserLogger]
(
[UserID] [INT] IDENTITY(1,1) NOT NULL,
[UserName] [VARCHAR](50) NOT NULL,
[Name] [VARCHAR](100) NULL,
[TS] [TIMESTAMP] NULL,
[IpAddress] [NCHAR](10) NULL
) ON [PRIMARY]
I get an error
Cannot update a timestamp column
which I don't see why.
Any idea?

timestamp is not what you think it is. It is some sort of internal representation of the row address. Here is an explanation.
Use datetime or datetime2 to fix your problem.
This is made all the more confusing, because CURRENT_TIMESTAMP doesn't return a timestamp.

Related

Stored Procedure With Input Output Scope Identity

I have two tables namely User and UserRole, where I want to pass the values via stored procedures with below tables. I need help how I can create a procedure which inserts into both the tables assuming a user has only one role i.e either User or Admin.
The Id parameter inserted into second table must be the Id of User Table.
Please suggest me.
CREATE TABLE [dbo].[User](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[Username] [nvarchar](50) NULL,
[Password] [nvarchar](50) NULL,
)
CREATE TABLE [dbo].[UserRole](
[Id] [int] IDENTITY(1,1) NOT NULL,
[UserId] [int] Foreign Key References User(ID)NOT NULL,
[Role] [nvarchar](50) NULL
)
CREATE PROCEDURE [dbo].[AddUserRole]
#Name VARCHAR(50),
#Username DATETIME,
#Password INT,
#Role NVARCHAR(50),
#Id INT OUT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [WH].[dbo].[User]
(
Name,
Username,
Password
)
VALUES
(
# Name,
#Username,
#Password
);
SET # Id = SCOPE_IDENTITY();
INSERT INTO [WH].[dbo].[UserRole]
(
UserId,
Role
)
VALUES
(
#Id,
# Role
);
END;
Use output method, detail here
try Something like this
DECLARE #MyTableVar table( Id int);
INSERT INTO [WH].[dbo].[User] (Name, Username, Password)
OUTPUT INSERTED.Id INTO #MyTableVar
VALUES (#Name, #Username, #Password);
INSERT INTO [WH].[dbo].[UserRole] (UserId, Role )
SELECT Id, #Role FROM #MyTableVar;
Now this working fine !
CREATE TABLE [dbo].[TempUser](
[Id] [int] IDENTITY(1,1) NOT NULL Primary key,
[Name] [nvarchar](50) NULL,
[Username] [nvarchar](50) NULL,
[Password] [nvarchar](50) NULL,
)
CREATE TABLE [dbo].[TempUserRole](
[Id] [int] IDENTITY(1,1) NOT NULL,
[UserId] [int] Foreign Key References TempUser(ID)NOT NULL,
[Role] [nvarchar](50) NULL
)
--ALTER TABLE TempAddUserRole DROP CONSTRAINT FK__TempUserR__UserI__503293D2;
EXEC TempAddUserRole 'Gulshan','12 sep 2018','1','Admin'
EXEC TempAddUserRole 'Gulshan','12 sep 2018','1','Admin',
ALTER PROCEDURE [dbo].[TempAddUserRole]
#Name VARCHAR(50),
#Username DATETIME,
#Password INT,
#Role NVARCHAR(50)
AS
BEGIN
SET NOCOUNT ON;
declare #Id INT
INSERT INTO [TempUser]
(
Name,
Username,
Password
)
VALUES
(
#Name,
#Username,
#Password
);
SET #Id = SCOPE_IDENTITY();
INSERT INTO [TempUserRole]
(
UserId,
Role
)
VALUES
(
#Id,
#Role
);
END;

Inserted clause returns 0 when used with triggers

I'm trying to get the last inserted rows Id from an inserts statement on the following table using SQL server 2012
[dbo].[Table](
[TableId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[CreatedBy] [nvarchar](50) NULL,
[CreatedDate] [datetime2](7) NOT NULL,
[ModifiedBy] [nvarchar](50) NULL,
[ModifiedDate] [datetime2](7) NULL,
CONSTRAINT [pk_Table] PRIMARY KEY CLUSTERED
(
[TableId] ASC
)
I'm also using an audit triggers on that table that are as follows:
trigger [dbo].[trigger_Table_auditColumnAutoInsert]
on [dbo].[Table]
instead of insert
/**************************************************************
* INSTEAD OF trigger on table [dbo].[Table] responsible
for automatically inserting audit column data
**************************************************************/
as
begin
set nocount on
declare #currentTime datetime2
set #currentTime = GETUTCDATE()
insert into [dbo].[Table]
(
Name,
CreatedBy,
CreatedDate,
ModifiedBy,
ModifiedDate
)
select
Name,
ISNULL(CreatedBy, system_user),
#currentTime,
NULL,
NULL
from inserted
select SCOPE_IDENTITY() as [TableId]
goto EOP -- end of procedure
ErrorHandler:
if (##trancount <> 0) rollback tran
EOP:
end
I used different approaches, but nothing 'SAFE' seems to work.
Using scope identity returns null
insert into dbo.[Table](Name) Values('foo')
select SCOPE_IDENTITY()
Using OUTPUT INSERTED always returns 0 for the identity coloumns; although it returns the other inserted values:
declare #tmpTable table
(
TableId int,
Name nvarchar (50)
)
INSERT INTO [dbo].[Table]([Name])
output inserted.TableId, inserted.Name into #tmpTable
VALUES('foo')
select * from #tmpTable
TableId Name
0 foo
I know of another solution to get the inserted Id from the triggers itself, by executing a dynamic sql command as follows:
declare #tmpTable table (id int)
insert #tmpTable (id )
exec sp_executesql N'insert into dbo.[Table](Name) Values(''foo'')'
select id from #tmpTable
I couldn't figure out why in the first 2 cases it is not working; why the SCOPE_IDENTITY() does not work although the triggers execute in the same transaction? And also why the INSERTED clause returns 0 for the identity column.
It appears that the following requirements apply to your audit column data:
Use the insert value supplied for CreatedBy, or use SYSTEM_USER by default.
Always use GETUTCDATE() for CreatedDate.
If the INSTEAD OF trigger (rather than an AFTER trigger) is not essential to your requirements, then you can use DEFAULT constraints on your audit columns and an AFTER INSERT trigger to enforce requirement #2.
CREATE TABLE [dbo].[Table]
(
[TableId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[CreatedBy] [nvarchar](50) NOT NULL CONSTRAINT [DF_Table_CreatedBy] DEFAULT SYSTEM_USER,
[CreatedDate] [datetime2](7) NOT NULL CONSTRAINT [DF_Table_CreatedDate] DEFAULT GETUTCDATE(),
[ModifiedBy] [nvarchar](50) NULL,
[ModifiedDate] [datetime2](7) NULL,
CONSTRAINT [pk_Table] PRIMARY KEY CLUSTERED ([TableId] ASC)
)
GO
CREATE TRIGGER Trigger_Table_AfterInsert ON [dbo].[Table]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON
UPDATE [dbo].[Table] SET [CreatedDate]=GETUTCDATE()
FROM [dbo].[Table] AS T
INNER JOIN INSERTED AS I ON I.[TableId]=T.[TableId]
END
GO
Then, both SCOPE_IDENTITY() and OUTPUT INSERTED techniques to get the new TableId value work as expected.
If the INSTEAD OF trigger is essential to your implementation, then SELECT ##IDENTITY is an alternative to SCOPE_IDENTITY.

drop rows in a user defined table type

i have the following type:
CREATE TYPE [dbo].[locationTable] AS TABLE(
[location_id] [varchar](100) NULL,
[name] [nvarchar](100) NULL,
[address] [varchar](100) NULL,
[latitude] [varchar](100) NULL,
[longitude] [varchar](100) NULL,
[distance] [varchar](100) NULL,
[state] [varchar](100) NULL,
[sub_cat] [varchar](100) NULL,
[idCat] [varchar](100) NULL,
[icon_link] [varchar](100) NULL,
[checkinsCount] [int] NULL
)
GO
and i'm passing a table as parameter having the above type to a stored procedure... but i need to delete some rows from this table in my stored procedure but i keep getting that it cannot be modified.... sql is always requesting to define the table as readonly and in this case i can not modify
A table parameter to a stored procedure must be readonly. MSDN says:
Note that the READONLY keyword is required for declaring a table-valued parameter.
You can solve this dilemma by copying the content to a local table variable. For example:
if exists (select * from sys.procedures where name = 'TestProc')
drop procedure TestProc
if exists (select * from sys.types where name = 'TestType')
drop type TestType
go
create type TestType as table (id int, name varchar(20))
go
create procedure dbo.TestProc
#par TestType readonly
as
declare #t TestType
insert #t select * from #par
delete #t where id = 2
select * from #t
go
declare #p1 TestType
insert #p1 values (1,'a'), (2,'b'), (3,'c');
exec dbo.TestProc #p1

SCOPE_IDENTITY() id's conflicts

I have a 3 tables UniversityReg, SupporterReg & Login. If university or supporter register with the system, always general details goes to their table & login details goes to Login table. In here I use scope_identity.
I'm getting error when I go to save supporter reg details.
Errors
Msg 515, Level 16, State 2, Procedure SupporterReg_SP, Line 16
Cannot insert the value NULL into column 'SupporterId', table 'CounsellingDB.dbo.SupporterReg'; column does not allow nulls. INSERT fails.
Msg 515, Level 16, State 2, Procedure SupporterReg_SP, Line 20
Cannot insert the value NULL into column 'LoginID', table 'CounsellingDB.dbo.Login'; column does not allow nulls. INSERT fails.
UniversityReg SP
ALTER PROCEDURE [dbo].[UniversityReg_SP]
(
#Username varchar(50),
#Password varchar(50),
#UniversityName varchar(50) ,
#GovernmentRegNo varchar(50) ,
#Country varchar(50) ,
#CreatedBy varchar(50)
)
AS
DECLARE #LoginID int
INSERT INTO UniversityReg (UniversityName,GovernmentRegNo,Country,CreatedBy,ShortCode)values(#UniversityName,#GovernmentRegNo,#Country,#CreatedBy,'UNI')
SET #LoginID = SCOPE_IDENTITY();
INSERT INTO Login values(#LoginID,#Username,#Password,'UNI')
RETURN
SupporterReg_SP
CREATE PROCEDURE [dbo].[SupporterReg_SP]
(
#UserName varchar(50),
#Password varchar(50),
#SupporterName varchar(50),
#University varchar(50) ,
#ContactNo varchar(50),
#Email varchar(50),
#StudentLocation varchar(50)
)
AS
DECLARE #LoginID int
INSERT INTO SupporterReg(SupporterName,University,ContactNo,Email,StudentLocation,ImagePath,ShortCode)V alues(#SupporterName,#University,#ContactNo,#Email,#StudentLocation,'','SUP')
SET #LoginID = SCOPE_IDENTITY();
INSERT INTO Login values(#LoginID,#UserName,#Password,'SUP')
RETURN
UniversityReg Table
[UniversityId] [int] IDENTITY(1,1) NOT NULL,
[Username] [varchar](50) NULL,
[Password] [varchar](50) NULL,
[UniversityName] [varchar](50) NULL,
[GovernmentRegNo] [varchar](50) NULL,
[Country] [varchar](50) NULL,
[CreatedBy] [varchar](50) NULL,
[ShortCode] [varchar](50) NULL,
Login Table
[LoginID] [int] NOT NULL,
[UserName] [nvarchar](50) NOT NULL,
[Password] [nvarchar](50) NOT NULL,
[ShortCode] [nvarchar](50) NULL
SupporterReg table
[SupporterId] [int] NOT NULL,
[SupporterName] [varchar](50) NULL,
[University] [varchar](50) NULL,
[ContactNo] [varchar](50) NULL,
[Email] [varchar](50) NULL,
[StudentLocation] [varchar](50) NULL,
[ImagePath] [varchar](50) NULL,
[ShortCode] [varchar](50) NULL,
In your stored procedures #LoginID is the ID for SupporterReg or UniversityReg, not for Login
In Login, your LoginID column should be defined as int identity(1,1) and you should specify column names in your insert
INSERT INTO Login (column, names, go here)
values(#LoginID,#UserName,#Password,'SUP')
Your trouble is that your procedure is attempting to insert multiple values into the SupporterReg table however you are not including a value for the SupporterID column, which has been defined a NOT NULL.
You can either pass a value along with that insert statement, or adjust the table DDL so that it's an IDENTITY column and will fill that data for you.
Your design is wrong. You should not be using the id from two different tables in the login table as the loginid. This is a model that can not work in practice as both tables will sooner or later have the same id and you won't know which one the login relates to. You have a the parent child relationship reversed.
The correct design is to insert to Login first making the loginid the identity. Then insert to the child table of either UniversityReg or SupporterReg. When you do this you can have FKs and referntial integrity.

How to create a trigger to modify date column on table with double key

I need to create a trigger that saves the current date&timestamp into the row when it is being inserted or updated.
Table structure
[Period] [char](7) NOT NULL,
[CompanyAlfaCode] [char](2) NOT NULL,
[SamplePercentaje] [int] NULL,
[Usuario] [varchar](50) NULL,
[Date] [Datetime] NULL,
Thank you very much.
Maybe something like:
CREATE TRIGGER DateTrigger ON CompanySampleConfig
AFTER INSERT,UPDATE
AS
BEGIN
UPDATE CompanySampleConfig SET [Date] = current_timestamp
WHERE EXISTS
(SELECT '' FROM inserted
WHERE CompanySampleConfig.[Period] = inserted.[Period]
AND CompanySampleConfig.[CompanyAlfaCode] = inserted.[CompanyAlfaCode]
END