How do I return the row I just inserted including DB-generated identifier?
My SQL is just a standard dynamic SQL insert
insert into dbo.TableName (Col1, Col2) values (#Col1, #Col2);
I have tried select from inserted, but inserted object is not a known object
insert into dbo.TableName (Col1, Col2) values (#Col1, #Col2); select * from inserted;
I have tried using output, but I cannot do that, when there is a trigger on the table
insert into dbo.TableName (Col1, Col2) output inserted.* values (#Col1, #Col2);
Any ideas?
You could insert the row and then use SCOPE_IDENTITY() to get the ID of the row you inserted and return the row with that id from the table.
For example:
INSERT INTO tableA VALUES (a1, a2);
DECLARE #Id INT = SCOPE_IDENTITY();
SELECT * FROM tableA WHERE ID = #Id
You can use OUTPUT clause:
--Sample table
CREATE TABLE IdentityInsert
(
ID int IDENTITY,
A int
)
INSERT IdentityInsert OUTPUT inserted.* VALUES (3)
There is no way to ask SQL Server which row was inserted last unless you are doing so in the same batch as the insert. For example, if your table has an IDENTITY column, you can use SCOPE_IDENTITY() (never use ##IDENTITY, since that can be unreliable if you have or will ever add triggers to the source table):
INSERT dbo.table(column) SELECT 1;
SELECT SCOPE_IDENTITY();
More generally, you can use the OUTPUT clause, which doesn't rely on an IDENTITY column (but will still make it difficult to identify which row(s) the clause identifies if there is no PK):
INSERT dbo.table(column) OUTPUT inserted.* SELECT 1;
If you're not talking about the same batch, then the only real way to identify the last row inserted is to use a date/time column where the timestamp of insertion is recorded. Otherwise it is like you emptied a bag of marbles on the floor, then asked someone to enter the room and identify which one hit the floor last.
You may be tempted or even advised to use the IDENT_CURRENT() function, but I explain here why this is unreliable too.
You could add a column to track this going forward:
ALTER TABLE dbo.table ADD DateInserted DEFAULT CURRENT_TIMESTAMP;
Now you can find the last row(s) inserted by simply:
;WITH x AS (SELECT *, r = RANK() OVER (ORDER BY DateInserted DESC)
FROM dbo.table)
SELECT * FROM x WHERE r = 1;
(If you don't want ties, you can add a tie-breaking column to the ORDER BY, or you can simply change RANK() to ROW_NUMBER() if you don't care which of the tied rows you get.)
You might make the assumption that the last row inserted is the highest identity value, but this isn't necessarily the case. The identity can be reseeded and it can also be overridden using SET IDENTITY_INSERT ON;.
Related
I need to get the maximum number in a column because in a insert operation I have to insert max number in that column +1 for each insert, I did this:
insert into table1(id ,.., field,...)
select newid(), ..., (SELECT CONVERT(VARCHAR(8), FORMAT((MAX(number)+1),'00000000'))
FROM table1)
It works just for the first inserted row, than I get this number for other rows too!
This is too long for a comment.
Why aren't you using an identity column? You can define the number as:
number int identity(1, 1)
If you want the value as a string padded to eight characters, then use a computed column:
number_string as (format(number, '00000000'))
EDIT:
There are strong reasons why you want an identity column and not to calculate the values yourself. You can do what you want using row_number(), where the logic looks like this:
insert into table1(id ,.., field,...)
select newid(), ...,
convert(varchar(8),
(coalesce(t1.max_number, 0) +
row_number() over (order by (select null))
)
)
from table2 t2 cross join
(select max(t1.number) as max_number from table1 t1) t1;
N
Note: I am assuming that the inserts are coming from a different table, but table2 can really be table1.
Very importantly: This is not thread safe. Two different threads can run the same code and result in the same values. The solution to this is locking the entire table. However, that can have very significant performance impacts.
There's a lot to comment on here, and like Gordon, I can't fit it into one.
Firstly, I notice you have a column id and you're inserted the value NEWID() into it. I therefore hope that id isn't your CLUSTERED INDEX, as if it is NEWID() is not doing it any favours. If you are using a uniqueindentifier for your CLUSTERED INDEX, use NEWSEQUENTIALID() instead, and don't provuide the value in the INSERT:
ALTER TABLE dbo.Table1 ADD CONSTRAINT DF_Table1_id DEFAULT NEWSEQUENTIALID() FOR id;
As for your INSERT, as I said in my comment: "Use an IDENTITY column. If you must then have sequential values, use a VIEW and ROW_NUMBER. Let SQL Server gracefully handle the incrementing value. Trying to increment the number yourself is going to only cause you problems, such as race conditions, and your data will be in a far worse position.". Unfortunately you can't change an existing column to an IDENTITY, so this is a little harder. Likely you'll want to do something like:
ALTER TABLE dbo.Table1 ADD number_new int IDENTITY(1,1);
GO
SET IDENTITY_INSERT dbo.Table1 ON;
--UPdate the new column with the existing values
UPDATE dbo.Table1
SET number_new = number;
SET IDENTITY_INSERT dbo.Table1 OFF;
GO
--Drop the old column and rename
ALTER TABLE dbo.Table1 DROP COLUMN number;
EXEC sp_rename N'dbo.Table1.number_new', N'number', N'COLUMN'
As Gordon said, if you simply then need a formatted value (with leading 0's) and no worry about gaps, use a computed column:
ALTER TABLE dbo.Table1 ADD Number_f AS RIGHT(CONCAT('00000000',number),8) PERSISTED;
If, however, you want them to be in sequential order, and update accordingly when a row is deleted, or a INSERT fails, etc, then you can use a view, with the following expression:
RIGHT(CONCAT('00000000',ROW_NUMBER() OVER (ORDER BY number ASC),8)
You can use a ROW_NUMBER() function to increment the IDs. Just replace the +1 with ROW_NUMBER() OVER(). Something like this:
insert into table1(id ,.., field,...)
select (SELECT MAX(number) FROM table1) + ROW_NUMBER() OVER(ORDER BY <field1>), ...
from table1
SQL Fiddle
I am trying to insert multiple rows into SQL. The table contains an external ID column that when using the APP increases this ID by one. The external ID is not the Primary key, but another ID in the table. Currently last external ID is 544. I want to insert 1600 additional rows and have the external ID increase by 1 for every row inserted. I have tried the following, but all of the external IDs end up being 100.
INSERT INTO tableA (externalid,tableuiduid)
VALUES ((select ISNULL(MAX(EXTERNALID) +1, 0)from tableA),newid());
I have also tried this, but it ends up inserting a duplicate external ID, as there are gaps in the numbers.
INSERT INTO tableA (ExternalID,tableAuid)
VALUES ((select count (externalid) + from tableA),newid());
Please let me know what I need to use to have this increase by 1 and not insert a duplicate ID.
The way you're doing it now, depending on your RDBMS, you can use an INSERT INTO ... SELECT statement:
INSERT INTO tableA (externalid, tableuiduid)
select ISNULL(MAX(EXTERNALID) + 1, 0), newid()
from tableA;
But you'll need to execute that 1600 times.
If you have an auxilliary number table or use a recursive CTE, you could use that to generate 1600 rows at once, but without knowing your RDBMS a precise implementation is very difficult.
You could define the field as an automatically incrementing field or sequence, but I get the impression that that isn't a good idea because you're not always going to be determining what the externalid value is.
You should use the +1 outside of ISNULL. And use INSERT INTO .. SELECT.
Try this way:
DECLARE #Cnt as int
SET #Cnt = 0
WHILE (#Cnt < 1600)
BEGIN
INSERT INTO tableA (externalid,tableuiduid)
select ISNULL(MAX(EXTERNALID),0) + 1,newid()
from tableA
SET #Cnt = #Cnt + 1
END
First create a sequence.
create sequence seq1
start with 544
increment by 1
maxvalue 99999;
Then insert.
INSERT INTO tableA (externalid)
VALUES (seq1.nextval())
I want to insert a row into a SQL server table at a specific position. For example my table has 100 rows and I want to insert a new row at position 9. But the ID column which is PK for the table already has a row with ID 9. How can I insert a row at this position so that all the rows after it shift to next position?
Relational tables have no 'position'. As an optimization, an index will sort rows by the specified key, if you wish to insert a row at a specific rank in the key order, insert it with a key that sorts in that rank position. In your case you'll have to update all rows with a value if ID greater than 8 to increment ID with 1, then insert the ID with value 9:
UPDATE TABLE table SET ID += 1 WHERE ID >= 9;
INSERT INTO TABLE (ID, ...) VALUES (9, ...);
Needless to say, there cannot possibly be any sane reason for doing something like that. If you would truly have such a requirement, then you would use a composite key with two (or more) parts. Such a key would allow you to insert subkeys so that it sorts in the desired order. But much more likely your problem can be solved exclusively by specifying a correct ORDER BY, w/o messing with the physical order of the rows.
Another way to look at it is to reconsider what primary key means: the identifier of an entity, which does not change during that entity lifetime. Then your question can be rephrased in a way that makes the fallacy in your question more obvious:
I want to change the content of the entity with ID 9 to some new
value. The old values of the entity 9 should be moved to the content
of entity with ID 10. The old content of entity with ID 10 should be
moved to the entity with ID 11... and so on and so forth. The old
content of the entity with the highest ID should be inserted as a new
entity.
Usually you do not want to use primary keys this way. A better approach would be to create another column called 'position' or similar where you can keep track of your own ordering system.
To perform the shifting you could run a query like this:
UPDATE table SET id = id + 1 WHERE id >= 9
This do not work if your column uses auto_increment functionality.
No, you can't control where the new row is inserted. Actually, you don't need to: use the ORDER BY clause on your SELECT statements to order the results the way you need.
DECLARE #duplicateTable4 TABLE (id int,data VARCHAR(20))
INSERT INTO #duplicateTable4 VALUES (1,'not duplicate row')
INSERT INTO #duplicateTable4 VALUES (2,'duplicate row')
INSERT INTO #duplicateTable4 VALUES (3,'duplicate rows')
INSERT INTO #duplicateTable4 VALUES (4,'second duplicate row')
INSERT INTO #duplicateTable4 VALUES (5,'second duplicat rows')
DECLARE #duplicateTable5 TABLE (id int,data VARCHAR(20))
insert into #duplicateTable5 select *from #duplicateTable4
delete from #duplicateTable4
declare #i int , #cnt int
set #i=1
set #cnt=(select count(*) from #duplicateTable5)
while(#i<=#cnt)
begin
if #i=1
begin
insert into #duplicateTable4(id,data) select 11,'indian'
insert into #duplicateTable4(id,data) select id,data from #duplicateTable5 where id=#i
end
else
insert into #duplicateTable4(id,data) select id,data from #duplicateTable5 where id=#i
set #i=#i+1
end
select *from #duplicateTable4
This kind of violates the purpose of a relational table, but if you need, it's not really that hard to do.
1) use ROW_NUMBER() OVER(ORDER BY NameOfColumnToSort ASC) AS Row to make a column for the row numbers in your table.
2) From here you can copy (using SELECT columnsYouNeed INTO ) the before and after portions of the table into two separate tables (based on which row number you want to insert your values after) using a WHERE Row < ## and Row >= ## statement respectively.
3) Next you drop the original table using DROP TABLE.
4) Then you use a UNION for the before table, the row you want to insert (using a single explicitly defined SELECT statement without anything else), and the after table. By now you have two UNION statements for 3 separate select clauses. Here you can just wrap this in a SELECT INTO FROM clause calling it the name of your original table.
5) Last, you DROP TABLE the two tables you made.
This is similar to how an ALTER TABLE works.
INSERT INTO customers
(customer_id, last_name, first_name)
SELECT employee_number AS customer_id, last_name, first_name
FROM employees
WHERE employee_number < 1003;
FOR MORE REF: https://www.techonthenet.com/sql/insert.php
I am inserting records through a query similar to this one:
insert into tbl_xyz select field1 from tbl_abc
Now I would like to retreive the newly generated IDENTITY Values of the inserted records. How do I do this with minimum amount of locking and maximum reliability?
You can get this information using the OUTPUT clause.
You can output your information to a temp target table or view.
Here's an example:
DECLARE #InsertedIDs TABLE (ID bigint)
INSERT into DestTable (col1, col2, col3, col4)
OUTPUT INSERTED.ID INTO #InsertedIDs
SELECT col1, col2, col3, col4 FROM SourceTable
You can then query the table InsertedIDs for your inserted IDs.
##IDENTITY will return you the last inserted IDENTITY value, so you have two possible problems
Beware of triggers executed when inserting into table_xyz as this may change the value of ##IDENTITY.
Does tbl_abc have more than one row. If so then ##IDENTITY will only return the identity value of the last row
Issue 1 can be resolved by using SCOPE__IDENTITY() instead of ##IDENTITY
Issue 2 is harder to resolve. Does field1 in tbl_abc define a unique record within tbl_xyz, if so you could reselect the data from table_xyz with the identity column. There are other solutions using CURSORS but these will be slow.
SELECT ##IDENTITY
This is how I've done it before. Not sure if this will meet the latter half of your post though.
EDIT
Found this link too, but not sure if it is the same...
How to insert multiple records and get the identity value?
As far as I know, you can't really do this with straight SQL in the same script. But you could create an INSERT trigger. Now, I hate triggers, but it's one way of doing it.
Depending on what you are trying to do, you might want to insert the rows into a temp table or table variable first, and deal with the result set that way. Hopefully, there is a unique column that you can link to.
You could also lock the table, get the max key, insert your rows, and then get your max key again and do a range.
Trigger:
--Use the Inserted table. This conaints all of the inserted rows.
SELECT * FROM Inserted
Temp Table:
insert field1, unique_col into #temp from tbl_abc
insert into tbl_xyz (field1, unique_col) select field1, unique_col from tbl_abc
--This could be an update, or a cursor, or whatever you want to do
SELECT * FROM tbl_xyz WHERE EXISTS (SELECT top 1 unique_col FROM #temp WHERE unique_col = tbl_xyz.unique_col)
Key Range:
Declare #minkey as int, #maxkey as int
BEGIN TRANS --You have to lock the table for this to work
--key is the name of your identity column
SELECT #minkey = MAX(key) FROM tbl_xyz
insert into tbl_xyz select field1 from tbl_abc
SELECT #maxkey = MAX(key) FROM tbl_xyz
COMMIT Trans
SELECT * FROM tbl_xyz WHERE key BETWEEN #minkey and #maxkey
Sybase db tables do not have a concept of self updating row numbers. However , for one of the modules , I require the presence of rownumber corresponding to each row in the database such that max(Column) would always tell me the number of rows in the table.
I thought I'll introduce an int column and keep updating this column to keep track of the row number. However I'm having problems in updating this column in case of deletes. What sql should I use in delete trigger to update this column?
You can easily assign a unique number to each row by using an identity column. The identity can be a numeric or an integer (in ASE12+).
This will almost do what you require. There are certain circumstances in which you will get a gap in the identity sequence. (These are called "identity gaps", the best discussion on them is here). Also deletes will cause gaps in the sequence as you've identified.
Why do you need to use max(col) to get the number of rows in the table, when you could just use count(*)? If you're trying to get the last row from the table, then you can do
select * from table where column = (select max(column) from table).
Regarding the delete trigger to update a manually managed column, I think this would be a potential source of deadlocks, and many performance issues. Imagine you have 1 million rows in your table, and you delete row 1, that's 999999 rows you now have to update to subtract 1 from the id.
Delete trigger
CREATE TRIGGER tigger ON myTable FOR DELETE
AS
update myTable
set id = id - (select count(*) from deleted d where d.id < t.id)
from myTable t
To avoid locking problems
You could add an extra table (which joins to your primary table) like this:
CREATE TABLE rowCounter
(id int, -- foreign key to main table
rownum int)
... and use the rownum field from this table.
If you put the delete trigger on this table then you would hugely reduce the potential for locking problems.
Approximate solution?
Does the table need to keep its rownumbers up to date all the time?
If not, you could have a job which runs every minute or so, which checks for gaps in the rownum, and does an update.
Question: do the rownumbers have to reflect the order in which rows were inserted?
If not, you could do far fewer updates, but only updating the most recent rows, "moving" them into gaps.
Leave a comment if you would like me to post any SQL for these ideas.
I'm not sure why you would want to do this. You could experiment with using temporary tables and "select into" with an Identity column like below.
create table test
(
col1 int,
col2 varchar(3)
)
insert into test values (100, "abc")
insert into test values (111, "def")
insert into test values (222, "ghi")
insert into test values (300, "jkl")
insert into test values (400, "mno")
select rank = identity(10), col1 into #t1 from Test
select * from #t1
delete from test where col2="ghi"
select rank = identity(10), col1 into #t2 from Test
select * from #t2
drop table test
drop table #t1
drop table #t2
This would give you a dynamic id (of sorts)