Stored Procedure With Input Output Scope Identity - sql

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;

Related

Get Identity of destination table when using **DELETE FROM ... OUTPUT ... INTO**

I use bellow code to archive old data in ArchiveTable and delete archived data from SourceTable
DELETE FROM SourceTable
OUTPUT
DELETED.[ID],
DELETED.[Code],
DELETED.[Title]
INTO ArchiveTable([OldID], [Code], [Title])
WHERE Condition
Structure of tables:
CREATE TABLE [SourceTable](
[ID] [INT] IDENTITY(1,1) NOT NULL,
[Code] [VARCHAR](16) NULL,
[Title] [NVARCHAR](128) NULL,
CONSTRAINT [PK_SourceTable] PRIMARY KEY CLUSTERED ([ID] ASC)
)
GO
CREATE TABLE [ArchiveTable](
[ID] [INT] IDENTITY(1,1) NOT NULL,
[OldID] [INT] NOT NULL,
[Code] [VARCHAR](16) NULL,
[Title] [NVARCHAR](128) NULL,
CONSTRAINT [PK_ArchiveTable] PRIMARY KEY CLUSTERED ([ID] ASC)
)
GO
I need to return deleted records and ArchiveTable.[ID] to application. I change the code like this:
DELETE FROM SourceTable
OUTPUT
DELETED.[ID],
DELETED.[Code],
DELETED.[Title]
INTO ArchiveTable([OldID], [Code], [Title])
OUTPUT DELETED.*
WHERE Condition
This code return deleted records but I don't know how to get ID of ArchiveTable for this records. Look at ArchiveTable structure, It has OldID column that refer to SourceTable.ID and ID column that it is an Identity column of ArchiveTable. I need to ArchiveTable.ID in final result.
You can use a temporary table
CREATE TABLE #DeletedRows(
[ID] [INT] NOT NULL,
[Code] [VARCHAR](16) NULL,
[Title] [NVARCHAR](128) NULL
)
DELETE SourceTable
OUTPUT
DELETED.[ID],
DELETED.[Code],
DELETED.[Title]
INTO #DeletedRows([ID], [Code], [Title])
WHERE Condition
INSERT ArchiveTable([OldID], [Code], [Title])
OUTPUT INSERTED.*
SELECT [ID], [Code], [Title]
FROM #DeletedRows
DROP TABLE #DeletedRows
A variant with a table variable
DECLARE #DeletedRows TABLE(
[ID] [INT] NOT NULL,
[Code] [VARCHAR](16) NULL,
[Title] [NVARCHAR](128) NULL
)
DELETE SourceTable
OUTPUT
DELETED.[ID],
DELETED.[Code],
DELETED.[Title]
INTO #DeletedRows([ID], [Code], [Title])
WHERE Condition
INSERT ArchiveTable([OldID], [Code], [Title])
OUTPUT INSERTED.*
SELECT [ID], [Code], [Title]
FROM #DeletedRows
I found an interesting variant using DML with OUTPUT in SP and INSERT...EXEC... after that:
Test tables:
CREATE TABLE TestTable(
ID int NOT NULL PRIMARY KEY,
Title varchar(10) NOT NULL
)
CREATE TABLE TestTableLog(
LogID int NOT NULL IDENTITY,
OperType char(1) NOT NULL,
CHECK(OperType IN('I','U','D')),
ID int NOT NULL,
Title varchar(10) NOT NULL
)
DML procedures:
CREATE PROC InsTestTable
#ID int,
#Title varchar(10)
AS
INSERT TestTable(ID,Title)
OUTPUT inserted.ID,inserted.Title,'I' OperType
VALUES(#ID,#Title)
GO
CREATE PROC UpdTestTable
#ID int,
#Title varchar(10)
AS
UPDATE TestTable
SET
Title=#Title
OUTPUT inserted.ID,inserted.Title,'U' OperType
WHERE ID=#ID
GO
CREATE PROC DelTestTable
#ID int
AS
DELETE TestTable
OUTPUT deleted.ID,deleted.Title,'D' OperType
WHERE ID=#ID
GO
Tests:
-- insert test
INSERT TestTableLog(ID,Title,OperType)
EXEC InsTestTable 1,'A'
INSERT TestTableLog(ID,Title,OperType)
EXEC InsTestTable 2,'B'
INSERT TestTableLog(ID,Title,OperType)
EXEC InsTestTable 3,'C'
-- update test
INSERT TestTableLog(ID,Title,OperType)
EXEC UpdTestTable 2,'BBB'
-- delete test
INSERT TestTableLog(ID,Title,OperType)
EXEC DelTestTable 3
GO
-- show resutls
SELECT *
FROM TestTableLog
Maybe it'll be interesting to someone.

Cannot update a timestamp column

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.

Insert multiple records using stored procedure

I have two tables
emplyoee (first table)
id primary key auto increment
emp_name varchar
student(second table)
id foriegnkey emplyoee.id
st_name varchar
I want to insert multiple student records for a single employeeid . My code is attached here , but this use to only one student record update. How can I write stored procedure for this need.
I am new with SQL server and stored procedure.
Could you please help me?
create procedure empst_Sp
#emp_name varchar(50),
#st_name varchar(50)
as
begin
insert into emplyoee (emp_name) values (#emp_name)
insert into student(id,st_name) values(SCOPE_IDENTITY(),#st_name)
end
For your case, you can try this code above ( I'm using XML parameter type)
CREATE PROCEDURE EmployeeIns
#EmployeeName NVARCHAR(50),
#Students XML
AS
/*
#Students : <Students>
<Student Name='Studen 1'/>
<Student Name='Studen 1'/>
</Students>
*/
BEGIN
DECLARE #StudenTable TABLE(Name NVARCHAR(50))
DECLARE #EmployeeId INT
INSERT INTO #StudenTable
SELECT Tbl.Col.value('#Name', 'NVARCHAR(50)')
FROM #Students.nodes('//Student') Tbl(Col)
INSERT INTO Emplyoee VALUES(#EmployeeName)
SET #EmployeeId = SCOPE_IDENTITY()
INSERT INTO Student
SELECT #EmployeeId, Name FROM #StudenTable
END
Update 1 :
Your table design should be look like this :
CREATE TABLE [dbo].[Emplyoee](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](150) NULL,
CONSTRAINT [PK_Emplyoee] PRIMARY KEY CLUSTERED
(
[Id] ASC
))
CREATE TABLE [dbo].[Student](
[EmployeeId] [int] NULL,
[Name] [nvarchar](150) NULL,
[Id] [int] IDENTITY(1,1) NOT NULL,
CONSTRAINT [PK_Student] PRIMARY KEY CLUSTERED
(
[Id] ASC
))
The execute code :
EXEC EmployeeIns #EmployeeName='trungtin1710', #Students = '<Students><Student Name="Studen 1"/><Student Name="Studen 1"/></Students>'
All you need is a local variable in which you can set the value retrieved from Scope_Identity:-
CREATE PROCEDURE empst_Sp
#emp_name varchar(50),
#st_name varchar(50)
AS
BEGIN
DECLARE #id INT
INSERT INTO emplyoee (emp_name) VALUES (#emp_name)
set #id = SCOPE_IDENTITY()
INSERT INTO student(id,st_name) VALUES (#id,#st_name)
END
As I understand:
If emplyoee with #emp_name is already exists then insert student records with ID of the emplyoee, if there is not any emplyoee with #emp_name then need to insert new emplyoee and student with ID of the new emplyoee. Yes?
CREATE PROCEDURE empst_Sp
#emp_name varchar(50),
#st_name varchar(50)
AS
BEGIN
DECLARE #EmplyoeeId int
SET #EmplyoeeId = NULL
select #EmplyoeeId = id
from emplyoee
where emp_name = #emp_name
IF #EmplyoeeId IS NULL
BEGIN
insert into emplyoee (emp_name) values (#emp_name)
SET #EmplyoeeId = SCOPE_IDENTITY()
END
insert into student(id, st_name) values(#EmplyoeeId, #st_name)
END

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.