How do I populate the identity column when inserting into a table? - sql

I have a table whose data is inserted by selecting from another table. For example,
CREATE TABLE TestTable (ID int, FirstName VARCHAR(100), LastName VARCHAR(100))
INSERT INTO TestTable (FirstName, LastName)
SELECT FirstName, LastName
FROM Person.Contact
But How do I populate the ID? I tried
INSERT INTO TestTable (SCOPE_IDENTITY()+1, FirstName, LastName)
SELECT FirstName, LastName
FROM Person.Contact
I don't want to make ID as identity column is because I duplicate the table structure from an exsiting one which the ID is regular column.
But it doesn't work. Any idea?

This depends on the exact SQL server you're using, but since it appears you're using Microsoft SQL Server:
Simply labeling your column as ID is not enough. You'll need to make sure that the ID column is marked in SQL Server as an Identity column. This is similar to marking a column SERIAL in PostgreSQL, or AUTOINCREMENT in MySQL. Make sure you've done this first.
Assuming you've done that, simply let the database itself add the identity value by explicitly not referencing that column in your INSERT statement. Thus, something like
INSERT INTO TestTable (FirstName, LastName)
SELECT FirstName, LastName
FROM Person.Contact
and relying on SQL Server's underlying identity support to fill it in for you will work fine. It looks from your example like the thing you're missing is marking the ID column as an identity column in the first place.

They are right in that you should specify a autonumber column. As below:
CREATE TABLE TestTable
(
ID int NOT NULL IDENTITY (1, 1),
Firstname varchar(100) NULL,
Lastname varchar(100) NULL
)
Then when you insert, use the following:
DECLARE #MyTableVar table( ID int);
INSERT INTO TestTable
OUTPUT INSERTED.ID
INTO #MyTableVar
SELECT FirstName, LastName
FROM Person.Contact;
Then you select #MyTableVar for the identities you inserted

Is below what you are looking for?
SELECT
id + 0
,FirstName
,LastName
INTO test_table
From Person.Contact

Related

SQL Inserted table trigger

