Bulk inert to parent children tables - sql

I have the following three tables (each RobotPart has exactly one arm and one leg). I have a large number of {arm, leg} pairs need to be inserted. Any new combinations of arm and leg will create a new entry in RobotPart. Any existing combination will not be inserted. No updates are needed for either parent or children. I need preserve the identity columns. Any efficient ways to accomplish this in SQL?
CREATE TABLE [dbo].[Arm](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Model] [varchar](20) NULL,
CONSTRAINT [PK_dbo.Arm] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
) ON [PRIMARY]
CREATE TABLE [dbo].[Leg](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Model] [varchar](10) NULL,
CONSTRAINT [PK_dbo.Leg] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
) ON [PRIMARY]
CREATE TABLE [dbo].[RobotPart](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](20) NOT NULL,
[ArmId] [int] NOT NULL,
[LegId] [int] NOT NULL,
CONSTRAINT [PK_dbo.RobotPart] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[RobotPart] WITH CHECK ADD CONSTRAINT [FK_dbo.RobotPart_dbo.Arm_ArmId] FOREIGN KEY([ArmId])
REFERENCES [dbo].[Arm] ([Id])
GO
ALTER TABLE [dbo].[RobotPart] WITH CHECK ADD CONSTRAINT [FK_dbo.RobotPart_dbo.Leg_LegId] FOREIGN KEY([LegId])
REFERENCES [dbo].[Leg] ([Id])
GO

Step 1: Insert new arms and legs using a LEFT OUTER JOINs from your source table to the arms and legs table, respectively. (a separate insert statement for each table)
Step 2: Insert the new combinations using an inner join from your source table to arms and legs and a left outer join from the your source table to the RobotPart table.

Here is my initial try on it using MERGE. Not sure how this compare to Lmu92 proposed.
CREATE TYPE [dbo].[RobotPart_udtt] AS TABLE(
[Arm] [varchar](20) NOT NULL,
[Leg] [varchar](10) NOT NULL,
[Name] [varchar](20) NOT NULL
)
GO
CREATE PROCEDURE dbo.[prc_Component_Create]
#robotParts [RobotPart_udtt] READONLY
AS
BEGIN
SET NOCOUNT ON;
DECLARE #messageId INT
DECLARE #status INT
MERGE [Arm] AS TARGET
USING (
SELECT
tR.arm AS Model
FROM #robotParts AS tR
) AS SOURCE
ON TARGET.Model = SOURCE.Model
WHEN NOT MATCHED THEN
INSERT
(
Model
)
VALUES
(
SOURCE.Model
);
MERGE [Leg] AS TARGET
USING (
SELECT
tR.leg AS Model
FROM #robotParts AS tR
) AS SOURCE
ON TARGET.Model = SOURCE.Model
WHEN NOT MATCHED THEN
INSERT
(
Model
)
VALUES
(
SOURCE.Model
);
WITH NewParts (ArmId, LegId, Name)
AS
(
SELECT tA.Id
, tL.Id
, tR.Name
FROM #robotParts AS tR
INNER JOIN [Arm] AS tA
ON tR.Arm = tA.Model
INNER JOIN [Leg] AS tL
ON tR.Leg = tL.Model
)
INSERT INTO RobotPart (Name, ArmId, LegId)
SELECT tN.Name
, tN.ArmId
, tN.LegId
FROM NewParts AS tN
LEFT JOIN RobotPart AS tR
ON tR.ArmId = tN.ArmId
AND tR.LegId = tN.LegId
WHERE tR.ArmId IS NULL AND tR.LegId IS NULL
END

Related

SQL Statement summarize missing employee certifications

