Can't update column values, it is associated with a clustered index? - sql-server-2005

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
)

Related

Incorrect syntax when working with a variable in IDENTITY

I'm trying to create a new table with a primary key value that is a continuation of a previous table.
My code is:
DECLARE #Table1_NextKey INT
SELECT #Table1_NextKey = MAX(id) + 1
FROM [Copy of Table1]
CREATE TABLE [dbo].Table1
(
[ID] [int] NOT NULL IDENTITY(#Table1_NextKey, 1)
CONSTRAINT PK_Table1 PRIMARY KEY CLUSTERED,
[PLAN] [nvarchar](255) NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
But I get this error:
Msg 102, Level 15, State 1, Line 24
Incorrect syntax near '#Table1_NextKey'
Is there a way to get the Create Table to work with the variable?
You are going about this the wrong way.
What you are clearly trying to do, is copy a table and then you would like to continue the identity values.
In this case, do not declare the seed value differently in the CREATE TABLE, just manually set it afterwards:
CREATE TABLE [dbo].Table1
(
[ID] [int] NOT NULL IDENTITY(1, 1)
CONSTRAINT PK_Table1 PRIMARY KEY CLUSTERED,
[PLAN] [nvarchar](255) NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
-- Do some copying code here
DECLARE #Table1_NextKey INT =
(
SELECT #Table1_NextKey = MAX(id) -- not + 1
FROM Table1
);
DBCC CHECKIDENT (Table1 RESEED, #Table1_NextKey) WITH NO_INFOMSGS;
You can only use literal values for identity, you'll need to dynamically construct your create statement, as follows
declare #sql nvarchar(max)=Concat(N'
CREATE TABLE [dbo].Table1(
[ID] [int] NOT NULL IDENTITY(', #Table1_NextKey, N', 1) CONSTRAINT PK_Table1 PRIMARY KEY CLUSTERED,
[PLAN] [nvarchar](255) NULL)
ON [PRIMARY]')
exec sp_executesql #sql
Don't know why SQL Server is so weird about this but with Stu's help, I got this to work:
DECLARE #Table1_NextKey INT,
#SQL_command varchar(4000)
select #Table1_NextKey=max(id)+1 from [Copy of Table1]
set #SQL_command=
'CREATE TABLE [dbo].Table1(
[ID] [int] NOT NULL IDENTITY(' + convert(varchar(5), #Table1_Nextkey) + ', 1) CONSTRAINT PK_Table1 PRIMARY KEY CLUSTERED,
[PLAN] [nvarchar](255) NULL)
ON [PRIMARY]
GO'

T-SQL split table vertically (moving column) with (almost) same performance

In T-SQL (MS SQL Server 2016) I want to split vertically a big table (220 GB - 500 million rows) as some columns data are descriptions and some are daily data.
So from
CREATE TABLE [BigTable](
[OptionID] [int] NOT NULL,
[Date] [datetime] NOT NULL,
[ParentID] [bigint] NOT NULL,
[Description] [char](255) NOT NULL,
[Price] [real] NULL,
[PriceTheo] [real] NULL
CONSTRAINT [PK_BigTable] PRIMARY KEY CLUSTERED
(
[ParentID] ASC,
[Date] ASC,
[OptionID] ASC
) ON [PRIMARY]
) ON [PRIMARY]
GO
I would move to:
CREATE TABLE [DescriptionTable](
[OptionVersionID] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[OptionID] [int] NOT NULL,
[ParentID] [bigint] NOT NULL,
[Description] [char](255) NOT NULL,
CONSTRAINT [PK_DescriptionTable] PRIMARY KEY CLUSTERED
([OptionVersionID] ASC) ON [PRIMARY]) ON [PRIMARY]
CREATE TABLE [DailyTable](
[OptionVersionID] [int] NOT NULL,
[Date] [datetime] NOT NULL,
[Price] [real] NULL,
[PriceTheo] [real] NULL
CONSTRAINT [PK_DailyTable] PRIMARY KEY CLUSTERED
([OptionVersionID] ASC,[Date] ASC) ON [PRIMARY]) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_DailyTable_Date] ON [DailyTable]
([Date] ASC) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_DailyTable_OptionVersionID] ON [DailyTable]
([OptionVersionID] ASC) ON [PRIMARY]
GO
ALTER TABLE [DailyTable] WITH CHECK ADD CONSTRAINT [FK_DailyTable_DescriptionTable] FOREIGN KEY([OptionVersionID])
REFERENCES [DescriptionTable] ([OptionVersionID])
GO
ALTER TABLE [DailyTable] CHECK CONSTRAINT [FK_DailyTable_DescriptionTable]
GO
I then create a view
CREATE VIEW [vBigTable]
AS
SELECT
[OptionID],
[Date],
[ParentID],
[Description],
[Price],
[PriceTheo]
FROM DailyTable da INNER JOIN
DescriptionTable de ON da.OptionVersionID = de.OptionVersionID
I thought I should get the same kind of performance (almost) when I request data from the view vBigTable but actually I don't (some request can be 10x slower). Do I miss something to have almost the same performance when I select, join, group by ... (only reading data) from vBigTable or even when I use the specific INNER JOIN between Description and Daily table?
PS: I have more non clustered indices and columns in real life.

SQL foreign key constraint with additional column value check

I have this situation
CREATE TABLE [dbo].[Customer]
(
Id INT IDENTITY(1,1) NOT NULL,
IsWholesaler BIT NOT NULL,
Name NVARCHAR(255) NOT NULL
)
CREATE TABLE [dbo].[WholesalerUser]
(
Id INT IDENTITY(1,1) NOT NULL,
CustomerId INT NOT NULL,
UserId INT NOT NULL
)
CREATE TABLE [dbo].[User]
(
Id INT IDENTITY(1,1) NOT NULL,
Name NVARCHAR(255)
)
One Customer which has IsWholesaler column set to true can have multiple users.
I have WholesalerUser table for this purpose but the Foreign key WholesalerUser.CustomerId is attached to Customer.Id but how to check the column IsWholesaler = 1 ?
Foreign key can be defined over several columns at once.
Add a column IsWholesaler to the WholesalerUser.
Add a check constraint to make sure that it is always 1.
Define foreign key over two columns (CustomerId, IsWholesaler).
To define such foreign key the Customer table should have either primary key as (ID, IsWholesaler), or a separate unique index on (ID, IsWholesaler).
Table Customer
CREATE TABLE [dbo].[Customer](
[Id] [int] IDENTITY(1,1) NOT NULL,
[IsWholesaler] [bit] NOT NULL,
[Name] [nvarchar](255) NOT NULL,
CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED
(
[Id] ASC
))
Unique index to support foreign key constraint:
CREATE UNIQUE NONCLUSTERED INDEX [IX_ID_IsWholesaler] ON [dbo].[Customer]
(
[Id] ASC,
[IsWholesaler] ASC
)
Table WholesalerUser
CREATE TABLE [dbo].[WholesalerUser](
[Id] [int] IDENTITY(1,1) NOT NULL,
[CustomerId] [int] NOT NULL,
[IsWholesaler] [bit] NOT NULL,
[UserId] [int] NOT NULL,
CONSTRAINT [PK_WholesalerUser] PRIMARY KEY CLUSTERED
(
[Id] ASC
))
Foreign key
ALTER TABLE [dbo].[WholesalerUser] WITH CHECK
ADD CONSTRAINT [FK_WholesalerUser_Customer]
FOREIGN KEY([CustomerId], [IsWholesaler])
REFERENCES [dbo].[Customer] ([Id], [IsWholesaler])
GO
ALTER TABLE [dbo].[WholesalerUser]
CHECK CONSTRAINT [FK_WholesalerUser_Customer]
GO
Check constraint
ALTER TABLE [dbo].[WholesalerUser] WITH CHECK
ADD CONSTRAINT [CK_WholesalerUser]
CHECK (([IsWholesaler]=(1)))
GO
ALTER TABLE [dbo].[WholesalerUser]
CHECK CONSTRAINT [CK_WholesalerUser]
GO
ALTER TABLE [dbo].[WholesalerUser]
ADD CONSTRAINT [DF_WholesalerUser_IsWholesaler]
DEFAULT ((1)) FOR [IsWholesaler]
GO

Bulk inert to parent children tables

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

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.