If I run the following select statement inside an insert trigger, is it possible that it will return more than one result?:
DECLARE #variable char(1) = (SELECT ID FROM inserted)
If so, then what's the best to handle it?
Here is the problem that I am trying to solve: Every time when the new record is inserted into a table, I want to take the newly inserted ID and insert it into another table(if it doesn't exists).
Thank you!
Instead of
DECLARE #variable char(1) = (SELECT ID FROM inserted)
You can do something like following:
Declare #VarTempTbl as table (id int)
Insert into #VarTempTbl (id)
Select id from inserted
So that you can get those values for further processing
Now, I had created Two tables One for Master table and another for When any Insertion happens in that Master table, that entry has to inserted into the another table.
CREATE TABLE tblEmployee
(
Id int Primary Key,
Name nvarchar(30),
Gender nvarchar(10),
DepartmentId int
)
CREATE TABLE tblEmployee_New
(
Id int Primary Key,
Name nvarchar(30),
Gender nvarchar(10),
DepartmentId int
)
Trigger:
CREATE TRIGGER TR_EMPLOYEEDETAILS_AFTEROFINSERT
ON TBLEMPLOYEE
AFTER INSERT
AS
BEGIN
TRUNCATE TABLE tblEmployee_New
INSERT INTO TBLEMPLOYEE_NEW(ID, NAME, GENDER, DEPARTMENTID)
SELECT ID, NAME, GENDER, DEPARTMENTID
FROM INSERTED
END
Now Lets try to insert into record into a master table
Insert into tblEmployee values (1,'John', 'Male', 3)
Insert into tblEmployee values (2,'Mike', 'Male', 2)
It has automatically insert the newly inserted records into the another table.
If your want to remove the Previous records then add a drop Statement in that above Trigger.
Note: You can also use #Temp Table instead of creating a another table('tblEmployee_New')
Kinldy Share your comments

SQL - Need to duplicate rows but with a unique primary key

I need to duplicate some rows in a table but the primary key needs to be unique. When I try to do it this way I get a "Violation of PRIMARY KEY constraint"
INSERT INTO Company.Customer
SELECT CustomerId, FirstName, LastName
FROM Company.Customer
WHERE LastName LIKE '%JONES%';
CustomerId in this example is the primary key. So the values in FirstName and LastName need to stay the same but the CustomerId needs to change
Thanks!
Based on your comments it seems that CustomerId is not IDENTITY column. This makes things trickier, because it is unclear how your primary keys are generated. For simple case you can use such approach:
-- Retrieve maximum value of CustomerId
DECLARE #maxid int = 0
SELECT #maxid = MAX(CustomerId) FROM Company.Customer
-- When inserting data for column CustomerId add maximum id value and row number
-- This should ensure that the key values do not clash
INSERT INTO Company.Customer (CustomerId, FirstName, LastName)
SELECT ROW_NUMBER() OVER (ORDER BY CustomerId ASC) + #maxid, FirstName, LastName
FROM Company.Customer
WHERE LastName LIKE '%JONES%';
However, if you don't have a reason not to, I suggest using IDENTITY column - It will make things easier to handle.
Leave the primary key "CustomerId" out of select portion of your query... It should be auto generated for you on the inserted rows.
INSERT INTO Customer (FirstName, LastName)
(SELECT FirstName, LastName
FROM Customer
WHERE LastName LIKE '%JONES%')

Insert data in 3 tables at a time using Postgres

I want to insert data into 3 tables with a single query.
My tables looks like below:
CREATE TABLE sample (
id bigserial PRIMARY KEY,
lastname varchar(20),
firstname varchar(20)
);
CREATE TABLE sample1(
user_id bigserial PRIMARY KEY,
sample_id bigint REFERENCES sample,
adddetails varchar(20)
);
CREATE TABLE sample2(
id bigserial PRIMARY KEY,
user_id bigint REFERENCES sample1,
value varchar(10)
);
I will get a key in return for every insertion and I need to insert that key in the next table.
My query is:
insert into sample(firstname,lastname) values('fai55','shaggk') RETURNING id;
insert into sample1(sample_id, adddetails) values($id,'ss') RETURNING user_id;
insert into sample2(user_id, value) values($id,'ss') RETURNING id;
But if I run single queries they just return values to me and I cannot reuse them in the next query immediately.
How to achieve this?
Use data-modifying CTEs:
WITH ins1 AS (
INSERT INTO sample(firstname, lastname)
VALUES ('fai55', 'shaggk')
-- ON CONFLICT DO NOTHING -- optional addition in Postgres 9.5+
RETURNING id AS sample_id
)
, ins2 AS (
INSERT INTO sample1 (sample_id, adddetails)
SELECT sample_id, 'ss' FROM ins1
RETURNING user_id
)
INSERT INTO sample2 (user_id, value)
SELECT user_id, 'ss2' FROM ins2;
Each INSERT depends on the one before. SELECT instead of VALUES makes sure nothing is inserted in subsidiary tables if no row is returned from a previous INSERT. (Since Postgres 9.5+ you might add an ON CONFLICT.)
It's also a bit shorter and faster this way.
Typically, it's more convenient to provide complete data rows in one place:
WITH data(firstname, lastname, adddetails, value) AS (
VALUES -- provide data here
('fai55', 'shaggk', 'ss', 'ss2') -- see below
, ('fai56', 'XXaggk', 'xx', 'xx2') -- works for multiple input rows
-- more?
)
, ins1 AS (
INSERT INTO sample (firstname, lastname)
SELECT firstname, lastname -- DISTINCT? see below
FROM data
-- ON CONFLICT DO NOTHING -- UNIQUE constraint? see below
RETURNING firstname, lastname, id AS sample_id
)
, ins2 AS (
INSERT INTO sample1 (sample_id, adddetails)
SELECT ins1.sample_id, d.adddetails
FROM data d
JOIN ins1 USING (firstname, lastname)
RETURNING sample_id, user_id
)
INSERT INTO sample2 (user_id, value)
SELECT ins2.user_id, d.value
FROM data d
JOIN ins1 USING (firstname, lastname)
JOIN ins2 USING (sample_id);
db<>fiddle here
You may need explicit type casts in a stand-alone VALUES expression - as opposed to a VALUES expression attached to an INSERT where data types are derived from the target table. See:
Casting NULL type when updating multiple rows
If multiple rows can come with identical (firstname, lastname), you may need to fold duplicates for the first INSERT:
...
INSERT INTO sample (firstname, lastname)
SELECT DISTINCT firstname, lastname FROM data
...
You could use a (temporary) table as data source instead of the CTE data.
It would probably make sense to combine this with a UNIQUE constraint on (firstname, lastname) in the table and an ON CONFLICT clause in the query.
Related:
How to use RETURNING with ON CONFLICT in PostgreSQL?
Is SELECT or INSERT in a function prone to race conditions?
Something like this
with first_insert as (
insert into sample(firstname,lastname)
values('fai55','shaggk')
RETURNING id
),
second_insert as (
insert into sample1( id ,adddetails)
values
( (select id from first_insert), 'ss')
RETURNING user_id
)
insert into sample2 ( id ,adddetails)
values
( (select user_id from first_insert), 'ss');
As the generated id from the insert into sample2 is not needed, I removed the returning clause from the last insert.
Typically, you'd use a transaction to avoid writing complicated queries.
http://www.postgresql.org/docs/current/static/sql-begin.html
http://dev.mysql.com/doc/refman/5.7/en/commit.html
You could also use a CTE, assuming your Postgres tag is correct. For instance:
with sample_ids as (
insert into sample(firstname, lastname)
values('fai55','shaggk')
RETURNING id
), sample1_ids as (
insert into sample1(id, adddetails)
select id,'ss'
from sample_ids
RETURNING id, user_id
)
insert into sample2(id, user_id, value)
select id, user_id, 'val'
from sample1_ids
RETURNING id, user_id;
You could create an after insert trigger on the Sample table to insert into the other two tables.
The only issue i see with doing this is that you wont have a way of inserting adddetails it will always be empty or in this case ss. There is no way to insert a column into sample thats not actualy in the sample table so you cant send it along with the innital insert.
Another option would be to create a stored procedure to run your inserts.
You have the question taged mysql and postgressql which database are we talking about here?

versioning of a table

anybody has seen any examples of a table with multiple versions for each record
something like if you would had the table
Person(Id, FirstName, LastName)
and you change a record's LastName than you would have both versions of LastName (first one, and the one after the change)
I've seen this done two ways. The first is in the table itself by adding an EffectiveDate and CancelDate (or somesuch). To get the current for a given record, you'd do something like: SELECT Id, FirstName, LastName FROM Table WHERE CancelDate IS NULL
The other is to have a global history table (which holds all of your historical data). The structure for such a table normally looks something like
Id bigint not null,
TableName nvarchar(50),
ColumnName nvarchar(50),
PKColumnName nvarchar(50),
PKValue bigint, //or whatever datatype
OriginalValue nvarchar(max),
NewValue nvarchar(max),
ChangeDate datetime
Then you set a trigger on your tables (or, alternatively, add a policy that all of your Updates/Inserts will also insert into your HX table) so that the correct data is logged.
The way we're doing it (might not be the best way) is to have an active bit field, and a foreign key back to the parent record. So for general queries you would filter on active employees, but you can get the history of a single employee with their Employee ID.
declare #employees
(
PK_emID int identity(1,1),
EmployeeID int,
FirstName varchar(50),
LastName varchar(50),
Active bit,
FK_EmployeeID int
primary key(PK_emID)
)
insert into #employees
(
EmployeeID,
FirstName,
LastName,
Active,
FK_EployeeID
)
select 1, 'David', 'Engle', 1,null
union all
select 2, 'Amy', 'Edge', 0,null
union all
select 2, 'Amy','Engle',1,2

How to insert sequential numbers in primary key using select subquery?

I am reading a table A and inserting the date in Table B (both tables are of same structure except primary key data type). In Table B, Primary key is int whereas in Table A it is UniqueIdentifier.
INSERT INTO TableB
(ID, Names, Address)
(select ID, Names, Address from TableA)
Now how can i insert int type incremental value (1,2,3,so on) in TableB instead of uniqueidentifier from TableA using above script.
Help?
Why not change Table B so that the primary key is an identity which auto-increments?
Go to the table properties, select the ID field, under "Identity specification", set "Identity Increment" = 1, "Identity Seed" = 1. By doing that, the ID becomes auto incremental...
Then your insert statement would be something like:
INSERT INTO TableB (Names, Address) (select Names, Address from TableA)
If changing the schema of your TableB is not an option then add a rank to your select statement like this:
insert into tableB select rank() over(order by id), name, address from tableA
This will always start at 1. I you could add + 10 if you wanted to start your numbering at a number other than 1. I'm sure you get the idea from there.
CREATE TABLE TableB
(
ID int PRIMARY KEY IDENTITY(1,1),
Name nvarchar(200),
Address nvarchar(200)
)
Then, in the query, don't specify the value of the identity column.
INSERT INTO TableB(Name, Address)
SELECT Name, Address FROM TableA