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

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.

Related

Reset identity seed after in SQL Server in table filled with records

I have a Table called Person, this table is non-empty and it's filled with records ( more than 1000 rows )
How can I reset the identity seed of this table?
Id
PersonName
154
Alice
155
John
The query was executed, but the table still has the identity
use [MyDatabase]
DBCC CHECKIDENT ('dbo.Person', RESEED, 0)
GO
Expected result of the table after executing the previous query :
Id
PersonName
1
Alice
2
John
The problem is your understanding; the code is very likely working exactly as it is supposed to, and as I demonstrated.. RESEED resets the value of the next IDENTITY generated, it doesn't change any of the existing values.
Take SQL similar to what I gave in the comments:
CREATE TABLE dbo.Person (ID int IDENTITY(1,1), AbligatoryColumn char(1));
GO
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT N1.N
FROM N N1, N N2, N N3)
INSERT INTO dbo.Person (AbligatoryColumn)
SELECT 'a'
FROM Tally;
GO
SELECT TOP (5) *
FROM dbo.Person
ORDER BY ID ASC;
If you run this you get the follow results:
ID AbligatoryColumn
----------- ----------------
1 a
2 a
3 a
4 a
5 a
Now, let's RESEED the table, and INSERT another row:
DBCC CHECKIDENT ('dbo.Person', RESEED, 0)
GO
INSERT INTO dbo.Person
DEFAULT VALUES;
GO
SELECT TOP (5) *
FROM dbo.Person
ORDER BY ID ASC;
This gives the following data set:
ID AbligatoryColumn
----------- ----------------
1 a
1 NULL
2 a
3 a
4 a
Notice that there are 2 rows where ID has a value of 1. This is because the new row we inserted has used thenew seed, so the next value generated was 1 (as when you RESEED you are defining the last value used, not the next value to be).
Note that you can't UPDATE the value of an IDENTITY, so if we tried the following you would get an error:
UPDATE dbo.Person
SET ID = ID + 1000
WHERE AbligatoryColumn = 'a';
Cannot update identity column 'ID'.
The real question why do you want to change the value? An IDENTITY is just an arbitrary value, it's value doesn't matter.
If you "must" (and I would suggest you don't need to) you would need to CREATE a new table, INSERT the data from your existing table into it (likely with IDENTITY_INSERT enabled) and then DROP your old table, and rename the new one. If you have any foreign key constraints pointing to your current table, you'll need to DROP all of these, and I really hope you have any data referencing the existing ID values, as you'll then need to update all the foreign key values before you recreate the foreign key constraints. As a result your new table would (albeit likely temporarily) need to have both the old and new PK values in separate columns.
So, in truth, leave it as it is; the value of an IDENTITY literally doesn't matter. It's an arbitrary value and it whether the first value starts at 1, 17, or -167 or if there are numbers "missing" is irrelevant to functionality of what IDENTITY is there to achieve; an always ascending value.

Get back the id of each insert in SQL Server

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

When using OUTPUT in an insert statement, if you specify the order by in the select does it honor the order specified in the select?

I am inserting data into two tables. Within each insert there is an OUTPUT to a #temp table each with an identity column. The select that is generating the data for the insert has the same order by for each insert. Later on I join the two #temp tables by the Identity column. What I would expect is that the identity column numbers would line up as the order by is specified on both sides when inserting. Every long once in a while it appears those numbers don't match up and the only thing I can think of is that perhaps the OUTPUT isn't always honoring the order by in the select statements when writing the OUTPUT data to the temp tables.
CREATE TABLE #TempTable
(
RowNumber Integer IDENTITY (1,1) NOT NULL,
TableID Integer
CONSTRAINT PK_TableID PRIMARY KEY NONCLUSTERED (RowNumber)
)
INSERT INTO Table
(column1,column2,column3,etc)
OUTPUT
INSERTED.ID
INTO #TempTable
(ID)
SELECT
column1,column2,column3,etc
FROM
Other table
ORDER BY
SourceFlag,
StoreID,
storenumber,
EstablishDate,
TableID
What I would expect is that the statements would insert for example 25 rows in both statements in the same order 1 through 25. Then I should be able to join based on the row number 1 = 1, 25= 25, etc. in order to get the matching data. What I think is happening is somehow that order is getting messed up, so that row #1 from the first insert really matches say row #14 from the second, so when I later join 1 on 1 I'm getting mismatched data.
Apparently, it doesn't:
However, SQL Server does not guarantee the order in which rows are
processed and returned by DML statements using the OUTPUT clause.
You need to identify a natural key in your data and then reference it to match the newly inserted rows with the OUTPUT resultset.
Alternatively, you can replace the INSERT with MERGE; in this case, you will be able to catch the newly created identity values for your records in the OUTPUT clause.

Insert into a row at specific position into SQL server table with PK

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

Sort MySQL column in alphanumeric order

How can I sort into MySQL in AlphaNumeric order;
Suppose I have a table with data like this.
Name + ID
AA | 10
AE | 2
AD | 1
When I sort column ID the result is
1
10
2
But when I add + 0 in select statement like this for column id
SELECT * FROM tableName ORDER BY columnName + 0;
1
2
10
But the result for column Name is this
AA
AE
AD
I already refer to this link but it doesn't work for me.
Alphanumeric Order By in Mysql
Note: All column type is varchar and I cannot predict what data will be inserted.
I know my problem is simple but still I can't get the result I want.
Also I cannot use Java to sort the result because I use LIMIT on it.
I'll appreciate any help.Thanks
Additional Info: (Sample Table to be sorted base on Name and ID)
The header1 is the Name which set to another table and same with header2 w/c is ID
CREATE TABLE IF NOT EXISTS `sort` (
`header1` varchar(200) DEFAULT NULL,
`header2` varchar(200) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `sort` (`header1`, `header2`) VALUES
('AA', '10'),
('AE', '2'),
('AD', '1'),
('AD', '1'),
('AF', 'a'),
('AF', 'a1'),
('1', 'a1');
Change your field's datatype to a numerical data type instead of a Character type.
The plus zero must work because it is converting the data to a number.
It seems like its not sorting because it shows the order how I inserted the data.
Now it's clear! The problem is your misunderstanding how tables work. There's no defined order for a table. That's it. Whenever you do a SELECT without an ORDER BY, the DB is free to return the columns in any order it likes. And there's nothing you could do about it.
Normally, the rows get returned in an order determined by the primary key. Tf there's none, the insertion order gets used, unless there are deletions. After deletions, the DB puts the new records somewhere to fill the gaps. However, this is no guaranteed behavior, when you switch to another version or whatever, it may change.
Never rely on the order of rows in a table. Always use ORDER BY or a VIEW containing ORDER BY if the order matters.