max value in insert into - sql

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

Related

Return inserted row

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;.

How do I add an auto incrementing column to an existing vertica table?

I have a table that currently has the following structure
id, row1
(null), 232
(null), 4455
(null), 16
I'd like for id to be an auto incrementing primary key, as follows:
id, row1
1, 232
2, 4455
3, 16
I've read the documentation and it looks like the function that I need is AUTO_INCREMENT and that I can edit the table using an ALTER TABLE statement. However, I can't seem to get the syntax quite right. How do I go about doing this? Is it even possible with a pre-existing table?
What you need to do is the following:
create a new sequence:
CREATE SEQUENCE sequence_auto_increment START 1;
create a new table:
create table tab2 as select * from tab1 limit 0;
insert the data:
insert /*+ direct */ into tab2
select NEXTVAL('sequence_auto_increment'),row1 from tab1;
as #Kermit mentioned the best way to do it in Vertica is to recreate the table(once) instead of multiple times, use the direct hint so you skip the WOS storage(much faster)
As for the column constraint that #Nazmul created, i won't use it Vertica doesn't care to much about constraints, you will need to force him to insert what you want and default constraints are not the way.
You need to update your exiting data something like below
UPDATE table
SET id = table2.id
FROM
(
SELECT row1, RANK() OVER (ORDER BY val) as id
FROM t1;
) as table2
where table.primaryKey = table2.primaryKey
Then you do alter your table using below syntax
-- get the value to start sequence at
SELECT MAX(id) FROM t2;
-- create the sequence
CREATE SEQUENCE seq1 START 5;
-- syntax as of 6.1
-- modify the column to add next value for future rows
ALTER TABLE t2 ALTER COLUMN id SET DEFAULT NEXTVAL('seq1');
If you want to use the Auto_Increment feature,
1)Copy data to temp table
2)Recreate the base table with the column using auto increment
3)Copy back the data to for other columns
If you just want the numbers in, refer the other answer by Nazmul

Insert value into an old identity field

I have a table which had an identity column.
for many reasons we had to remove the identity from that column.
I have a system that inserts a value to that table by the old way, passes null for the identity column.
is there a simple way to define identity column to receive a value in case it is passed to it, and if a value of null is passed, to make that table to set a unique value to that field that is not found in that table (act like identity).
What i mean is, if there is value, insert it.
And if the system tries to insert null act like an identity.
Thanks in advance.
A trigger seems like a good choice for this situation, but there are a lot of ways you can handle the situation.
The example below creates an INSTEAD OF insert trigger that gets the columns that were supposed to be inserted, and generates a new value via a function if the ID column is null.
CREATE TRIGGER myTrigger ON myTable
INSTEAD OF INSERT
AS
INSERT INTO myTable (myIDcolumn, anotherColumn)
SELECT COALESCE(myIDColumn, dbo.SomeMethodToCreateID), anotherColumn
FROM inserted
How that SomeTheodToCreateID function is to work is up to you. One thing you could do is change the SELECT to combine max plus row_number:
SELECT (COALESCE(myIDColumn,
(SELECT MAX(myIDColumn) FROM myTable) + ROW_NUMBER() OVER(ORDER BY anotherColumn)
)
...
Try This
DBCC CHECKIDENT ( '[dbo].[Customers]', RESEED, 1 )

Row number in Sybase tables

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)

Insert into ... Select *, how to ignore identity?

I have a temp table with the exact structure of a concrete table T. It was created like this:
select top 0 * into #tmp from T
After processing and filling in content into #tmp, I want to copy the content back to T like this:
insert into T select * from #tmp
This is okay as long as T doesn't have identity column, but in my case it does. Is there any way I can ignore the auto-increment identity column from #tmp when I copy to T? My motivation is to avoid having to spell out every column name in the Insert Into list.
EDIT: toggling identity_insert wouldn't work because the pkeys in #tmp may collide with those in T if rows were inserted into T outside of my script, that's if #tmp has auto-incremented the pkey to sync with T's in the first place.
SET IDENTITY_INSERT ON
INSERT command
SET IDENTITY_INSERT OFF
As identity will be generated during insert anyway, could you simply remove this column from #tmp before inserting the data back to T?
alter table #tmp drop column id
UPD: Here's an example I've tested in SQL Server 2008:
create table T(ID int identity(1,1) not null, Value nvarchar(50))
insert into T (Value) values (N'Hello T!')
select top 0 * into #tmp from T
alter table #tmp drop column ID
insert into #tmp (Value) values (N'Hello #tmp')
insert into T select * from #tmp
drop table #tmp
select * from T
drop table T
See answers here and here:
select * into without_id from with_id
union all
select * from with_id where 1 = 0
Reason:
When an existing identity column is selected into a new table, the new column inherits the IDENTITY property, unless one of the following conditions is true:
The SELECT statement contains a join, GROUP BY clause, or aggregate function.
Multiple SELECT statements are joined by using UNION.
The identity column is listed more than one time in the select list.
The identity column is part of an expression.
The identity column is from a remote data source.
If any one of these conditions is true, the column is created NOT NULL instead of inheriting the IDENTITY property. If an identity column is required in the new table but such a column is not available, or you want a seed or increment value that is different than the source identity column, define the column in the select list using the IDENTITY function. See "Creating an identity column using the IDENTITY function" in the Examples section below.
All credit goes to Eric Humphrey and bernd_k
Not with SELECT * - if you selected every column but the identity, it will be fine. The only way I can see is that you could do this by dynamically building the INSERT statement.
Just list the colums you want to re-insert, you should never use select * anyway. If you don't want to type them ,just drag them from the object browser (If you expand the table and drag the word, columns, you will get all of them, just delete the id column)
INSERT INTO #Table
SELECT MAX(Id) + ROW_NUMBER() OVER(ORDER BY Id)
set identity_insert on
Use this.
Might an "update where T.ID = #tmp.ID" work?
it gives me a chance to preview the data before I do the insert
I have joins between temp tables as part of my calculation; temp tables allows me to focus on the exact set data that I am working with. I think that was it. Any suggestions/comments?
For part 1, as mentioned by Kolten in one of the comments, encapsulating your statements in a transaction and adding a parameter to toggle between display and commit will meet your needs. For Part 2, I would needs to see what "calculations" you are attempting. Limiting your data to a temp table may be over complicating the situation.