T-SQL Insert into multiple linked tables using a condition and without using a cursor - tsql

T-SQL Insert into multiple linked tables using a condition and without using a cursor.
Hello,
I have the following tables
CREATE TABLE [dbo].[TestMergeQuote](
[uid] [uniqueidentifier] NOT NULL,
[otherData] [nvarchar](50) NULL,
CONSTRAINT [PK_TestMergeQuote] PRIMARY KEY CLUSTERED
(
[uid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[TestMergeQuote] ADD CONSTRAINT [DF_TestMergeQuote_uid] DEFAULT (newid()) FOR [uid]
--=============
CREATE TABLE [dbo].[TestMergeClient](
[id] [int] IDENTITY(1,1) NOT NULL,
[otherData] [nvarchar](50) NULL,
CONSTRAINT [PK_TestMergeClient] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
--==============
CREATE TABLE [dbo].[TestMergeDocument](
[id] [int] NOT NULL,
[uid_quote] [uniqueidentifier] NOT NULL,
[id_owner] [int] NOT NULL,
[id_keeper] [int] NULL,
[otherData] [nvarchar](50) NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[TestMergeDocument] WITH CHECK ADD CONSTRAINT [FK_TestMergeDocument_TestMergeClient_Keeper] FOREIGN KEY([id_keeper])
REFERENCES [dbo].[TestMergeClient] ([id])
GO
ALTER TABLE [dbo].[TestMergeDocument] CHECK CONSTRAINT [FK_TestMergeDocument_TestMergeClient_Keeper]
GO
ALTER TABLE [dbo].[TestMergeDocument] WITH CHECK ADD CONSTRAINT [FK_TestMergeDocument_TestMergeClient_Owner] FOREIGN KEY([id_owner])
REFERENCES [dbo].[TestMergeClient] ([id])
GO
ALTER TABLE [dbo].[TestMergeDocument] CHECK CONSTRAINT [FK_TestMergeDocument_TestMergeClient_Owner]
GO
ALTER TABLE [dbo].[TestMergeDocument] WITH CHECK ADD CONSTRAINT [FK_TestMergeDocument_TestMergeQuote] FOREIGN KEY([uid_quote])
REFERENCES [dbo].[TestMergeQuote] ([uid])
GO
ALTER TABLE [dbo].[TestMergeDocument] CHECK CONSTRAINT [FK_TestMergeDocument_TestMergeQuote]
GO
AND also table X with other various data.
I want to insert into these three tables the data that already exists in these 3 tables, but giving it different id's, and also replacing some of the data within the X table.
It's a sort of a "copy the data from last year", but add new info.
The condition is that id_keeper is sometimes null, and no insert should be done for it.
I am aware that I have to use OUTPUT and MERGE, but I have no ideea how to achieve something this complex.
The CRUDE code for this using a cursor would be:
DECLARE #OldIdDocument INT, #NewIdDocument INT
DECLARE #OldIdOwner INT, #NewIdOwner INT
DECLARE #OldIdKeeper INT, #NewIdKeeper INT
DECLARE #OldIdQuote UNIQUEINDETIFIER, #NewIdQuote UNIQUEINDETIFIER,
INSERT INTO TestMergeQuote(otherData)
SELECT TOP(1) otherData FROM TestMergeQuote WHERE uid = #OldIdQuote
SET #NewIdQuote = ##IDENTITY
INSERT INTO TestMergeClient(otherData)
SELECT TOP(1) otherData FROM TestMergeClient WHERE uid = #OldIdOwner
SET #NewIdOwner = ##IDENTITY
IF(#OldIdKeeper IS NOT NULL)
BEGIN
INSERT INTO TestMergeClient(otherData)
SELECT TOP(1) otherData FROM TestMergeClient WHERE uid = #OldIdKeeper
SET #NewIdKeeper = ##IDENTITY
END
INSERT INTO TestMergeDocument([uid_quote], [id_owner] , [id_keeper], otherData)
SELECT TOP(1) #NewIdQuote , #NewIdOwner , #NewIdKeeper ,otherData FROM TestMergeDocument WHERE uid = #OldIdDocument
SET #NewIdDocument = ##IDENTITY

You shouldn't have to use a cursor. What I would try is to first pump the data out into separate tables so you can manipulate the data to your heart's content.
Something like this first:
select * into TestMergeQuote_Temp from TestMergeQuote
That will make a new table with the data you want to copy. Of course you can add a where clause to filter the data so you aren't copying a very large table.
Then you can add values, change values, delete values on the _Temp versions.
When you are ready you can insert the data back. Of course you might have to turn auto id off if you have a primary key that is auto-incrementing. Or if you just want new id's and don't want to make id's manually, you should be able to insert the new records just fine and have new id's created for you.
But as a start, try pumping the data into new tables and then worry about inserting after that.

Related

Check to see if a row or an ID already exist before doing a mass insert on a table

I'm trying to an insert from one database table to another like this:
INSERT INTO factoryDB_Development.dbo.engineList
SELECT *
FROM factoryDB_Staging.dbo.engineList
WHERE engineID NOT IN (SELECT engineID
FROM factoryDB_Development.dbo.engineList)
But I am getting this error:
Cannot insert duplicate key in object 'dbo.engineList'
I am assume it's because that ID already exists in factoryDB_Development.dbo.engineList.
When I checked online at the Microsoft site, I just saw this:
To work around this issue, enable trace flag 8690 to disable the Spool operation
Is there a way to check to see if a row or ID already exists before doing the insert?
Thanks!
If I understood your problem correctly, you want to avoid inserting record in target table if that record with the same ID already exists.
If the ID column in question is a primary key, you can simply set IGNORE_DUP_KEY = ON by recreating the index.
Below is the sample example.
CREATE TABLE [dbo].[Source](
[ID] [int] NULL,
[Name] [varchar](5) NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[Target](
[ID] [int] NOT NULL,
[Name] [varchar](5) NULL,
PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = ON, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
--input sample values in source
INSERT INTO [Source] VALUES (1, 'A') , (1, 'A') , (2, 'B')
--insert data from source to target
INSERT INTO Target
SELECT * FROM Source
DROP TABLE SOURCE
DROP TABLE TARGET

How to insert an identity value into another table

I have two tables:
create table Clients
(
id_client int not null identity(1,1) Primary Key,
name_client varchar(10) not null,
phone_client int not null
)
create table Sales
(
id_sale int not null identity(1,1) Primary Key,
date_sale date not null,
total_sale float not null
id_clients int not null identity(1,1) Foreign Key references Clients
)
So, let's insert into Clients ('Ralph', 00000000), the id_client is going to be 1 (obviously). The question is: How could I insert that 1 into Sales?
FIrst of all - you cannot have two columns defined as identity in any table - you will get an error
Msg 2744, Level 16, State 2, Line 1
Multiple identity columns specified for table 'Sales'. Only one identity column per table is allowed.
So you will not be able to actually create that Sales table.
The id_clients column in the Sales table references an identity column - but in itself, it should not be defined as identity - it gets whatever value your client has.
create table Sales
(
id_sale int not null identity(1,1) Primary Key,
date_sale date not null,
total_sale float not null
id_clients int not null foreign key references clients(id_client)
)
-- insert a new client - this will create an "id_client" for that entry
insert into dbo.Clients(name_client, phone_client)
values('John Doe', '+44 44 444 4444')
-- get that newly created "id_client" from the INSERT operation
declare #clientID INT = SCOPE_IDENTITY()
-- insert the new "id_client" into your sales table along with other values
insert into dbo.Sales(......, id_clients)
values( ......., #clientID)
This works for me like a charm, because as far as I understand, if I migrate user or customer data etc from an old copy of the database, - the identity column and the UId(user id) or CId(customer id) used to give each row it's own unique identity - will become unsynced and therefor I use the second column(TId/UId etc) that contains a copy of the identity column's value to relate my data purely through app logic flow.
I can offset the actual SQL Identity Column(IdColumn) when a migration takes place via inserting and deleting dummy data to the count of the largest number found in that TId/CId etc column with the old data to increment the new database table(s) identity column(s) away from having duplicates on new inserts by using the dummy inserts to push the identity column up to old data values and therefor new customers or users etc will continue to source unique values from the identity column upon new inserts after being filled with the old data which will still contain the old identity values in the second column so that the other data(transactions etc) in other tables will still be in sync with which customer/user is related to said data, be it a transaction or whatever. But not have duplicates as the dummy data will have pushed up the IdColumn value to match the old data.
So if I have say 920 user records and the largest UId is 950 because 30 got deleted. Then I will dummy insert and delete 31 rows into the new table before adding the old 920 records to ensure UId's will have no duplicates.
It is very around the bush, but it works for my limited understanding of SQL XD
What this also allows me to do is delete a migrated user/customer/transaction at a later time using the original UId/CId/TId(copy of identity) and not have to worry that it will not be the correct item being deleted if I did not have the copy and were to be aiming at the actual identity column which would be out of sync if the data is migrated data(inserted into database from old database). Having a copy = happy days. I could use (SET IDENTITY_INSERT [dbo].[UserTable] ON) but I will probably screw that up eventually, so this way is FOR ME - fail safe.
Essentially making the data instance agnostic to an extent.
I use OUTPUT inserted.IdColumn to then save any pictures related using that in the file name and am able to call them specifically for the picture viewer and that data is also migration safe.
To do this, a copy, specifically at insert time, is working great.
declare #userId INT = SCOPE_IDENTITY() to store the new identity value-UPDATE dbo.UserTable to select where to update all in this same transaction
-SET UId = #userId to set it to my second column
-WHERE IdColumn = #userId; to aim at the correct row
USE [DatabaseName]
GO
/****** Object: StoredProcedure [dbo].[spInsertNewUser] Script Date:
2022/02/04 12:09:19 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[spInsertNewUser]
#Name varchar(50),
#PhoneNumber varchar(20),
#IDNumber varchar(50),
#Address varchar(200),
#Note varchar(400),
#UserPassword varchar(MAX),
#HideAccount int,
#UserType varchar(50),
#UserString varchar(MAX)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
INSERT INTO dbo.UserTable
([Name],
[PhoneNumber],
[IDNumber],
[Address],
[Note],
[UserPassword],
[HideAccount],
[UserType],
[IsDeleted],
[UserString])
OUTPUT inserted.IdColumn
VALUES
(#Name,
#PhoneNumber,
#IDNumber,
#Address,
#Note,
#UserPassword,
#HideAccount,
#UserType,
#UserString);
declare #userId INT = SCOPE_IDENTITY()
UPDATE dbo.UserTable
SET UId = #userId
WHERE IdColumn = #userId;
END
Here is the create for the table for testing
CREATE TABLE [dbo].[UserTable](
[IdColumn] [int] IDENTITY(1,1) NOT NULL,
[UId] [int] NULL,
[Name] [varchar](50) NULL,
[PhoneNumber] [varchar](20) NULL,
[IDNumber] [varchar](50) NULL,
[Address] [varchar](200) NULL,
[Note] [varchar](400) NULL,
[UserPassword] [varchar](max) NULL,
[HideAccount] [int] NULL,
[UserType] [varchar](50) NULL,
[IsDeleted] [int] NULL,
[UserString] [varchar](max) NULL,
CONSTRAINT [PK_UserTable] PRIMARY KEY CLUSTERED
(
[IdColumn] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

How to define a column to be both primary key and self-incremented in Transact-sql?

to define a column to be a primary key I do this
ContactID int NOT NULL PRIMARY KEY
It looks like I need still to provide the value of the ContactID when inserting rows. I want primary key to be unique and incremented (+1) as if I defined the table using the designer.
Thanks for helping
Simply do this:
ContactID int not null identity(1,1) primary key
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Contacts](
[ContactID] [int] IDENTITY(1,1) NOT NULL,
CONSTRAINT [PK_Contacts] PRIMARY KEY CLUSTERED
(
[ContactID] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY]
GO
you will have to add your other columns, etc.
UPDATE: as absolute minimum statement:
CREATE TABLE Contacts(ContactID INT IDENTITY (1,1) PRIMARY KEY);

T-SQL Self Join in combination with aggregate function

I have the following table:
CREATE TABLE [dbo].[Tree](
[AutoID] [int] IDENTITY(1,1) NOT NULL,
[Category] [varchar](10) NULL,
[Condition] [varchar](10) NULL,
[Description] [varchar](50) NULL,
CONSTRAINT [PK_Tree] PRIMARY KEY CLUSTERED
(
[AutoID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
The data looks like this:
INSERT INTO [Test].[dbo].[Tree]
([Category]
,[Condition]
,[Description])
VALUES ('1','Alpha','Type 1')
INSERT INTO [Test].[dbo].[Tree]
([Category]
,[Condition]
,[Description])
VALUES ('1','Alpha','Type 1')
INSERT INTO [Test].[dbo].[Tree]
([Category]
,[Condition]
,[Description])
VALUES ('2','Alpha','Type 2')
INSERT INTO [Test].[dbo].[Tree]
([Category]
,[Condition]
,[Description])
VALUES ('2','Alpha','Type 2')
GO
I try now to do the following:
SELECT Category,COUNT(*) as CategoryCount FROM Tree where Condition = 'Alpha' group by Category
However, I also wish to get the Description for each Element. I tried several subqueries, self joins etc., but I always come to the problem that the subquery cannot return more than one record.
The problem is caused by a poor database design which I cannot change and I have run out of ideas as to how to get this done in a single query.
If you need description, you have to include it in the aggregate
SELECT Category ,
[Description] ,
COUNT(*) AS CategoryCount
FROM Tree
WHERE Condition = 'Alpha'
GROUP BY Category ,
[Description]

CREATE TABLE statement question in T-SQL

I am working on a pomotion database and below is what my CREATE TABLE steatment looks like:
CREATE TABLE [dbo].[sponsors]
(
[InstId] [bigint] NOT NULL,
[EncryptedData] [varbinary](44) NOT NULL,
[HashedData] [varbinary](22) NOT NULL,
[JobId] [bigint] NOT NULL,
CONSTRAINT [PK_sponsors] PRIMARY KEY CLUSTERED
(
[InstId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[sponsors] WITH CHECK ADD CONSTRAINT [FK_sponsors_jobs] FOREIGN KEY([JobId])
REFERENCES [dbo].[jobs] ([Id])
GO
ALTER TABLE [dbo].[sponsors] CHECK CONSTRAINT [FK_sponsors_jobs]
GO
ALTER TABLE [dbo].[sponsors] WITH CHECK ADD CONSTRAINT [FK_sponsors_titles] FOREIGN KEY([TId])
REFERENCES [dbo].[titles] ([TId])
GO
ALTER TABLE [dbo].[sponsors] CHECK CONSTRAINT [FK_sponsors_titles]
GO
And I'd like to get rid of ALTER TABLE statements and make them part of CREATE TABLE
statements, I know how to do the most but not sure how to get CHECK CONSTRAINT during
create table, does anyone have experience with this? or know how to?
You can just add each foreign key constraint right in the CREATE TABLE declaration:
CREATE TABLE [dbo].[sponsors]
(
[InstId] [bigint] NOT NULL,
[EncryptedData] [varbinary](44) NOT NULL,
[HashedData] [varbinary](22) NOT NULL,
[JobId] [bigint] NOT NULL,
[TId] [int] NOT NULL,
CONSTRAINT [PK_sponsors] PRIMARY KEY CLUSTERED
(
[InstId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
CONSTRAINT [FK_sponsors_jobs] FOREIGN KEY([JobId])
REFERENCES [dbo].[jobs] ([Id]),
CONSTRAINT [FK_sponsors_titles] FOREIGN KEY([TId])
REFERENCES [dbo].[titles] ([TId])
) ON [PRIMARY]
You seem to have missed a column (TId)
CREATE TABLE [dbo].[sponsors]
(
[InstId] [bigint] NOT NULL
CONSTRAINT [PK_sponsors] PRIMARY KEY CLUSTERED,
[EncryptedData] varbinary NOT NULL,
[HashedData] varbinary NOT NULL,
[JobId] [bigint] NOT NULL
CONSTRAINT [FK_sponsors_jobs] FOREIGN KEY REFERENCES [dbo].[jobs] ([Id]),
[TId] int NOT NULL
CONSTRAINT [FK_sponsors_titles] FOREIGN KEY REFERENCES [dbo].[titles] ([TId])
) ON [PRIMARY]
The ALTER TABLE ... CHECK CONSTRAINT command simply enables (or disables with NOCHECK) the constraint. Constraints are enabled by default when you add them, so this extra statement is redundant and not needed if you are adding the constraints in the CREATE statement.
http://msdn.microsoft.com/en-us/library/ms190273(SQL.90).aspx