I have a stored procedure that inserts a user into a table but I want an output value equals to the new inserted UserID in the table but I don't know how to do it can you guys help me?
I have this
ALTER PROCEDURE dbo.st_Insert_User
(
#Nombre varchar(200),
#Usuario varchar(100),
#Password varchar(100),
#Administrador bit,
#Resultado int output
)
AS
INSERT INTO tbl_Users(Nombre, Usuario, Password, Administrador)
VALUES(#Nombre, #Usuario, #Password, #Administrador)
SELECT #resultado = UserID
I also tried
SELECT #resultado = UserID FROM tbl_Users WHERE Usuario = #Usuario
SELECT SCOPE_IDENTITY()
will give you the identity of the row
For SQL Server, you want to use the OUTPUT clause. See information and examples in Books Online here. It does cover your case-- as it mentions "The OUTPUT clause may be useful to retrieve the value of identity or computed columns after an INSERT or UPDATE operation."
(If this is for real world purposes, you do of course have security concerns in storing passwords that you should address.)
Add at the end
select ##Identity
Related
I have a stored procedure up_InsertEmployees. I have a functionality where I am uploading a batch of employee details into the web application. This functionality inserts the employees details into the DB using the above mentioned stored procedure.
The stored procedure goes something like this
create procedure 'sp_InsertEmployees'
(#EmployeeUN,
#FirstName,
#LastName,
#City, #Ref)
BEGIN
declare #BatchRef varchar(20)
set #BatchRef = #Ref+GetUTCDate()
Insert into Employee(EmployeeUN, FirstName, LastName, City, BatchRef)
Values(#EmployeeUN, #FirstName, #LastName, #City, #BatchRef)
END
Here the column Ref holds the reference of the batch upload that I have performed. The value BatchRef has to be the same for all the employees of that particular batch. Since I am using GetUTCDate() the value of BatchRef might change with every employee that is being inserted. Can somebody please let me know how I can calculate the value of BatchRef when the first employee is being inserted and then keep it constant there on? I want this to be done in SQL only and not in the code. And also I want the datetime value to be in BatchRef so that each batch values are unique.
The best way to keep a consistent BatchRef value across multiple rows being inserted is to insert all of the rows from that batch at the same time ;-). And doing so will also have the benefit of being quite a bit more efficient :-).
Assuming you are using SQL Server 2008 or newer, this can be accomplish via Table-Valued Parameters (TVPs). I have detailed the approach in my answer to the following question:
Pass Dictionary<string,int> to Stored Procedure T-SQL
For this particular use-case, the stored procedure would look something like the following:
CREATE PROCEDURE InsertEmployees
(
#EmployeeBatch dbo.EmployeeData READONLY,
#Ref VARCHAR(50)
)
BEGIN
DECLARE #BatchRef VARCHAR(50);
SET #BatchRef = #Ref + CONVERT(VARCHAR(30), GetUTCDate(), 121);
INSERT INTO Employee (EmployeeUN, FirstName, LastName, City, BatchRef)
SELECT eb.EmployeeUN, eb.FirstName, eb.LastName, eb.City, #BatchRef
FROM #EmployeeBatch eb;
END
Anyone stuck on SQL Server 2005 can accomplish the same basic concept using XML. It won't be as efficient as the entire XML document needs to be created in the app code and then parsed into a temp table in the stored procedure, but it is still a lot faster than doing row-by-row operations.
Given:
#Ref is unique per each batch and never reused
#Ref needs to stay unique per each batch, not unique per each Employee that is uploaded
There is no option to alter the upload process from one call per Employee to doing the entire batch as a single set
You want the time that the batch started (i.e. when the first Employee in each batch is uploaded)
Then:
Don't combine #Ref with GETUTCDATE(), but instead track the start time in another table.
CREATE TABLE dbo.EmployeeBatchStartTime
(
BatchRef VARCHAR(50) NOT NULL
CONSTRAINT [PK_EmployeeBatchStartTime] PRIMARY KEY,
StartTime DATETIME NOT NULL
CONSTRAINT [DF_EmployeeBatchStartTime_StartTime] DEFAULT (GETUTCDATE())
);
Then you can check to see if a row for the passed-in value of #Ref exists in that table and if not, insert it, else skip it.
CREATE PROCEDURE InsertEmployee
(
#EmployeeUN DataType,
#FirstName DataType,
#LastName DataType,
#City DataType,
#Ref VARCHAR(50)
)
AS
SET NOCOUNT ON;
IF (NOT EXISTS(
SELECT *
FROM dbo.EmployeeBatchStartTime
WHERE BatchRef = #Ref
)
)
BEGIN
INSERT INTO dbo.EmployeeBatchStartTime (BatchRef)
VALUES (#Ref);
END;
INSERT INTO Employee (EmployeeUN, FirstName, LastName, City, BatchRef)
VALUES (#EmployeeUN, #FirstName, #LastName, #City, #BatchRef);
This not only lets you keep the value of BatchRef clean and usable (instead of combining a DATETIME value into it) but also gives you a DATETIME value that is usable without any error-prone text parsing or conversion from a string into a DATETIME datatype. This means that you can even add an index, if need be, to the StartTime field in the EmployeeBatchStartTime which will allow you to JOIN to that table on BatchRef and then use StartTime in an ORDER BY and it will be rather efficient :). AND, this requires no change at all to the existing app code :) :).
Ive got a question which I am completely stumped on. It reads as follows:
Write a import stored procedure that take information from a table (source) and write it into another table (target).
Im guessing it would look something like the following:
create procedure add_data(#name, #surname)
AS
BEGIN
SELECT name, surname
FROM cardholder
INSERT INTO new_table
values (name, surname)
Is my logic correct or am I completely missing it?
You are almost there. Since you are directly copying from one table to another, you can use the INSERT INTO ... SELECT ... idiom, like so:
create procedure add_data
#name varchar(100)
#surname varchar(100)
AS
BEGIN
INSERT INTO new_table(name,surname)
SELECT name, surname
FROM cardholder
END
Note the 2 changes I made:
How you declare the parameters for the SP
The values clause should consist of the actual values being inserted. Since you are inserting data retrieved from a table rather than fixed values, you use the select query instead.
Try the following code.
create procedure add_data(#name VARCHAR(10), #surname VARCHAR(10))
AS
BEGIN
INSERT INTO new_table(name, surname)
SELECT name, surname
FROM cardholder
END
It would insert data from one table to another so in output what ever the parameters pass it will inserted into new table just plain and simple.
It is not correct. try dis
create procedure add_data
(
#name varchar(100),
#surname varchar(100)
)
AS
BEGIN
INSERT INTO target
SELECT name, surname
FROM cardholder
end
I have a set of tables which track access logs. The logs contain data about the user's access including user agent strings. Since we know that user agent strings are, for all intents and purposes, practically unlimited, these would need to be stored as a text/blob type. Given the high degree of duplication, I'd like to store these in a separate reference table and have my main access log table have an id linking to it. Something like this:
accesslogs table:
username|accesstime|ipaddr|useragentid
useragents table:
id|crc32|md5|useragent
(the hashes are for indexing and quicker searching)
Here's the catch, i am working inside a framework that doesn't give me access to create fancy things like foreign keys. In addition, this has to be portable across multiple DBMSs. I have the join logic worked out for doing SELECTS but I am having trouble figuring out how to insert properly. I want to do something like
INSERT INTO accesslogs (username, accesstime, ipaddr, useragentid)
VALUES
(
:username,
:accesstime,
:ipaddr,
(
CASE WHEN
(
SELECT id
FROM useragents
WHERE
useragents.crc32 = :useragentcrc32
AND
useragents.md5 = :useragentmd5
AND useragents.useragent LIKE :useragent
) IS NOT NULL
THEN
THAT_SAME_SELECT_FROM_ABOVE()
ELSE
GET_INSERT_ID_FROM(INSERT INTO useragents (crc32, md5, useragent) VALUES (:useragentcrc32, :useragentmd5, :useragent))
)
)
Is there any way to do this that doesn't use pseudofunctions whose names i just made up? The two parts i'm missing is how to get the select from above and how to get the new id from a subquery insert.
You will need to do separate inserts to each of the tables. You can not do insert into both at the same time.
If you use MS SQL Server once you inserted you can get inserted id by SCOPE_IDENTITY(), and then use it in another table insert.
I'm not sure there is a cross platform way of doing this. You may have to have a lot of special cases for each supported back end. For Example, for SQL Server you'd use the merge statement as the basis of the solution. Other DBMSs have different names if they support it at all. Searching for "Upsert" might help.
Edt - added the second query to be explicit, and added parameters.
-- SQL Server Example
--Schema Defs
Create Table Test (
id int not null identity primary key,
UserAgent nvarchar(50)
)
Create Table WebLog (
UserName nvarchar(50),
APAddress nvarchar(50),
UserAgentID int
)
Create Unique Index UQ_UserAgent On Test(UserAgent)
-- Values parsed from log
Declare
#UserName nvarchar(50) = N'Loz',
#IPAddress nvarchar(50) = N'1.1.1.1',
#UserAgent nvarchar(50) = 'Test'
Declare #id int
-- Optionally Begin Transaction
-- Insert if necessary and get id
Merge
Into dbo.Test as t
Using
(Select #UserAgent as UserAgent) as s
On
t.[UserAgent] = s.[UserAgent]
When Matched Then
Update Set #id = t.id
When Not Matched Then
Insert (UserAgent) Values (s.UserAgent);
If #id Is Null Set #id = scope_identity()
Insert Into WebLog (UserName, IPAddress, UserAgentID) Values (#UserName, #IPAddress, #id)
-- Optionally Commit Transaction
I want a stored procedure to return the primary key of the new record after it gets executed. I think it will be returned by OUT parameter in the procedure. But how to select the newly inserted row ID ? I don't want to use select MAX(row_id) as it is a multi user environment.
Any procedure sample will be appreciated.
My platform is ISeries DB2 V5 R4. Thanks.
Edit
The row id Column is not an identity column. It uses a sequence for the key which gets generated via a trigger before insert on table.
Edit
Here is what I am trying to do
Begin Stored procedure
Insert into Employees;
(row id gets automatically generated by trigger)
Return row id ;
I want to avoid a select in returning row id.
just set the out parameter to the column that contains the PK.
CREATE PROCEDURE DB2TBL.DO_STUFF (IN Param1 INT, IN Param2 CHAR(32),OUT Param3 INT)
/* Param1 is primary key */
LANGUAGE SQL
P1: BEGIN
DECLARE OUTPARAM INT;
/* Do the stored procedure */
SET OUTPARAM = Param1;
--UPDATED---
Hi Popo,
First off could you give more detail on what you mean when you say the rowid is assigned by a trigger?
If you had a real identity column you would use the IDENTITY_VAL_LOCAL() function like this right after the INSERT: SELECT IDENTITY_VAL_LOCAL() INTO myrowid FROM SYSIBM.SYSDUMMY1; I'm not 100% on that syntax because I generally use embedded SQL and it works differently there so you might have to play with it. IBM documentation is at http://publib.boulder.ibm.com/infocenter/iseries/v6r1m0/index.jsp?topic=/db2/rbafzscaidentity.htm.
However since you are doing something more complicated, I think this alternate method might work. You'll need to re-format your INSERT to be wrapped in a SELECT.
SELECT myrowid
INTO myrowid
FROM FINAL TABLE (
INSERT INTO myfile (myrowid, other_stuff) VALUES (default, 'blah')
)
You'll need to adjust for the proper field names and so on but I think this will do the trick. There's not much documentation but if you want to see it go to http://publib.boulder.ibm.com/infocenter/iseries/v6r1m0/index.jsp?topic=/db2/rbafzbackup.htm and scroll all the way down to the bottom of the page.
Cheers
CREATE PROCEDURE ASF_InsertNewAuthorRequest
(IN #REQUESTTYPE CHAR(1), IN #UserID VARCHAR(18), IN #DATECREATED TIMESTAMP, IN #REQUESTSTATUS CHAR(1))
LANGUAGE SQL
DYNAMIC RESULT SETS 1
P1: BEGIN
DECLARE cursor1 CURSOR WITH RETURN for
SELECT IDENTITY_VAL_LOCAL FROM SYSIBM.SYSDUMMY1;
INSERT INTO AFS_REQUEST
( REQUESTTYPE, "UserID", DATECREATED, REQUESTSTATUS )
VALUES
( #REQUESTTYPE, #UserID, #DATECREATED, #REQUESTSTATUS );
OPEN cursor1;
END P1
INSERT INTO [User] (columns)
OUTPUT inserted.userId
VALUES (#values)
This will return the newly created userId column value... very simple.
We have a user table, every user has an unique email and username. We try to do this within our code but we want to be sure users are never inserted (or updated) in the database with the same username of email.
I've added a BEFORE INSERT Trigger which prevents the insertion of duplicate users.
CREATE TRIGGER [dbo].[BeforeUpdateUser]
ON [dbo].[Users]
INSTEAD OF INSERT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #Email nvarchar(MAX)
DECLARE #UserName nvarchar(MAX)
DECLARE #UserId int
DECLARE #DoInsert bit
SET #DoInsert = 1
SELECT #Email = Email, #UserName = UserName FROM INSERTED
SELECT #UserId = UserId FROM Users WHERE Email = #Email
IF (#UserId IS NOT NULL)
BEGIN
SET #DoInsert = 0
END
SELECT #UserId = UserId FROM Users WHERE UserName = #UserName
IF (#UserId IS NOT NULL)
BEGIN
SET #DoInsert = 0
END
IF (#DoInsert = 1)
BEGIN
INSERT INTO Users
SELECT
FirstName,
LastName,
Email,
Password,
UserName,
LanguageId,
Data,
IsDeleted
FROM INSERTED
END
ELSE
BEGIN
DECLARE #ErrorMessage nvarchar(MAX)
SET #ErrorMessage =
'The username and emailadress of a user must be unique!'
RAISERROR 50001 #ErrorMessage
END
END
But for the Update trigger I have no Idea how to do this.
I've found this example with google:
http://www.devarticles.com/c/a/SQL-Server/Using-Triggers-In-MS-SQL-Server/2/
But I don't know if it applies when you update multiple columns at once.
EDIT:
I've tried to add a unique constraint on these columns but it doesn't work:
Msg 1919, Level 16, State 1, Line 1
Column 'Email' in table 'Users' is of a type
that is invalid for use as a key column in an index.
You can add a unique contraint on the table, this will raise an error if you try and insert or update and create duplicates
ALTER TABLE [Users] ADD CONSTRAINT [IX_UniqueUserEmail] UNIQUE NONCLUSTERED
(
[Email] ASC
)
ALTER TABLE [Users] ADD CONSTRAINT [IX_UniqueUserName] UNIQUE NONCLUSTERED
(
[UserName] ASC
)
EDIT: Ok, i've just read your comments to another post and seen that you're using NVARCHAR(MAX) as your data type. Is there a reason why you might want more than 4000 characters for an email address or username? This is where your problem lies. If you reduce this to NVARCHAR(250) or thereabouts then you can use a unique index.
Sounds like a lot of work instead of just using one or more unique indexes. Is there a reason you haven't gone the index route?
Why not just use the UNIQUE attribute on the column in your database? Setting that will make the SQL server enforce that and throw an error if you try to insert a dupe.
You should use a SQL UNIQUE constraint on each of these columns for that.
You can create a UNIQUE INDEX on an NVARCHAR as soon as it's an NVARCHAR(450) or less.
Do you really need a UNIQUE column to be so large?
In general, I would avoid Triggers wherever possible as they can make the behaviour very hard to understand unless you know that the trigger exists. As other commentatators have said, a unique constraint is the way to go (once you have amended your column definitions to allow it).
If you ever find yourself needing to use a trigger, it may be a sign that your design is flawed. Think hard about why you need it and whether it is performing logic that belongs elsewhere.
Be aware that if you use the UNIQUE constraint/index solution with SQL Server, only one null value will be permitted in that column. So, for example, if you wanted the email address to be optional, it wouldn't work, because only one user could have a null email address. In that case, you would have to resort to another approach like a trigger or a filtered index.