drop rows in a user defined table type - sql

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

Related

Trying to write a stored procedure with a Identity column to create my table values

I am trying to write a stored procedure that creates a new row of data in my table that has an Identity column. I can't figure out what I'm doing wrong to get it to store my data in the table.
Here is my table
CREATE TABLE [Promotional].[Proposals]
(
[Proposal_Uid] [int] IDENTITY(1,1) NOT NULL,
[Prime_Contract] [nvarchar](30) NULL,
[Sub_Contract] [nvarchar](30) NULL,
[Po_Id] [nvarchar](30) NULL,
[Proposal_Title] [nvarchar](50) NULL,
[Client_Name] [nvarchar](40) NULL,
[Client_Code] [nvarchar](20) NULL,
[Total_Proposal_Amount] [decimal](18, 2) NULL,
[ODC_Amount] [decimal](18, 2) NULL,
[Manager_Name] [nvarchar](30) NULL,
[Admin] [nvarchar](30) NULL,
[Due_Date] [date] NULL,
[Start_Date] [date] NULL,
[End_Date] [date] NULL,
[Proposal_Number] [nvarchar](20) NULL,
[Contract_Type] [nvarchar](16) NULL,
CONSTRAINT [Proposal_Uid]
PRIMARY KEY CLUSTERED ([Proposal_Uid] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
Here is my stored procedure:
ALTER PROCEDURE [Promotional].[Proposals_Create]
(#Prime_Contract nvarchar(30),
#Sub_Contract nvarchar(30),
#Po_Id nvarchar(30),
#Proposal_Title nvarchar(50),
#Client_Name nvarchar(40),
#Client_Code nvarchar(20),
#Total_Proposal_Amount decimal(18,2),
#ODC_Amount decimal(18,2),
#Manager_Name nvarchar(30),
#Admin nvarchar(30),
#Due_Date date,
#Start_Date date,
#End_Date date,
#Proposal_Number nvarchar(20),
#Contract_Type nvarchar(16),
#NewId int output)
AS
BEGIN
IF #NewId IS NULL
BEGIN
INSERT INTO Promotional.Proposals (Prime_Contract, Sub_Contract, Po_Id, Proposal_Title, Client_Name, Client_Code,
Total_Proposal_Amount, ODC_Amount, Manager_Name, Admin, Due_Date, Start_Date, End_Date, Proposal_Number, Contract_Type)
VALUES (#Prime_Contract, #Sub_Contract, #Po_Id, #Proposal_Number, #Client_Name, #Client_Code,
#Total_Proposal_Amount, #ODC_Amount, #Manager_Name, #Admin, #Due_Date, #Start_Date, #End_Date,
#Proposal_Number, #Contract_Type)
SET #NewId = SCOPE_IDENTITY()
END
RETURN #NewId
END
GO
I know it's something small I missed. But, my OUTCOME is that when you run the procedure, it inserts the values in the correct columns, and the primary key is auto updated without having to stick your own value into the statement. My execution of the statement would be as follows
EXEC Promotional.Proposals_Create 'hj','fd','fd','fd','fd','fd','0.23','1.24','fd','fd','2020/08/30','2020/08/30','2020/08/30','fd','fd';
And I get this error:
Msg 201, Level 16, State 4, Procedure Promotional.Proposals_Create, Line 0 [Batch Start Line 0]
Procedure or function 'Proposals_Create' expects parameter '#NewId', which was not supplied.
I shouldn't have to provide that parameter because it should auto update with my PK which is Proposal_Uid. Or do I have to make my Proposal_Uid the value returned from ScopeIdentity?
#NewID should not be a parameter of the procedure since it is an IDENTITY column.
You can get the stored procedure to return the generated id by providing an output variable to store its content. This behaviour is also documented here.
declare #test int;
EXEC Proposals_Create 'hj','fd','fd','fd','fd','fd','0.23','1.24','fd','fd',
'2020/08/30','2020/08/30','2020/08/30','fd','fd', #test OUTPUT;
select #test as 'new_identity';
set #test = null; -- because of the IF statement in your procedure...
EXEC Proposals_Create 'hj','fd','fd','fd','fd','fd','0.23','1.24','fd','fd',
'2020/08/30','2020/08/30','2020/08/30','fd','fd', #test OUTPUT;
select #test as 'new_identity';
The selects return 1 and 2 respectively.
Fiddle (Remark: schema name removed in code above and in fiddle).

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.

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;

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.