I am trying to create a report on tables that I can't modify and am not sure if this is even possible.
Using the script below, I am trying to get a query result of:
--Certification | Employee | Has Certification
--CPR | Santa Clause | Yes
--CPR | Rudolph | No
CREATE TABLE [dbo].[Certification]([Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_Certification] PRIMARY KEY CLUSTERED ([Id] ASC));
CREATE TABLE [dbo].[Employee]([Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_Employee] PRIMARY KEY CLUSTERED ([Id] ASC));
CREATE TABLE [dbo].[EmployeeCertification]([Id] [int] IDENTITY(1,1) NOT NULL,
[CertificationID] [int] NOT NULL,
[EmployeeID] [int] NOT NULL,
CONSTRAINT [PK_EmployeeCertification] PRIMARY KEY CLUSTERED ([Id] ASC));
ALTER TABLE [dbo].[EmployeeCertification] WITH CHECK ADD CONSTRAINT
[FK_EmployeeCertification_Certification] FOREIGN KEY([CertificationID])
REFERENCES [dbo].[Certification] ([Id])
ALTER TABLE [dbo].[EmployeeCertification] CHECK CONSTRAINT
[FK_EmployeeCertification_Certification]
ALTER TABLE [dbo].[EmployeeCertification] WITH CHECK ADD CONSTRAINT
[FK_EmployeeCertification_Employee] FOREIGN KEY([EmployeeID])
REFERENCES [dbo].[Employee] ([Id])
ALTER TABLE [dbo].[EmployeeCertification] CHECK CONSTRAINT
[FK_EmployeeCertification_Employee]
INSERT INTO Certification (Name) VALUES ('CPR');
INSERT INTO Employee (Name) VALUES ('Santa Clause'),('Rudolph');
INSERT INTO EmployeeCertification (CertificationID,EmployeeID) VALUES(1,1);
You can cross join employees and certifications to generate all possible combinations, then use a subquery to check whether each tuple exists in the bridge table:
select c.name as certification, e.name as employee,
case when exists (
select 1
from employeecertification ec
where ec.employeeid = e.id and ec.certificationid = c.id
) then 'Yes' else 'No' end as has_certification
from employee e
cross join certification c
This can also be done with a left join:
select c.name as certification, e.name as employee,
case ec.id is null then 'No' else 'Yes' end as has_certification
from employee e
cross join certification c
left join employeecertification ec on ec.employeeid = e.id and ec.certificationid = c.id

SQL Server: select records, not linked to another table

I have a table:
CREATE TABLE [dbo].[CollectionSite]
(
[SiteCode] [nvarchar](32) NOT NULL,
[AddressId] [int] NOT NULL,
[RemittanceId] [int] NULL,
// additional columns
)
and a linked table:
CREATE TABLE [dbo].[CollectionSiteAddress]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](255) NULL,
[Address1] [nvarchar](255) NULL,
[Address2] [nvarchar](255) NULL,
[City] [nvarchar](128) NULL,
[State] [nvarchar](64) NULL,
[Zip] [nvarchar](32) NULL,
)
Relationship between these 2 tables:
ALTER TABLE [dbo].[CollectionSite] WITH CHECK
ADD CONSTRAINT [FK_CollectionSite_CollectionSiteAddress_AddressId]
FOREIGN KEY([AddressId]) REFERENCES [dbo].[CollectionSiteAddress] ([Id])
GO
ALTER TABLE [dbo].[CollectionSite] WITH CHECK
ADD CONSTRAINT [FK_CollectionSite_CollectionSiteAddress_RemittanceId]
FOREIGN KEY([RemittanceId]) REFERENCES [dbo].[CollectionSiteAddress] ([Id])
GO
I want to select all records from CollectionSiteAddress, which are not linked to CollectionSite (neither AddressId nor RemittanceId). Which request should I use?
I tried:
SELECT *
FROM CollectionSiteAddress
LEFT JOIN CollectionSite ON CollectionSiteAddress.Id = CollectionSite.AddressId
OR CollectionSiteAddress.Id = CollectionSite.RemittanceId
but it selects all records from CollectionSiteAddress
You are missing this WHERE clause:
WHERE CollectionSite.[SiteCode] IS NULL
because you want all the unmatched rows of CollectionSiteAddress.
I used the column [SiteCode] to check if it is NULL because it is not nullable in the definition of the table.
So you can write your query like this (shortened with aliases):
SELECT csa.*
FROM CollectionSiteAddress csa LEFT JOIN CollectionSite cs
ON csa.Id = cs.AddressId OR csa.Id = cs.RemittanceId
WHERE cs.[SiteCode] IS NULL
Or use NOT EXISTS:
SELECT csa.*
FROM CollectionSiteAddress csa
WHERE NOT EXISTS (
SELECT 1
FROM CollectionSite cs
WHERE csa.Id = cs.AddressId OR csa.Id = cs.RemittanceId
)

