Insert and alter in one statement - sql

I'd like to store a set of data into a database but if it's a pre-existing record, I'd like to alter it. Otherwise, create a new one. Is there a combine statement for that? (Haven't got any when googling.)
Right now, the best I have is to check if already exists and then perform one of the operations. Seems cumbersome to me.
create table Stuff (
Id int identity(1001, 1) primary key clustered,
Beep int unique,
Boop nvarchar(50))

IN MYSQL :
You may use INSERT ... ON DUPLICATE KEY UPDATE .
eg:
INSERT INTO table (a,b,c) VALUES (4,5,6)
ON DUPLICATE KEY UPDATE c=9;
For more information: http://dev.mysql.com/doc/refman/5.6/en/insert-on-duplicate.html

MySQL uses INSERT... ON DUPLICATE KEY and MSSQL uses MERGE
MERGE is supported by Azure, and I can highly recommend this blog article on it, as a good intro to the statement
Here is a merge statement based on the schema provided...
create table #Stuff (
Id int identity(1001, 1) primary key clustered,
Beep int unique,
Boop nvarchar(50),
Baap nvarchar(50)
);
INSERT INTO #Stuff VALUES (1,'boop', 'poop');
INSERT INTO #Stuff VALUES (2,'beep', 'peep');
SELECT * FROM #STUFF;
MERGE #Stuff
USING (VALUES(1,'BeepBeep','PeepPeep')) AS TheNewThing(A,B,C)
ON #Stuff.Beep = TheNewThing.A
WHEN MATCHED THEN UPDATE SET #Stuff.Boop = TheNewThing.B, #Stuff.Baap = 'fixed'
WHEN NOT MATCHED THEN INSERT (Beep,Boop,Baap) VALUES (
TheNewThing.A, TheNewThing.B, TheNewThing.C);
SELECT * FROM #STUFF
I also found a really good SO Q which might make good further reading

yes you can easily do it using pl/sql here is sample code which will help you
http://docs.oracle.com/cd/B10501_01/appdev.920/a96624/01_oview.htm#7106

Related

How to get ID of an identity column?

