Generate ID for duplicate values in sql server - sql

I found following link to assign identical ID to duplicates in SQL server,
my understanding there is no sql server function to automatically generate it rather than using insert and update queries in link attached, is that statement True, if yes, then what would be the trigger if for example someone insert data to MyTable then run insert and update query from link:
Assign identical ID to duplicates in SQL server
INSERT INTO secondTable (word) SELECT distinct word FROM MyTable;
UPDATE MyTable SET ID = (SELECT id from secondTable where MyTable.word = secondTable.word)
thanks,
S

Is this what you want? I can't think of an "automatic" solution that would just increase the Id for new words.
CREATE TABLE MyTable (
Id INT NOT NULL,
Word NVARCHAR(255) NOT NULL
PRIMARY KEY (Id, Word)); -- primary key will make it impossible to have more than one combination of word and id
DECLARE #word NVARCHAR(255) = 'Hello!';
-- Get existing id or calculate a new id
DECLARE #Id INT = (SELECT Id FROM MyTable WHERE Word = #word);
IF(#id IS NULL) SET #Id = (SELECT MAX(Id) + 1 FROM MyTable);
INSERT INTO MyTable (Id, Word)
VALUES (#id, #word)
SELECT * FROM MyTable
If you can't for some reason have id and word as a combined primary key, you may use an unique index to make sure that there is only one combination

Related

Dynamically created Tables JOIN in SQL Server

I have 3 types of tables
Major table as follows
CREATE TABLE #InitialTable
(
Id int PRIMARY KEY IDENTITY(1,1),
RP varchar(20)
)
INSERT INTO #InitialTable
VALUES ('R1', 'R2', 'R3')
GO
Table contains dynamically created tables information as follows
CREATE TABLE #DynamicTablesInfo
(
Id int PRIMARY KEY IDENTITY(1,1),
RPId int FOREIGN KEY REFERENCES #InitialTable(Id),
TableName varchar(100)
)
GO
INSERT INTO #DynamicTablesInfo
VALUES (1, 'Table_X1'), (2, 'Table_X2'), (3, 'Table_X3')
GO
Dynamically created tables these tables can be any number of tables and the tables info is available in above table.
CREATE TABLE #Table_X1
(
Id int PRIMARY KEY IDENTITY,
Version_Value varchar(100)
)
GO
INSERT INTO #Table_X1
VALUES ('Val_X1_1'), ('Val_X1_2'), ('Val_X1_3')
GO
CREATE TABLE #Table_X2
(
Id int PRIMARY KEY IDENTITY,
Version_Value varchar(100)
)
GO
INSERT INTO #Table_X2
VALUES ('Val_X2_1'), ('Val_X2_2'), ('Val_X2_3')
GO
CREATE TABLE #Table_X3
(
Id int PRIMARY KEY IDENTITY,
Version_Value varchar(100)
)
GO
INSERT INTO #Table_X3
VALUES ('Val_X3_1'), ('Val_X3_2'), ('Val_X3_3')
GO
Now I wanted to join InitialTable with dynamically created tables (Table_X1, Table_X2, Table_X3,....) with the help of DynamicTablesInfo table - how to do that?
Note: for easy update, delete, insert I created them as temporary tables but in my application all are real tables.
Instead of doing this:
CREATE TABLE #Table_X1
(
Id int PRIMARY KEY IDENTITY
,Version_Value varchar(100)
)
GO
CREATE TABLE #Table_X2
(
Id int PRIMARY KEY IDENTITY
,Version_Value varchar(100)
)
GO
CREATE TABLE #Table_X3
(
Id int PRIMARY KEY IDENTITY
,Version_Value varchar(100)
)
GO
Do this one time:
CREATE TABLE Table_X
(
Id int PRIMARY KEY IDENTITY
,Version_Value varchar(100)
,X_number INT
)
GO
Then instead of doing this:
INSERT INTO #Table_X1 VALUES ('Val_X1_1'),('Val_X1_2'),('Val_X1_3')
INSERT INTO #Table_X2 VALUES ('Val_X2_1'),('Val_X2_2'),('Val_X2_3')
INSERT INTO #Table_X3 VALUES ('Val_X3_1'),('Val_X3_2'),('Val_X3_3')
Do this:
INSERT INTO Table_X VALUES ('Val_X1_1',1),('Val_X1_2',1),('Val_X1_3',1)
INSERT INTO Table_X VALUES ('Val_X2_1',2),('Val_X2_2',2),('Val_X2_3',2)
INSERT INTO Table_X VALUES ('Val_X3_1',3),('Val_X3_2',3),('Val_X3_3',3)
Much easier to query without dynamics:
--no
SELECT * FROM Table_X1
--yes
SELECY * FROM Table_X WHERE X_Number = 1
You've indicated you're stuck with it how it is, so you'll need to create and run your queries dynamically too. This is c#/vb flavored pseudocode:
string sql = "SELECT * FROM sometable"
for int x = 1 to 3
sql = sql + " table_x{x} on sometable.id = table_x{x}.id"
Or perhaps build a Union:
string sql = "WITH allx AS (SELECT * FROM table_x1"
for int x = 2 to 10
sql = sql + " UNION ALL SELECT * FROM table_x{x}"
sql = sql + ") select * from sometable inner join allx on..."
But I echo larnu's sentiments in the comments.. if you truly cannot change the tables that are created, consider creating a VIEW in a similar way to the UNION code above, that will sit alongside X number of tables and will provide a way to query without dynamic:
CREATE VIEW AllX AS(
SELECT x.*, 1 as Which FROM TABLE_X1 x
UNION ALL SELECT x.*, 2 as Which FROM TABLE_X2 x
UNION ALL SELECT x.*, 3 as Which FROM TABLE_X3 x
...
Use the same technique that creates 10 tables to string together a CREATE VIEW statement that views over the 10 tables, then you can query the view without Dynamic sql generation

Merge with primary key violation

I have a file based import thingy where the users can post files to be imported in the database. New records are inserted and records with an already existing Id are updated.
After posting a file with
ID NAME
5 Silly
they can correct this by posting a new file with
ID NAME
5 Sally
I have a bulk insert (C# windows service) of the file into a bulk table (Sql Server Azure v12). The files can contain millions of rows so I'd like to avoid iterating through rows. After the bulk insert i have a SP that does a merge update / insert and updates already existing rows and inserts new ones.
The problem I've come across is when the users post a new record and a correction of the same record in the same file. I get a PRIMARY KEY VIOLATION on the target table.
Is there a nice way to solve this?
Here's an example:
--drop table #bulk
--drop table #target
create table #bulk(
id int,
name varchar(10)
)
insert into #bulk values (1,'John')
insert into #bulk values (2,'Sally')
insert into #bulk values (3,'Paul')
insert into #bulk values (4,'Gretchen')
insert into #bulk values (5,'Penny')
insert into #bulk values (5,'Peggy')
create table #target(
id int not null,
name varchar(10),
primary key (id))
merge #target as target
using(select id, name from #bulk) as bulktable
on target.id = bulktable.id
when matched then update
set target.name = bulktable.name
when not matched then
insert(id, name) values (bulktable.id, bulktable.name);
This will handle the latest value for name.
You need a new create script for #bulk
CREATE TABLE #bulk
(
row_id int identity(1,1),
id int,
name varchar(10)
)
This is the script you can use with the new bulk table:
;WITH CTE as
(
SELECT
id, name,
row_number() over (partition by id order by row_id desc) rn
FROM #bulk
), CTE2 as
(
SELECT id, name
FROM CTE
WHERE rn = 1
)
MERGE #target as target
USING CTE2 as bulktable
on target.id = bulktable.id
WHEN matched and
not exists(SELECT target.name except SELECT bulktable.name)
-- this will handle null values. Otherwise it could simply have been:
-- matched and target.name <> bulktable.name
THEN update
SET target.name = bulktable.name
WHEN not matched THEN
INSERT(id, name) VALUES (bulktable.id, bulktable.name);

How to capture the unique identifier after the insert [duplicate]

This question already has answers here:
How do I return a new IDENTITY column value from an SQLServer SELECT statement?
(7 answers)
Closed 8 years ago.
I have to create new email records in an existing table in the joining table I would like to update a field that would denote that this is a new record.
Example:
INSERT INTO dbo.email.email (dbo.email.eml_address, dbo.email.eml_customer_key)
SELECT new_email, new_customer_key
FROM NEW_TABLE
Update dbo.email_ext
Set dbo.email_ext.new_eml = '1'
Where dbo.email_ext.eml_key_ext = 'Recently create key from insert statement shown above'
You'll want to use the SCOPE_IDENTITY() value, this will contain the ID of the record just created, but only one.
Assuming you're handling ONE record:
DECLARE #ID INT
INSERT INTO dbo.email (eml_address, eml_customer_key)
SELECT new_email, new_customer_key
FROM NEW_TABLE
SET #ID = SCOPE_IDENTITY()
Update dbo.email_ext
Set new_eml = '1'
Where eml_key_ext = #ID
If you're inserting multiples you need to output the list into a table (in this case a table variable) and you can update them all at once.
DECLARE #myIDs TABLE (NEWID INT)
INSERT INTO dbo.email (eml_address, eml_customer_key)
OUTPUT inserted.ID INTO #myIDs
SELECT new_email, new_customer_key
FROM NEW_TABLE
Update t
Set new_eml = '1'
from dbo.email_ext t
join #myIDs m
on t.eml_key_ext = m.ID
Use an OUTPUT clause to capture autogenerated ids/guids/defaults/etc.
CREATE TABLE #test (
id int identity(1,1) primary key,
uid uniqueidentifier DEFAULT NEWID(),
value varchar(max)
)
INSERT #test (value)
OUTPUT inserted.*
SELECT 'test'
id guid value
----------- ------------------------------------ ---------
1 72B70577-2679-4C2A-A575-62D30807B9D2 test
(1 row(s) affected)

Insert New data into multiple tables with Foreign Key simultaneously in One query in sql server

I have two tables:
tblDepartment:
Id Name
and
tblEmployee:
Id FullName Dept_ID
Dept_ID is a foreign key for tblDepartment
I want to insert a new record into both tables.
I tried this:
declare #id int
insert dbo.tblDepartment
select Name='Name1'
select id = scope_Identity()
insert dbo.tblEmployee
select FullName = 'Name1'
select Dept_Id=#id
select Id=#id
However, this is not working. I searched through other posts, but they contain solutions for inserting existing data from one table into another, not creating a new record. How can I do this in one query?
You need to use variables properly along with column lists for inserts. Assuming you are using SQL Server:
declare #id int ;
insert dbo.tblDepartment(Name)
select 'Name1';
select #id = scope_Identity();
insert dbo.tblEmployee(FullName, Dept_Id)
select 'Name1', #id;
Also, scope_identity() is okay for learning about such id's. The safe way to really get this information is to use the output clause.
declare #id int
insert dbo.tblDepartment(Name)
select 'Name1'
-- Don't insert any other statements before next line...
select #id=scope_Identity()
insert dbo.tblEmployee(Fullname, Dept_ID)
select 'Name1', #id

Use autogenerated column to populate another column

How can I use an auto-generated column to populate another column during an INSERT statement?
Long story short: we are reusing a database table and an related ASP page to display completely different data than was originally intended.
I have a table similar in structure to the following. It's structure is out of my control.
ID int NON-NULL, IDENTITY(1,1)
OrderNo varchar(50) NON-NULL, UNIQUE
More ...
The table has been repurposed and we are not using the OrderNo column. However, it's NON-NULL and UNIQUE. As dummy data, I want to populate it with the row's ID column.
I have the following SQL so far, but can't work out how to use the row's generated ID.
INSERT INTO MyTable (OrderNo, More)
OUTPUT INSERTED.ID
VALUES (CAST(ID AS varchar(50)))
This just gives:
Msg 207, Level 16, State 1, Line 3
Invalid column name 'ID'.
Here's a solution using the OUTPUT clause. Unfortunately, you won't be able to do it in a single statement.
CREATE TABLE Orders (
ID int not null identity(1,1),
OrderNo varchar(50) not null unique
)
CREATE TABLE #NewIDs ( ID int )
INSERT Orders (OrderNo)
OUTPUT INSERTED.ID INTO #NewIDs
SELECT 12345
UPDATE o
SET o.OrderNo = i.ID
FROM Orders o
JOIN #NewIDs i
ON i.ID = o.ID
SELECT * FROM Orders
One option would be:
create trigger YourTable_Trigger
on YourTable
INSTEAD OF INSERT
as begin
INSERT INTO YourTable (OrderNo, AnotherField)
SELECT 0, AnotherField FROM Inserted
UPDATE YourTable SET OrderNo = SCOPE_IDENTITY() WHERE ID = SCOPE_IDENTITY()
end;
And here is the Fiddle.
Good luck.