Inserting into many-to-many table in SQL Server

This is my Tag table:
CREATE TABLE [dbo].[Tag](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](max) NULL,
[CreationDate] [datetime] NOT NULL,
[TagSlug] [nvarchar](max) NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
and this is my Post table:
CREATE TABLE [dbo].[Post](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Title] [nvarchar](400) NOT NULL,
[Body] [nvarchar](max) NOT NULL,
[Summary] [nvarchar](max) NOT NULL,
[CreationDate] [datetime] NOT NULL,
[UrlSlug] [nvarchar](max) NOT NULL,
[Picture] [nvarchar](max) NULL,
[TagId] [int] NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[Post] WITH CHECK ADD CONSTRAINT [Post_Tag] FOREIGN KEY([TagId])
REFERENCES [dbo].[Tag] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Post] CHECK CONSTRAINT [Post_Tag]
GO
I just wanna to insert the Id from Tag and PostId from Post into a new table named Post_Tag which is a many to many relation, this is the script of my Post_Tag table:
CREATE TABLE [dbo].[Post_Tag](
[PostId] [int] NOT NULL,
[TagId] [int] NOT NULL,
CONSTRAINT [PK_dbo.Post_Tag] PRIMARY KEY CLUSTERED ([PostId] ASC, [TagId] ASC)
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Post_Tag] WITH CHECK
ADD CONSTRAINT [FK_dbo.Post_Tag_dbo.Post_PostId]
FOREIGN KEY([PostId]) REFERENCES [dbo].[Post] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Post_Tag] CHECK CONSTRAINT [FK_dbo.Post_Tag_dbo.Post_PostId]
GO
ALTER TABLE [dbo].[Post_Tag] WITH CHECK
ADD CONSTRAINT [FK_dbo.Post_Tag_dbo.Tag_TagId]
FOREIGN KEY([TagId]) REFERENCES [dbo].[Tag] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Post_Tag] CHECK CONSTRAINT [FK_dbo.Post_Tag_dbo.Tag_TagId]
GO
Now, to do that I've tried the below query:
insert into [Blog].[dbo].[Post_Tag] (PostId,TagId)
select [Id] as [PostId] from [OldBlog].[dbo].[Tag]
select [TagId] from [OldBlog].[dbo].[Post]
but this error appear while running the script:
The select list for the INSERT statement contains fewer items than the insert list. The number of SELECT values must match the number of INSERT columns.
what's wrong with my query? thanks
The 2 select queries are being processed separately. You will have to come up with a way to join [OldBlog].[dbo].[Tag] to [OldBlog].[dbo].[Post] so you can insert fields PostId,TagId into [Blog].[dbo].[Post_Tag] from this new table expression.
For this, you can use the row number of each row from the two select statements as a link so you can join them and select what you need from both of them.
SELECT POST.[PostId], TAG.[TagId]
FROM (
select ROW_NUMBER() OVER (ORDER BY [Id]) AS Link, [Id] as [PostId] from [OldBlog].[dbo].[Tag]) AS POST
JOIN (
select ROW_NUMBER() OVER (ORDER BY [TagId]) AS Link, [TagId] from [OldBlog].[dbo].[Post]) AS TAG ON POST.Link = TAG.Link
IMPORTANT NOTE:
This is just a means of "forcing" a relationship between tables without any relationship to each other whatsoever. This is indeed a dangerous thing to do because we are forcing a relationship between the tables based on row number and not an actual key. This should only be used if there is no definite expected output or as a last resort if there is no other way to link two or more unrelated tables where the relationship of each selected column don't matter.

sql server insert records from one table to another