I have a many-to-many relationship between two tables, Users and Projects.
The table that connects those two together is named ProjectsUsers.
Here is the description of the tables and their relationships:
CREATE TABLE "Users"
(
Email VARCHAR(320) COLLATE SQL_Latin1_General_CP1_CI_AS PRIMARY KEY CHECK(LEN(Email) >= 3),
--More....
);
CREATE TABLE "Projects"
(
ProjectID INT PRIMARY KEY IDENTITY,
--More....
);
CREATE TABLE "ProjectsUsers"
(
UsersEmail VARCHAR(320) COLLATE SQL_Latin1_General_CP1_CI_AS CHECK(LEN(UsersEmail) >= 3) NOT NULL,
ProjectsID INT NOT NULL,
CONSTRAINT ProjectsUsers_PK PRIMARY KEY (UsersEmail, ProjectsID),
CONSTRAINT ProjectsID_FK FOREIGN KEY (ProjectsID) REFERENCES Projects (ProjectID)
ON DELETE CASCADE ON UPDATE CASCADE ,
CONSTRAINT UsersEmail_FK FOREIGN KEY (UsersEmail) REFERENCES Users(Email)
ON DELETE CASCADE ON UPDATE CASCADE
);
I am now trying to create a stored procedure that will insert a new project to the Projects table. After I add the project I want to create a reference to it in the ProjectsUsers table. The problem is, there is no possible way for me to know what the id of the project I just created - thus, I am unable to know what ID should I insert into the ProjectsUsers.
So if my stored procedure was something like this:
INSERT INTO Projects (Project, CreationDate, ProjectName)
VALUES (#project, GETDATE(), #email);
INSERT INTO ProjectsUsers VALUES (#email, ???)
How can I get the ID?
Just use SCOPE_IDENTITY like this:
INSERT INTO Projects (Project, CreationDate, ProjectName)
VALUES (#project, SYSDATETIME(), #email);
DECLARE #ProjectID INT = SCOPE_IDENTITY();
INSERT INTO ProjectsUsers
VALUES (#email, #ProjectID)
More all the relevant details about SCOPE_IDENTITY on the official Microsoft Documentation site.
As Sean Lange mentions, you can use SCOPE_IDENTITY to get last id inserted from within your proc
You can also use the OUTPUT clause and get possibly many ids. You can output in the screen or in a table, but it wont work if you are selecting from a table that has triggers.
Use the OUTPUT clause! Do not use the various identity functions or variables. This directly solves your problem:
DECLARE #ids TABLE (ProjectId int);
INSERT INTO Projects (Project, CreationDate, ProjectName)
OUTPUT inserted.ProjectId INTO #ids;
VALUES (#project, GETDATE(), #email);
INSERT INTO ProjectsUsers (UsersEmail, ProjectId)
SELECT #email, ProjectId
FROM #ids;
All the other methods of returning the identity have peculiarities:
Perhaps they don't work when the insert has multiple statements.
Perhaps concurrent inserts mess up the value.
Perhaps they don't work well with triggers.

Use trigger to act as foreign key across 2 different servers

I'm trying to replicate a foreign key across two servers using trigger. I know using trigger across 2 servers is not the best practice but my company on gives me read-only access to their database which I need to relate to my application.
I have DB1 which is my local database and it is attached to DB2 using linked server. I want trigger to check if a specific ID from a DB2_table on DB2 exists before executing an INSERT on DB1_table where the ID from DB2_table will act as a foreign key.
CREATE TRIGGER trigger_DB1_Table_Insert
#ID
BEFORE INSERT ON DB1_Table
AS
BEGIN
if exists(Select ID from DB2_Table where ID = #ID)
--execute insert
END
GO
I would actually recommend using a check constraint instead of a trigger. Check Constraints are designed to enforce data integrity and are a semantically better option. The example below creates some working tables, then creates a function which will check if the record exists in the other table.
The check constraint then uses the function and returns an error if the value doesn't exist.
CREATE TABLE dbo.OtherTable(
id INT
)
CREATE TABLE dbo.a(
id INT IDENTITY(1,1)
,OtherTableId INT
)
GO
INSERT INTO OtherTable (id) VALUES (1), (2), (3)
GO
-- Function will check if the ID exists in the other table
CREATE FUNCTION dbo.CheckOtherTableId(
#OtherTableId INT
)
RETURNS BIT
AS BEGIN
RETURN
(
SELECT
CASE
WHEN EXISTS
(
SELECT 1
FROM OtherTable
WHERE id = #OtherTableId
)
THEN 1
ELSE 0
END
)
END
GO
-- Add check constraint
ALTER TABLE a WITH CHECK
ADD CONSTRAINT [CK_OtherTable] CHECK (1=dbo.CheckOtherTableId(OtherTableId))
GO
-- Test should work
INSERT INTO a (OtherTableId) values (1)
Go
-- Test should fail
INSERT INTO a (OtherTableId) values (8)
GO

Update identity column sql [duplicate]

I have a MS SQL 2005 database with a table Test with column ID. ID is an identity column.
I have rows in this table and all of them have their corresponding ID auto incremented value.
Now I would like to change every ID in this table like this:
ID = ID + 1
But when I do this I get an error:
Cannot update identity column 'ID'.
I've tried this:
ALTER TABLE Test NOCHECK CONSTRAINT ALL
set identity_insert ID ON
But this does not solve the problem.
I need to have identity set to this column, but I need to change values as well from time to time. So my question is how to accomplish this task.
You need to
set identity_insert YourTable ON
Then delete your row and reinsert it with different identity.
Once you have done the insert don't forget to turn identity_insert off
set identity_insert YourTable OFF
IDENTITY column values are immutable.
However it is possible to switch the table metadata to remove the IDENTITY property, do the update, then switch back.
Assuming the following structure
CREATE TABLE Test
(
ID INT IDENTITY(1,1) PRIMARY KEY,
X VARCHAR(10)
)
INSERT INTO Test
OUTPUT INSERTED.*
SELECT 'Foo' UNION ALL
SELECT 'Bar' UNION ALL
SELECT 'Baz'
Then you can do
/*Define table with same structure but no IDENTITY*/
CREATE TABLE Temp
(
ID INT PRIMARY KEY,
X VARCHAR(10)
)
/*Switch table metadata to new structure*/
ALTER TABLE Test SWITCH TO Temp;
/*Do the update*/
UPDATE Temp SET ID = ID + 1;
/*Switch table metadata back*/
ALTER TABLE Temp SWITCH TO Test;
/*ID values have been updated*/
SELECT *
FROM Test
/*Safety check in case error in preceding step*/
IF NOT EXISTS(SELECT * FROM Temp)
DROP TABLE Temp /*Drop obsolete table*/
In SQL Server 2012 it is possible to have an auto incrementing column that can also be updated more straightforwardly with SEQUENCES
CREATE SEQUENCE Seq
AS INT
START WITH 1
INCREMENT BY 1
CREATE TABLE Test2
(
ID INT DEFAULT NEXT VALUE FOR Seq NOT NULL PRIMARY KEY,
X VARCHAR(10)
)
INSERT INTO Test2(X)
SELECT 'Foo' UNION ALL
SELECT 'Bar' UNION ALL
SELECT 'Baz'
UPDATE Test2 SET ID+=1
Through the UI in SQL Server 2005 manager, change the column remove the autonumber (identity) property of the column (select the table by right clicking on it and choose "Design").
Then run your query:
UPDATE table SET Id = Id + 1
Then go and add the autonumber property back to the column.
Firstly the setting of IDENTITY_INSERT on or off for that matter will not work for what you require (it is used for inserting new values, such as plugging gaps).
Doing the operation through the GUI just creates a temporary table, copies all the data across to a new table without an identity field, and renames the table.
This can be done using a temporary table.
The idea
disable constraints (in case your id is referenced by a foreign key)
create a temp table with the new id
delete the table content
copy back data from the copied table to your original table
enable previsously disabled constraints
SQL Queries
Let's say your test table have two additional columns (column2 and column3) and that there are 2 tables having foreign keys referencing test called foreign_table1 and foreign_table2 (because real life issues are never simple).
alter table test nocheck constraint all;
alter table foreign_table1 nocheck constraint all;
alter table foreign_table2 nocheck constraint all;
set identity_insert test on;
select id + 1 as id, column2, column3 into test_copy from test v;
delete from test;
insert into test(id, column2, column3)
select id, column2, column3 from test_copy
alter table test check constraint all;
alter table foreign_table1 check constraint all;
alter table foreign_table2 check constraint all;
set identity_insert test off;
drop table test_copy;
That's it.
DBCC CHECKIDENT ( ‘databasename.dbo.orders’,RESEED, 999)
you can change any identity column number with this command,and also you can start that field number from every number you want.for example in my command i ask to start from 1000 (999+1)
hope that it would be enough...good luck
If the column is not a PK you could always create a NEW column in the table with the incremented numbers, drop the original and then alter the new one to be the old.
curious as to why you might need to do this... most I've ever had to futz with Identity columns was to backfill numbers and I just ended up using DBCC CHECKIDENT ( tablename,RESEED,newnextnumber)
good luck!
Identity modifying may fail depending on a number of factors, mainly revolving around the objects/relationships linked to the id column. It seems like db design is as issue here as id's should rarely if ever change (i'm sure you have your reasons and are cascasding the changes). If you really need to change id's from time to time, I'd suggest either creating a new dummy id column that isn't the primary key/autonumber that you can manage yourself and generate from the current values. Alternately, Chrisotphers idea above would be my other suggestion if you're having issues with allowing identity insert.
Good luck
PS it's not failing because the sequential order it's running in is trying to update a value in the list to an item that already exists in the list of ids? clutching at straws, perhaps add the number of rows+1, then if that works subtract the number of rows :-S
If you need to change the IDs occasionally, it's probably best not to use an identity column. In the past we've implemented autonumber fields manually using a 'Counters' table that tracks the next ID for each table. IIRC we did this because identity columns were causing database corruption in SQL2000 but being able to change IDs was occasionally useful for testing.
You can insert new rows with modified values and then delete old rows. Following example change ID to be same as foreign key PersonId
SET IDENTITY_INSERT [PersonApiLogin] ON
INSERT INTO [PersonApiLogin](
[Id]
,[PersonId]
,[ApiId]
,[Hash]
,[Password]
,[SoftwareKey]
,[LoggedIn]
,[LastAccess])
SELECT [PersonId]
,[PersonId]
,[ApiId]
,[Hash]
,[Password]
,[SoftwareKey]
,[LoggedIn]
,[LastAccess]
FROM [db304].[dbo].[PersonApiLogin]
GO
DELETE FROM [PersonApiLogin]
WHERE [PersonId] <> ID
GO
SET IDENTITY_INSERT [PersonApiLogin] OFF
GO
First save all IDs and alter them programmatically to the values you wan't, then remove them from database and then insert them again using something similar:
use [Name.Database]
go
set identity_insert [Test] ON
insert into [dbo].[Test]
([Id])
VALUES
(2)
set identity_insert [Test] OFF
For bulk insert use:
use [Name.Database]
go
set identity_insert [Test] ON
BULK INSERT [Test]
FROM 'C:\Users\Oscar\file.csv'
WITH (FIELDTERMINATOR = ';',
ROWTERMINATOR = '\n',
KEEPIDENTITY)
set identity_insert [Test] OFF
Sample data from file.csv:
2;
3;
4;
5;
6;
If you don't set identity_insert to off you will get the following error:
Cannot insert explicit value for identity column in table 'Test' when
IDENTITY_INSERT is set to OFF.
I saw a good article which helped me out at the last moment .. I was trying to insert few rows in a table which had identity column but did it wrongly and have to delete back. Once I deleted the rows then my identity column got changed . I was trying to find an way to update the column which was inserted but - no luck. So, while searching on google found a link ..
Deleted the columns which was wrongly inserted
Use force insert using identity on/off (explained below)
http://beyondrelational.com/modules/2/blogs/28/posts/10337/sql-server-how-do-i-insert-an-explicit-value-into-an-identity-column-how-do-i-update-the-value-of-an.aspx
Very nice question, first we need to on the IDENTITY_INSERT for the specific table, after that run the insert query (Must specify the column name).
Note: After edit the the identity column, don't forget to off the IDENTITY_INSERT. If you not done, you cannot able to Edit the identity column for any other table.
SET IDENTITY_INSERT Emp_tb_gb_Menu ON
INSERT Emp_tb_gb_Menu(MenuID) VALUES (68)
SET IDENTITY_INSERT Emp_tb_gb_Menu OFF
http://allinworld99.blogspot.com/2016/07/how-to-edit-identity-field-in-sql.html

Trigger for insert on identity column

I have a table A with an Identity Column which is the primary key.
The primary key is at the same time a foreign key that points towards another table B.
I am trying to build an insert trigger that inserts into Table B the identity column that is about to be created in table A and another custom value for example '1'.
I tried using ##Identity but I keep getting a foreign key conflict. Thanks for your help.
create TRIGGER dbo.tr ON dbo.TableA FOR INSERT
AS
SET NOCOUNT ON
begin
insert into TableB
select ##identity, 1;
end
alexolb answered the question himself in the comments above. Another alternative is to use the IDENT_CURRENT function instead of selecting from the table. The drawback of this approach is that it always starts your number one higher than the seed, but that is easily remedied by setting the seed one unit lower. I think it feels better to use a function than a subquery.
For example:
CREATE TABLE [tbl_TiggeredTable](
[id] [int] identity(0,1) NOT NULL,
[other] [varchar](max)
)
CREATE TRIGGER [trgMyTrigger]
ON [tbl_TriggeredTable]
INSTEAD OF INSERT,UPDATE,DELETE
SET identity_insert tbl_TriggeredTable ON
INSERT INTO tbl_TriggeredTable (
[id],
[other]
)
SELECT
-- The identity column will have a zero in the insert table when
-- it has not been populated yet, so we need to figure it out manually
case i.[id]
when 0 then IDENT_CURRENT('tbl_TriggeredTable') + IDENT_INCR('tbl_TriggeredTable')
ELSE i.[id]
END,
i.[other],
FROM inserted i
SET identity_insert tbl_TriggeredTable OFF
END

How do I insert into a table and get back the primary key value?

I have a primary key set up to auto increment.
I am doing multiple queries and I need to retrieve that primary key value to use as a foreign key in another table (IsIdentity = TRUE).
Is there any elegant way to get back the primary key value when I do an insert query? Right now I am requerying and getting the highest value in that column which seems really hacky.
Any suggestions?
If you are using SQL Server 2005 or later, you can use the OUTPUT clause.
create table T(
pk int identity primary key,
dat varchar(20)
);
go
insert into T
output inserted.pk
values ('new item');
go
drop table T;
The output can be directed to a table as well as to the client. For example:
create table T(
pk int identity primary key,
dat varchar(20)
);
create table U(
i int identity(1001,1) primary key,
T_pk int not null,
d datetime
);
go
insert into T
output inserted.pk, getdate()
into U(T_pk,d)
values ('new item'), ('newer item');
go
select * from T;
select * from U;
go
drop table T, U;
Beginning with SQL Server 2008, you can use "composable DML" for more possibilities.
insert into YourTable values (...)
get the new PK with scope_identity()
select scope_identity()
INSERT INTO YourTable (1, 2, etc.)
OUTPUT inserted.yourIDcolumn
VALUES (value1, value2, value...)
Note: This is for MS SQL 2005 and greater
SCOPE_IDENTITY() is probably what you want. It returns the ID of the last record inserted by the same code context in which it executes.
IDENT_CURRENT('tablename') is subject to concurrency issues. That is, there's no guarantee that another record won't be inserted between the INSERT and the call to IDENT_CURRENT.
I must confess, I'm not sure to what source of amazement the VillageIdiot's outburst refers, but I myself am quite astonished that this question does not appear to be a duplicate at all.
holy crap!!!
just call SCOPE_IDENTITY() function:
insert into your_talble(col1,col2) values('blah','more blah')
select scope_identity()
because selecting highest value will return error if any other statement make an insert. the function scope_identity() returns the identity created in current context (that is by your statement)
You should use scope_identity(). And I recommend to wrap insert statement and scope_identity() into transaction.