Multiple table Set-based insert with identity column in table1 and foreign key in table2 [duplicate] - sql

This question already has an answer here:
T-SQL - Insert Data into Parent and Child Tables
(1 answer)
Closed 3 years ago.
I am writing an Application that imports configuration data from multiple external systems. To decouple the user-data from imported data y am using an intermediate table. The following tables are simplified for the sake of the argument.
table [connection] (generic connection information referenced by other tables not included)
table [importConfig] (Connection configuration from one of the external systems)
table [connectionImportConfig] (intermediate table, linking [connection] and [importConfig]
For each connection in [importConfig] I want to create a row in [connection] with an identity column. Then I want to insert the new IDs from [connection] together with the identifier from [importConfig] in [connectionImportConfig].
Constraint: I don't want to use a cursor. Must be a set-based solution.
The examples I have found on stackoverflow are either not set-based or I found them not applicable for other reasons.
T-SQL Insert into multiple linked tables using a condition and without using a cursor (not valid for multiple rows, using ##IDENTITY)
How to perform INSERT/UPDATE to Linking (Join) table which has FKs to IDENTITY PKs (not valid for multiple rows. Not telling how to join the identity values with the original data I want to insert)
I have tried a lot and am stuck at the point where I have to insert the new IDs into connectionImportConfig
-- Test tables&data (stripped down version)
CREATE TABLE connection (ConnectionID int identity(1,1) NOT NULL, comment nvarchar(max) null)
CREATE TABLE connectionImportConfig(connectionID int NOT NULL, ConfigCode nvarchar(50) NOT NULL)
CREATE TABLE importConfig (ConfigCode nvarchar(50) NOT NULL)
DECLARE #MyConnection table (ConnectionID int not null);
insert into importConfig values ('a')
insert into importConfig values ('b')
insert into importConfig values ('c')
-- Insert into PK-table creating the IDs
INSERT INTO connection (comment)
OUTPUT INSERTED.ConnectionID INTO #MyConnection
SELECT * from importConfig
-- How do I insert the new IDs together with the ConfigCode? 1 has to be replaced with the ID.
-- JOIN doesn't seem to work because there is no join condition to use
INSERT INTO connectionImportConfig (connectionID, ConfigCode)
SELECT 1, ConfigCode FROM ImportConfig
select * from #MyConnection
select * from connectionImportConfig
-- Cleanup
DROP TABLE importConfig;
DROP TABLE connection;
DROP table connectionImportConfig;

Have a look at this. I am sure you could loosely modify this to your needs. I don't typically like inserting right off the output but it really depends on your data. Hope this example helps.
IF OBJECT_ID('tempdb..#ImportConfig') IS NOT NULL DROP TABLE #ImportConfig
IF OBJECT_ID('tempdb..#Config') IS NOT NULL DROP TABLE #Config
IF OBJECT_ID('tempdb..#Connection') IS NOT NULL DROP TABLE #Connection
GO
CREATE TABLE #ImportConfig (ImportConfigID INT PRIMARY KEY IDENTITY(1000,1), ImportConfigMeta VARCHAR(25))
CREATE TABLE #Config (ConfigID INT PRIMARY KEY IDENTITY(2000,1), ImportConfigID INT, ConfigMeta VARCHAR(25))
CREATE TABLE #Connection (ConnectionID INT PRIMARY KEY IDENTITY(3000,1), ConfigID INT, ConnectionString VARCHAR(50))
INSERT INTO #ImportConfig (ImportConfigMeta) VALUES
('IMPORT_ConfigMeta1'),('IMPORT_ConfigMeta2')
;MERGE
INTO #Config AS T
USING #ImportConfig AS S
ON T.ConfigID = S.ImportConfigID
WHEN NOT MATCHED THEN
INSERT (ImportConfigID, ConfigMeta) VALUES (
S.ImportConfigID,
REPLACE(S.ImportConfigMeta,'IMPORT_','')
)
OUTPUT INSERTED.ConfigID, 'CONNECTION_STRING: ' + INSERTED.ConfigMeta INTO #Connection;
SELECT 'IMPORT CONFIG' AS TableName, * FROM #ImportConfig
SELECT 'CONFIG' AS TableName, * FROM #Config
SELECT 'CONNECTION' AS TableName, * FROM #Connection

Related

use INSERT inside definition of VIEW: CREATE VIEW AS INSERT INTO

If I want to do something relatively complicated - something usually done by a stored procedure. Is it possible to make it automatic using a VIEW?
My specific case:
I want output table = input table A + some rows input table B. In a stored procedure, I can make a copy of table A and then INSERT INTO it, but it's not allowed in a view.
Simplified example:
input table is [test_album], and output table = input table + singer Prince.
--create test data
IF OBJECT_ID('[dbo].[test_album]', 'U') IS NOT NULL
DROP TABLE [dbo].[test_album]
CREATE TABLE [test_album] (
id int not null identity(1, 1) primary key,
singer VARCHAR(50) NULL,
album_title VARCHAR(100) NULL
)
INSERT INTO [test_album] (singer, album_title)
VALUES ('Adale', '19'),
('Michael Jaskson', 'Thriller')
--this can be executed as sql code or in stored proc
SELECT *
INTO [result_table]
FROM [test_album]
INSERT INTO [result_table] ([singer])
VALUES ('Prince')
select *
from [result_table]
--id singer album_title
--1 Adale 19
--2 Michael Jaskson Thriller
--3 Prince NULL
----as expected
But I can do this INSERT INTO inside a view.
Real-life case:
additional singers are in a table [extra_singers]
[test_album] may have many other columns (or schema may change) so it's ideal not to type all column names in the code.
--create test data
IF OBJECT_ID('[dbo].[test_album]', 'U') IS NOT NULL
DROP TABLE [dbo].[test_album]
IF OBJECT_ID('[dbo].[extra_singers]', 'U') IS NOT NULL
DROP TABLE [dbo].[extra_singers]
IF OBJECT_ID('[dbo].[result_table]', 'U') IS NOT NULL
DROP TABLE [dbo].[result_table]
CREATE TABLE [test_album] (
id int not null identity(1, 1) primary key,
singer VARCHAR(50) NULL,
album_title VARCHAR(100) NULL,
many_other_columns VARCHAR(100) NULL
)
INSERT INTO [test_album] (singer, album_title)
VALUES ('Adale', '19'),
('Michael Jaskson', 'Thriller')
CREATE TABLE [extra_singers] (
[id] int not null identity(1, 1) primary key,
[name] VARCHAR(50) NULL )
INSERT INTO [extra_singers] ([name])
VALUES ('Prince'),
('Taylor Swift')
--append [extra_singers] to [test_album]
--this can be executed as sql code or in stored proc
SELECT *
INTO [result_table]
FROM [test_album]
INSERT INTO [result_table] ([singer])
SELECT [name]
FROM [extra_singers]
Is there an alternative to this (that is automatic)?
any help's appreciated. Thank u-
a partial solution I can think of:
create view test_view as
select *
from [test_album]
union all
select 3 as id,
'Prince' as singer,
NULL as album_title
but you have to know all the column names in [test_album] and you can't let column [id] do auto-increment
So you may be misunderstanding what a view does, or what an insert is. A view is simply a wrapper around a single select query. It contains exactly one select statement, and nothing else. An insert permanently adds a row of data to a persisted table. The example you gave where you just union the row you want seems valid enough. And certainly if it's the same row you want every time, you would not want to be inserting (or even trying to insert) that row into the underlying table each time
This raises a couple questions though.
If you're always going to be unioning the same single row every time, why not jut add that row to the table?
If, lets say, you don't want that row in the underlying table, cool. But if it's always the same static values, why do you need to include it in the view? Can't it just be assumed it's there?
If it can't be assume to always be the same, you certainly don't want to be changing the VIEW body every time you need it to change. So if it is going to change and you don't want to insert it into the base table, maybe make a second table containing the values you want appended to the base table in the view. Then union the base table and the "extra values" table together instead of a single, hard coded row constructor.

Convert an existing Column to Identity

I have a table in SQL Server with bundle of records. I want to convert the ID column which is Primary Key to an identity Column without loss of data. I thought of the following two approaches:
Create a new table with identity & drop the existing table.
Create a new column with identity & drop the existing column.
but it's clear that they can not be implemented because keeping records is my first priority.
Is there another way to do this?
This solution violates your point 2, but there is no other way and I think your aim is to keep the old values, because nothing else makes sense...
You could do the following:
make it possible to insert into identity columns in your table:
set identity_insert YourTable ON
add a new ID column to your table with identity and insert the values from your old columns
turn identity insert off
set identity_insert YourTable OFF
delete old ID column
rename new column to old name
make it to the primary key
The only problem could be that you have your ID column already connected as foreign key to other tables. Then you have a problem with deleting the old column...
In this case you have to drop the foreign key constraints on your ID column after step 3, then do step 4 to 6 and then recreate your foreign key constraints.
As you are using SQL Server 2012, another possible alternative could be to create a sequence object that has a starting value of the highest ID +1 already in your table, then create a default constraint for your column using GET NEXT VALUE FOR and reference your sequence object you just created.
If you have direct access to the Server Database, just go into the design of the table, select the PK column, and change the identity to "Yes". Make sure you set your seed to the max value of that column. The increment is 1 by default. Save the table design and you should be good to go.
Considering the source table isn't too big:
Create new table (with IDENTITY)
Populate new table from existing table (with IDENTITY_INSERT ON)
Drop old table (drop any existing FKs first)
Rename new table to old name (re-establish FKs if needed)
-- Create Sample Existing Table
DROP TABLE IF EXISTS #tblTest
CREATE TABLE #tblTest
(
ID INT NOT NULL
, Val VARCHAR(10) NOT NULL
)
INSERT INTO #tblTest
(
ID
, Val
)
VALUES
(1, 'a')
, (2, 'b')
, (4, 'c')
GO
-- Create and Populate New Table (with IDENTITY_INSERT ON)
DROP TABLE IF EXISTS #tblTestNew
CREATE TABLE #tblTestNew
(
ID INT IDENTITY(1, 1) NOT NULL
, Val VARCHAR(10) NOT NULL
)
SET IDENTITY_INSERT #tblTestNew ON
INSERT INTO #tblTestNew
(
ID
, Val
)
(
SELECT
#tblTest.ID
, #tblTest.Val
FROM
#tblTest
)
SET IDENTITY_INSERT #tblTestNew OFF
GO
-- Rename Existing Table to Old (can use sp_rename instead, but I can't for temp tables)
SELECT * INTO #tblTestOld FROM #tblTest
DROP TABLE #tblTest
GO
-- Rename New Table to Existing (can use sp_rename instead, but I can't for temp tables)
SELECT * INTO #tblTest FROM #tblTestNew
DROP TABLE #tblTestNew
GO
-- Test Inserting new record
INSERT INTO #tblTest (Val)
VALUES ('d')
-- Verify Results
SELECT * FROM #tblTest
EXEC tempdb.sys.sp_help #objname = N'#tblTest'
-- Drop 'Old' Table (when ready)
DROP TABLE IF EXISTS #tblTestOld
-- Cleanup
DROP TABLE IF EXISTS #tblTest
DROP TABLE IF EXISTS #tblTestNew
DROP TABLE IF EXISTS #tblTestOld
If the table is very large, consider the log growth, Recovery Model, possible single-user mode, etc.
create table t1 (col1 int, col2 varchar(10))
insert into t1 values (10, 'olddata')
--add identity col
alter table t1 add col3 int identity(1,1)
GO
--rename or remove old column
alter table t1 drop column col1
--rename new col to old col name
exec sp_rename 't1.col3', 'col1', 'column'
GO
--add new test , review table
insert into t1 values ( 'newdata')
select * from t1

Insert and alter in one statement

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

Bulk adding records to one table and using their auto-ids in a relation table

I'm building an application that generates forms, however since these forms can have 100's of fields on them I would like to create them in a few queries as possible. I have no issues against using stored procedures or functions.
Forms
id - auto_inc
...
Fields
id - auto_inc
...
FormFields
formID - foreign key
fieldID - foreign key
When I create a form I run
INSERT INTO forms (xx) VALUES ('xx')
and then use
SELECT SCOPE_IDENTITY()
to get the ID from the form
Next I would like to add all the fields and their formfields relation in one or two queries.
INSERT INTO fields (xx, xx) VALUES ('xx', 'xx'), ('xx', 'xx'), ('xx', 'xx')...
Insert INTO formfields (formID, fieldID)
VALUES (#FORM_ID, #fieldID1), (#FORM_ID #fieldID2)...
I'm using php to generate these queries dynamically so #FORM_ID would actually be a php variable.
If I understand correctly you can use a combination of SCOPE_IDENTITY() and OUTPUT, here's an example
CREATE TABLE Foo (
PK INT IDENTITY(1,1) PRIMARY KEY,
I CHAR(1)
)
CREATE TABLE Bar (
PK INT IDENTITY(1,1) PRIMARY KEY,
J INT
)
CREATE TABLE Map (
FooPK INT,
BarPK INT
)
GO
INSERT INTO Foo(I) VALUES ('A')
DECLARE #FooPK INT = SCOPE_IDENTITY()
DECLARE #temp TABLE (BarPK INT)
INSERT INTO Bar(J)
OUTPUT INSERTED.PK INTO #temp
VALUES (4), (5),(6),(7)
INSERT INTO Map(FooPK, BarPK)
SELECT #FooPK, BarPK
FROM #temp
SELECT * FROM Foo
SELECT * FROM Bar
SELECT * FROM Map
GO
DROP TABLE Foo
DROP TABLE Bar
DROP TABLE Map
GO

How to add data to two tables linked via a foreign key?

If I were to have 2 tables, call them TableA and TableB. TableB contains a foreign key which refers to TableA. I now need to add data to both TableA and TableB for a given scenario. To do this I first have to insert data in TableA then find and retrieve TableA's last inserted primary key and use it as the foreign key value in TableB. I then insert values in TableB. This seems lika a bit to much of work just to insert 1 set of data. How else can I achieve this? If possible please provide me with SQL statements for SQL Server 2005.
That sounds about right. Note that you can use SCOPE_IDENTITY() on a per-row basis, or you can do set-based operations if you use the INSERT/OUTPUT syntax, and then join the the set of output from the first insert - for example, here we only have 1 INSERT (each) into the "real" tables:
/*DROP TABLE STAGE_A
DROP TABLE STAGE_B
DROP TABLE B
DROP TABLE A*/
SET NOCOUNT ON
CREATE TABLE STAGE_A (
CustomerKey varchar(10),
Name varchar(100))
CREATE TABLE STAGE_B (
CustomerKey varchar(10),
OrderNumber varchar(100))
CREATE TABLE A (
Id int NOT NULL IDENTITY(51,1) PRIMARY KEY,
CustomerKey varchar(10),
Name varchar(100))
CREATE TABLE B (
Id int NOT NULL IDENTITY(1123,1) PRIMARY KEY,
CustomerId int,
OrderNumber varchar(100))
ALTER TABLE B ADD FOREIGN KEY (CustomerId) REFERENCES A(Id);
INSERT STAGE_A VALUES ('foo', 'Foo Corp')
INSERT STAGE_A VALUES ('bar', 'Bar Industries')
INSERT STAGE_B VALUES ('foo', '12345')
INSERT STAGE_B VALUES ('foo', '23456')
INSERT STAGE_B VALUES ('bar', '34567')
DECLARE #CustMap TABLE (CustomerKey varchar(10), Id int NOT NULL)
INSERT A (CustomerKey, Name)
OUTPUT INSERTED.CustomerKey,INSERTED.Id INTO #CustMap
SELECT CustomerKey, Name
FROM STAGE_A
INSERT B (CustomerId, OrderNumber)
SELECT map.Id, b.OrderNumber
FROM STAGE_B b
INNER JOIN #CustMap map ON map.CustomerKey = b.CustomerKey
SELECT * FROM A
SELECT * FROM B
If you work directly with SQL you have the right solution.
In case you're performing the insert from code, you may have higher level structures that help you achieve this (LINQ, Django Models, etc).
If you are going to do this in direct SQL, I suggest creating a stored procedure that takes all of the data as parameters, then performs the insert/select identity/insert steps inside a transaction. Even though the process is still the same as your manual inserts, using the stored procedure will allow you to more easily use it from your code. As #Rax mentions, you may also be able to use an ORM to get similar functionality.