how does one insert records from one table to another that has a unique index in the destination table without going through the insert and then removal of duplicates by deleting the index?
INSERT INTO forms(url,feedUrl, dateadded)
SELECT url, feedurl, dateadded
FROM Book3 T2
where not exists(select * from forms T1 where T1.url = T2.url;
T2.feedurl = T1.feedUrl and T2.dateadded =T1.dateadded)
Violation of UNIQUE KEY constraint 'IX_forms'. Cannot insert duplicate key in object 'dbo.forms'.
Table forms
CREATE TABLE [dbo].[forms](
[id] [int] IDENTITY(1,1) NOT NULL,
[url] [varchar](450) NULL,
[feedUrl] [varchar](450) NULL,
[dateadded] [datetime] NULL,
CONSTRAINT [PK_forms] PRIMARY KEY CLUSTERED
(
Table book3
CREATE TABLE [dbo].[Book3](
[url] [varchar](450) NULL,
[feedurl] [varchar](450) NULL,
[dateadded] [datetime] NULL
) ON [PRIMARY]
You may have duplicates in your results set. Does this query give you fewer records than the orginal select?
SELECT distinct url, feedurl, dateadded
FROM Book3 T2
where not exists(select * from forms T1 where T1.url = T2.url
T2.feedurl = T1.feedUrl and T2.dateadded =T1.dateadded)

Can't update column values, it is associated with a clustered index?

I am having some problems when trying to update column values, this column has a clustered index associated to it.
This is the update statement.
UPDATE dbo.VentureXRef
SET RefValue = REPLICATE('0',7 - LEN(RefValue)) + RefValue WHERE LEN(RefValue) < 7
This is the error I get
Cannot insert duplicate key row in
object 'dbo.VentureXRef' with unique
index 'idx_WFHMJVXRef_RefValueByType'.
This is mytable definition
CREATE TABLE [dbo].[VentureXRef]
(
[ID] [int] NOT NULL IDENTITY(1, 1),
[RefValue] [varchar] (30) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[RefValueTypeID] [int] NOT NULL,
[State] [char] (2) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL CONSTRAINT [DF__WFHMJoint__State__2AC11801] DEFAULT (' '),
[ClientID] [int] NOT NULL,
[DoingBusinessAs] [varchar] (255) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[Disabled] [bit] NOT NULL CONSTRAINT [DF_VentureXRef_Disabled] DEFAULT (0),
[Username] [varchar] (64) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL CONSTRAINT [DF_VentureXRef_Username] DEFAULT (user_name()),
[DateDeleted] [datetime] NULL,
[DateLastModified] [datetime] NOT NULL CONSTRAINT [DF_VentureXRef_DateLastModified] DEFAULT (getdate())
) ON [PRIMARY]
GO
CREATE CLUSTERED INDEX [idx_WFHMJVXRef_RefValue] ON [dbo].[VentureXRef] ([RefValue], [State]) WITH (FILLFACTOR=80) ON [PRIMARY]
GO
ALTER TABLE [dbo].[VentureXRef] ADD CONSTRAINT [PK__WFHMJointVenture__28D8CF8F] PRIMARY KEY NONCLUSTERED ([ID]) WITH (FILLFACTOR=80) ON [PRIMARY]
GO
CREATE UNIQUE NONCLUSTERED INDEX [idx_WFHMJVXRef_RefValueByType] ON [dbo].[VentureXRef] ([RefValue], [State], [DateDeleted], [RefValueTypeID]) WITH (FILLFACTOR=80) ON [PRIMARY]
GO
ALTER TABLE [dbo].[VentureXRef] ADD CONSTRAINT [IX_VentureXRef] UNIQUE NONCLUSTERED ([RefValue], [RefValueTypeID], [State], [DateDeleted]) WITH (FILLFACTOR=80) ON [PRIMARY]
GO
ALTER TABLE [dbo].[VentureXRef] ADD CONSTRAINT [fk_WFHMJVXRef_ClientID] FOREIGN KEY ([ClientID]) REFERENCES [dbo].[Client] ([ClientID])
GO
ALTER TABLE [dbo].[VentureXRef] ADD CONSTRAINT [fk_WFHMJVXRef_RefValueTypeID] FOREIGN KEY ([RefValueTypeID]) REFERENCES [dbo].[VentureRefValueType] ([RefValueTypeID])
GO
What is the proper way to do this update statement?
Thanks in advance
YOur problem is you are trying to update it to a value that already exists in the table and so the unique index says it can't.
as mentioned by HILGEm this is a duplicate records problem.To identify records causing duplication you can run below query after substituting your table and database name in place of CTE
use test;
with cte as (
select '123' refvalue union all select '567' union all
select '0000123' union all
select '123456')
select refvalue from cte as a
where
len(refvalue) <7 and
exists(
select 1 from cte as b where
len(refvalue)>=7 and
REPLICATE('0',7 - LEN(a.RefValue)) + a.RefValue =b.refvalue
)