Get back the id of each insert in SQL Server - sql

Let's say we want to insert two users and I want to know the userId of each record I inserted.
Example:
Db:
User.lookup database with these columns:
UserId(PK, identity) | Username
Setup, insert two users:
declare #users table (uniqueId INT, name nvarchar(100));
insert into #users (0, 'TestUser')--Two users with the same name, they'll get a different userid in the db
insert into #users (1, 'TestUser')--Uniqueid is just an autonumber I use to tell the difference between them.
Insert statement:
insert into user.lookup (userName)
output inserted.userid
select name from #users;
This will return two usersIds, example 1 & 2. But how do I know which of the two users got which userId?
I can differentiate them in code with their 'uniqueid' I pass but I don't know how to return it.

Don't just output the id. You can include other columns:
insert into user.lookup (userName)
output inserted.*
select name from #users;
Here is a db<>fiddle.

You can't correlate the inserted rows with the database-assigned IDs, at least not without inserting an alternate key as well. INSERT ... OUTPUT will not let you output a row that wasn't actually inserted, so the column that correlates the un-keyed rows with the new key values has to be actually inserted.
So the options are:
To use a SEQUENCE instead of IDENTITY and and either assign IDs to the table variable before insert, or assign IDs to the entities on the client, eg by calling sp_sequence_get_range.
Use MERGE instead of INSERT. This is what Entity Framework Core does. See eg The Case of Entity Framework Core’s Odd SQL

As Gordon explained, one can output more than 1 column.
But just to put my 2 cents in, such insert doesn't really need an intermediate table variable.
create table lookup (
lookupId int identity primary key,
userName nvarchar(100),
createdOn datetime2 not null
default sysdatetime()
)
GO
✓
insert into lookup (userName) values
('TestUser1')
,('TestUser2')
;
GO
2 rows affected
insert into lookup (userName)
output inserted.lookupId, inserted.userName
values
('Testuser3'),
('Testuser3')
GO
lookupId | userName
-------: | :--------
3 | Testuser3
4 | Testuser3
select lookupId, userName
--, convert(varchar,createdOn) as createdOn
from lookup
order by lookupId
GO
lookupId | userName
-------: | :--------
1 | TestUser1
2 | TestUser2
3 | Testuser3
4 | Testuser3
db<>fiddle here

Related

If an Order By clause is specified, are identity values consistent? [duplicate]

When inserting multiple rows in a table, is there any guarantee that they go in in the order I specify? For instance, take the following:
DECLARE #blah TABLE
(
ID INT IDENTITY(1, 1),
Name VARCHAR(100) NOT NULL
);
INSERT INTO #blah (Name)
VALUES('Timmy'),
('Jonny'),
('Sally');
SELECT * FROM #blah
Is there any guarantee that Sally will have a higher primary key than Timmy?
The very similar question was asked before.
You can specify an ORDER BY in the INSERT.
If you do that, the order in which the IDENTITY values are generated is guaranteed to match the specified ORDER BY in the INSERT.
Using your example:
DECLARE #blah TABLE
(
ID INT IDENTITY(1, 1) NOT NULL,
Name VARCHAR(100) NOT NULL
);
INSERT INTO #blah (Name)
SELECT T.Name
FROM
(
VALUES
('Timmy'),
('Jonny'),
('Sally')
) AS T(Name)
ORDER BY T.Name;
SELECT
T.ID
,T.Name
FROM #blah AS T
ORDER BY T.ID;
The result is:
+----+-------+
| ID | Name |
+----+-------+
| 1 | Jonny |
| 2 | Sally |
| 3 | Timmy |
+----+-------+
That is, Name have been sorted and IDs have been generated according to this order. It is guaranteed that Jonny will have the lowest ID, Timmy will have the highest ID, Sally will have ID between them. There may be gaps between the generated ID values, but their relative order is guaranteed.
If you don't specify ORDER BY in INSERT, then resulting IDENTITY IDs can be generated in a different order.
Mind you, there is no guarantee for the actual physical order of rows in the table even with ORDER BY in INSERT, the only guarantee is the generated IDs.
In a question INSERT INTO as SELECT with ORDER BY Umachandar Jayachandran from MS said:
The only guarantee is that the identity values will be generated based
on the ORDER BY clause. But there is no guarantee for the order of
insertion of the rows into the table.
And he gave a link to Ordering guarantees in SQL Server, where Conor Cunningham from SQL Server Engine Team says:
INSERT queries that use SELECT with ORDER BY to populate rows guarantees how identity values are computed but not the order in which
the rows are inserted
There is a link to MS knowledge base article in the comments in that post: The behavior of the IDENTITY function when used with SELECT INTO or INSERT .. SELECT queries that contain an ORDER BY clause, which explains it in more details. It says:
If you want the IDENTITY values to be assigned in a sequential fashion
that follows the ordering in the ORDER BY clause, create a table that
contains a column with the IDENTITY property and then run an INSERT ... SELECT ... ORDER BY query to populate this table.
I would consider this KB article as an official documentation and consider this behaviour guaranteed.
Your best two options are:
Process the rows individually - insert the parent row, get the ID, then insert the children, then insert the next parent row, etc.
Alternatively, assuming that the data has an actual identifier in the real world (for example, email address, SSN, etc.) then you can use that to join back to your parent table when inserting into the child table.

How to insert row in a table every time I insert a new row in the main table?

I have a Vb.net app that is connected to an Access 2010* Database, I have a table with personal information of many students and another table with multiple true/false fields for every course the student has succed.
The structure is something like this
Table students
|Id_student | Name | Phone |
Table finishedCourses
| Id_stutent | chemistry | physics | maths |
How can I add a new row into finishedCourses table every time that I insert a new row into students table.
I don't know how add the rows with the same id in both tables.
I expect something like this
Table students
Id_student | Name | Phone
1234456 | abc | 12432534645
Table finishedCourses
Id_stutent | chemistry | physics | maths
1234456 | false | false | false
The default values for Courses are `False'. Initial status of each course is incomplete.
I tried to undestand what you want, you want to insert a initial values in finishedCourses when you insert a student information in table students, am I right?
I am not familiar with the Access database, I realized that the Access database may not have the trigger function, otherwise you can use the trigger to implement your requirement.
And in this problem, you can just write two insert SQLs to complete this with the same studentId, like below:
insert into students(id_101, 'Bob', '88089901');
insert into finishedCourses(id_101, false, false, false...);
If you are using SQL server then you can use trigger. The sample code is as below, I have no idea about MS-Access
CREATE TRIGGER trgAfterInsert ON [dbo].[students]
FOR INSERT
AS
DECLARE #Id_stutent int;
SELECT #Id_stutent=i.Id_student FROM inserted i;
INSERT INTO finishedCourses
(Id_stutent,chemistry,physics,maths)
VALUES(#Id_stutent ,false , false , false ,false );
GO

how to insert a data into many tables

For example, I have a 3 table
Table name: Role
table Attributes: roleid (pk), rolename
role to user one to many
Table name: user
table Attributes: roleid (fk), userid(pk), trackingid(fk) username, password, email
user to tacking one to one
Table name: tracking
table Attributes: trackingid(pk) approvalstatus*, status, createdby, createdDate(yyyy-mm-dd).
*meaning of attributes
approval status - admin will approve any changes so it can be pending, approved or rejected
status is to indicated whether the change request is new user/ edit or delete user.
How do I do a insert into statement to insert a new user for approval. As, when you insert the data in the database should look like this
+----------+----------+--------------------+----------+--------+----------------+-----------+-------------+
| username | password | email | rolename | status | approvalstatus | createdby | createdDate |
+----------+----------+--------------------+----------+--------+----------------+-----------+-------------+
| harry | password | harry#yahoo.com.sg | Admin | New | Pending | Barry | 2016-09-20 |
+----------+----------+--------------------+----------+--------+----------------+-----------+-------------+
This really depends on how you're interacting with the Database.
If you're using an ORM like Entity Framework or NHibernate, this comes out of the box depending on how you map your tables.
For information on this please view:
Entity Framework
NHibernate
If you're doing this direct to SQL Server you can use the following:
Stored Procedure - In this case you'll call a single stored procedure on your DB Server, which will do three inserts for you based on your inputs.
You can perform 3 operations to the DB using a single connection.
Regardless of whether you pick a Stored Proc. or manual statement your inserts will look like:
INSERT INTO ROLE (RoleID, RoleName) VALUES (newID(), 'Your Role Name'); --If you have auto increment on your PK you can ignore inserting into RoleID. Most Systems I work with now use GUID's for ID's so this is just an example.
INSERT INTO User (RoleID, UserID, TrackingID, UserName, Password, Email) Values (....)
INSERT INTO Tracking (TrackingID, ApprovalStatus, Status, CreatedBy, CreatedDate) values (....)
Once you have an entry in your DB you can update that using:
UPDATE Tracking SET ApprovalStatus = 'whatever you want here' where id = X
IF you need to maintain history of tracking, rather than update Tracking, you need to insert a new row and always make sure when you're SELECT'ing your data, you get the latest one based on the DateTime stamp.
Your table in your question is misleading. You're joining three tables to get those results, which is maybe what you want on our output.

SQL - keep values with UPDATE statement

I have a table "news" with 10 rows and cols (uid, id, registered_users, ....) Now i have users that can log in to my website (every registered user has a user id). The user can subscribe to a news on my website.
In SQL that means: I need to select the table "news" and the row with the uid (from the news) and insert the user id (from the current user) to the column "registered_users".
INSERT INTO news (registered_users)
VALUES (user_id)
The INSERT statement has NO WHERE clause so i need the UPDATE clause.
UPDATE news
SET registered_users=user_id
WHERE uid=post_news_uid
But if more than one users subscribe to the same news the old user id in "registered_users" is lost....
Is there a way to keep the current values after an sql UPDATE statement?
I use PHP (mysql). The goal is this:
table "news" row 5 (uid) column "registered_users" (22,33,45)
--- 3 users have subscribed to the news with the uid 5
table "news" row 7 (uid) column "registered_users" (21,39)
--- 2 users have subscribed to the news with the uid 7
It sounds like you are asking to insert a new user, to change a row in news from:
5 22,33
and then user 45 signs up, and you get:
5 22,33,45
If I don't understand, let me know. The rest of this solution is an excoriation of this approach.
This is a bad, bad, bad way to store data. Relational databases are designed around tables that have rows and columns. Lists should be represented as multiple rows in a table, and not as string concatenated values. This is all the worse, when you have an integer id and the data structure has to convert the integer to a string.
The right way is to introduce a table, say NewsUsers, such as:
create table NewsUsers (
NewsUserId int identity(1, 1) primary key,
NewsId int not null,
UserId int not null,
CreatedAt datetime default getdaete(),
CreatedBy varchar(255) default sysname
);
I showed this syntax using SQL Server. The column NewsUserId is an auto-incrementing primary key for this table. The columns NewsId is the news item (5 in your first example). The column UserId is the user id that signed up. The columns CreatedAt and CreatedBy are handy columns that I put in almost all my tables.
With this structure, you would handle your problem by doing:
insert into NewsUsers
select 5, <userid>;
You should create an additional table to map users to news they have registeres on
like:
create table user_news (user_id int, news_id int);
that looks like
----------------
| News | Users|
----------------
| 5 | 22 |
| 5 | 33 |
| 5 | 45 |
| 7 | 21 |
| ... | ... |
----------------
Then you can use multiple queries to first retrieve the news_id and the user_id and store them inside variables depending on what language you use and then insert them into the user_news.
The advantage is, that finding all users of a news is much faster, because you don't have to parse every single idstring "(22, 33, 45)"
It sounds like you want to INSERT with a SELECT statement - INSERT with SELECT
Example:
INSERT INTO tbl_temp2 (fld_id)
SELECT tbl_temp1.fld_order_id
FROM tbl_temp1
WHERE tbl_temp1.fld_order_id > 100;

MySQL: Multiple Inserts for a single column

I'm looking for a way to do multiple row inserts when I'm only inserting data for a single column.
Here is the example table:
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | tinyint(4) | NO | PRI | NULL | auto_increment |
| name | varchar(40) | NO | UNI | NULL | |
+-------+-------------+------+-----+---------+----------------+
I want to be able to insert something like ('admin', 'author', 'mod', 'user', 'guest') into the name column for each row.
The MySQL documentation shows that multiple inserts should be in the format:
INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);
However my statement ends up looking like this:
INSERT INTO User_Role(name) VALUES ('admin','author','mod','user','guest');
And I get the following:
ERROR 1136 (21S01): Column count doesn't match value count at row 1
Meaning that it thinks I'm trying to do a single row insert.
I'm not sure if I'm just missing something simple here, but I don't see anything in particular in the MySQL docs for this use case.
your syntax is a bit off. put parentheses around each data "set" (meaning a single value in this case) that you are trying to insert.
INSERT INTO User_Roll(name) VALUES ('admin'), ('author'), ('mod'), ('user'), ('guest');
I will advise you Don't put multiple values in a column.
make a new table:
INSERT INTO table_name (id, name) VALUES (1, 'name1'), (1, 'name2'), (1, 'name3'), (1, 'name4');