Simple SQL Server Delete fails - sql-server

I am seeing intermittent failures upon a simple delete.
Essentially I have a temporary note that has many entries. Each entry has a classification which is a lookup value. Once this note is completed, it gets sent to a note repository, and the temporary version needs to be deleted.
I can't replicate reliably, but on occasion, when calling the stored procedure that does the delete of the temp note, only SOME of the entries get deleted. Coincidentally (?) the entry left behind has always been of one specific classification type.
After many many many attempts I was able to reproduce the issue while running SQL Server Profiler. Despite trying to catch Attention, ErrorLog, EventLog, Exception, and Execution Warnings, the resulting profile shows nothing out of the ordinary.
None of the involved tables are large. In fact they're minuscule. ~100-1000 at any given time in Entry, ~100 in Draft, 9 in Classification, 3 in Category.
I don't believe it should matter, but just in case, this stored procedure is being called from Entity Framework.
Any ideas? Any ideas on what to try for troubleshooting? I'm completely at a loss. Thanks in advance for any help.
Here is the stored procedure for deletion:
CREATE PROCEDURE [NoteDraft].[ClearNoteDraft]
#DraftId BIGINT
AS
BEGIN
SET NOCOUNT ON;
DELETE FROM NoteDraft.[Entry]
WHERE DraftId = #DraftId
DELETE FROM NoteDraft.Draft
WHERE Id = #DraftId
END
Here are the table definitions (with some columns left out for brevity as noted.)
CREATE TABLE [NoteDraft].[Category]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NOT NULL,
[SortOrder] [int] NULL,
CONSTRAINT [PK_Category]
PRIMARY KEY CLUSTERED ([Id] ASC)
) ON [PRIMARY]
CREATE TABLE [NoteDraft].[Classification]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[CategoryId] [int] NOT NULL,
[Name] [varchar](50) NOT NULL,
[SortOrder] [int] NULL,
CONSTRAINT [PK_Classification]
PRIMARY KEY CLUSTERED ([Id] ASC)
)
CREATE TABLE [NoteDraft].[Draft]
(
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[DateModified] [datetime] NOT NULL CONSTRAINT [DF_TestNoteDraft_DateModified] DEFAULT (getdate()),
[AccountNumber] [varchar](30) NULL,
--...10 other biz columns...
CONSTRAINT [PK_Notes]
PRIMARY KEY CLUSTERED ([Id] ASC)
) ON [PRIMARY]
CREATE TABLE [NoteDraft].[Entry]
(
[DraftId] [bigint] NOT NULL,
[ClassificationId] [int] NOT NULL,
[Body] [varchar](2100) NULL,
CONSTRAINT [PK_Entry]
PRIMARY KEY CLUSTERED ([DraftId] ASC, [ClassificationId] ASC)
) ON [PRIMARY]
ALTER TABLE [NoteDraft].[Classification] WITH CHECK
ADD CONSTRAINT [FK_Classification_Category]
FOREIGN KEY([CategoryId]) REFERENCES [NoteDraft].[Category] ([Id])
GO
ALTER TABLE [NoteDraft].[Classification] CHECK CONSTRAINT [FK_Classification_Category]
GO
ALTER TABLE [NoteDraft].[Entry] WITH CHECK
ADD CONSTRAINT [FK_Entry_Classification]
FOREIGN KEY([ClassificationId]) REFERENCES [NoteDraft].[Classification] ([Id])
GO
ALTER TABLE [NoteDraft].[Entry] CHECK CONSTRAINT [FK_Entry_Classification]
GO
ALTER TABLE [NoteDraft].[Entry] WITH CHECK
ADD CONSTRAINT [FK_Entry_Draft]
FOREIGN KEY([DraftId]) REFERENCES [NoteDraft].[Draft] ([Id])
GO
ALTER TABLE [NoteDraft].[Entry] CHECK CONSTRAINT [FK_Entry_Draft]
GO

As with everything simple like this, the answer wasn't where I was looking.
Turns out, there's an event listener on the page that's re-inserting the records post deletion.
Still having trouble figuring out why the listener is running, but at least I know what's going on.

Related

Can SQL Server table have a foreign key to a table that resolves to many records?

Consider the following table...
CREATE TABLE [dbo].[Alerts]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[I18NMessageKey] [uniqueidentifier] NOT NULL
PRIMARY KEY CLUSTERED ([Id] ASC)
) ON [PRIMARY]
GO
and the following table...
CREATE TABLE [dbo].[I18NMessages]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Key] [uniqueidentifier] NOT NULL,
[Culture] [nvarchar](200) NOT NULL,
[Message] [nvarchar](max) NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
) ON [PRIMARY]
GO
I would like to add a foreign key constraint to table [Alerts] on the column [I18NMessageKey] to refer to many records in table [I18NMessages].
Is this possible without a third table?
The [I18NMessages] table holds the same message for the [Key] but in different languages depending on [Culture]. The relationship between [Alerts] and [I18NMessages] doesn't care about the culture. The resolution of [Culture] depends on the user at runtime.
In SQL Server, the uniqueness of the referenced key column(s) must be enforced by a primary key, unique constraint, or unique index. You need a third table with a unique I18NMessageKey column key to enforce referential integrity.
You can create a trigger and implement custom business logic

Slow on Retrieving data from 38GB SQL Table

I am looking for some advise. I have a SQL Server table called AuditLog and this table records any action/changes that happens to our DB from our web application.
I am trying to build some reports and anytime I try to pull data from this table it makes my query run from seconds to 10mins+. Just doing a
select * from dbo.auditlog
takes about 2hours+.
The table has 77 million rows and is growing. Anyhow, only thoughts at this moment is to do an index but that would slow down inserts. Not sure how much that would affect performance but have held back on it. Other thoughts were to partition the table or do an index view but we are running SQL Server 2014 Standard Edition and those options are not supported.
Here is the table create statement:
CREATE TABLE [dbo].[AuditLog]
(
[AuditLogId] [uniqueidentifier] NOT NULL,
[UserId] [uniqueidentifier] NULL,
[EventDateUtc] [datetime] NOT NULL,
[EventType] [char](1) NOT NULL,
[TableName] [nvarchar](100) NOT NULL,
[RecordId] [nvarchar](100) NOT NULL,
[ColumnName] [nvarchar](100) NOT NULL,
[OriginalValue] [nvarchar](max) NULL,
[NewValue] [nvarchar](max) NULL,
[Rams1RecordID] [uniqueidentifier] NULL,
[Rams1AuditHistoryID] [uniqueidentifier] NULL,
[Rams1UserID] [uniqueidentifier] NULL,
[CreatedBy] [uniqueidentifier] NULL,
[CreatedDate] [datetime] NULL DEFAULT (getdate()),
[OriginalValueNiceName] [nvarchar](100) NULL,
[NewValueNiceName] [nvarchar](100) NULL,
CONSTRAINT [PK_AuditLog]
PRIMARY KEY CLUSTERED ([TableName] ASC, [RecordId] ASC, [AuditLogId] ASC)
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[AuditLog] WITH NOCHECK
ADD CONSTRAINT [FK_AuditLog_User]
FOREIGN KEY([UserId]) REFERENCES [dbo].[User] ([UserID])
GO
ALTER TABLE [dbo].[AuditLog] CHECK CONSTRAINT [FK_AuditLog_User]
GO
ALTER TABLE [dbo].[AuditLog] WITH NOCHECK
ADD CONSTRAINT [FK_AuditLog_UserCreatedBy]
FOREIGN KEY([CreatedBy]) REFERENCES [dbo].[User] ([UserID])
GO
ALTER TABLE [dbo].[AuditLog] CHECK CONSTRAINT [FK_AuditLog_UserCreatedBy]
GO
With something that big there are a couple of things you might try.
The first thing you need to do is define how you accessing the table MOST of the time and index accordingly.
I would hope you are not do a select * from AuditLog without any filtering for a reporting solution - it shouldn't even be an option.
Finally, instead of indexed views or partitioning, you might consider a partitioned view.
A partitioned view is basically breaking your table up, physically into smaller meaningful tables - based on date or type or object or however you are MOST often accessing it. Each table is then indexed separately giving you much better stats and if you in 2012 or higher you can take advantage of ColumnStore, assuming you use something like a DATE to group the data.
Create a view that spans all of the tables and then report based on the view. Since you already grouped your data by how you MOST often will access it, your filter will act similarly to partition exclusion and get you to your data faster.
Of course this will result in a little more maintenance and some code change, but be well worth the effort if you are storing that much data and more in a single table.

Dropping PRIMARY KEY constraint

I have created a table which has two columns combined as a primary key.
CREATE TABLE [dbo].[Workflow_Name]
(
[Workflow_ID] [int] NOT NULL,
[Unique_Workflow_ID] [int] NOT NULL,
[Workflow_Name] [varchar](255) NULL,
[Row_ID] [int] NULL,
[ReleaseVersion] [varchar](255) NULL,
[Release] [varchar](255) NULL,
CONSTRAINT [PK_WorkFlowName] PRIMARY KEY CLUSTERED
([Workflow_ID] ASC, [Unique_Workflow_ID] ASC )
)
As seen , [Workflow_ID] ASC, [Unique_Workflow_ID] ASC combined together are forming the Primary key.
Now i want to remove [Unique_Workflow_ID] from the Primary key constraint and maintain only [Workflow_ID] as Primary Key.
How to do it?
You can do it by executing the following statements in SSMS Query Window after selecting the database that the table is in.
ALTER TABLE [dbo].[Workflow_Name]
DROP CONSTRAINT [PK_WorkFlowName]
ALTER TABLE [dbo].[Workflow_Name]
ADD CONSTRAINT [PK_WorkFlowName] PRIMARY KEY ([Workflow_ID] ASC)
Please note, in order for it to work, if you have existing rows in the [Workflow_Name], then the data in this new single column Primary Key i.e. in [Workflow_ID] must be unique per row. Otherwise the ALTER statement will (rightly) throw an error that data is not unique in that column.
You can remove your primary key constraint and then create a new one only with the Workflow_ID. But make sure it has only unique values.

Foreign Key Relationship possible performance issues

We have a User table with a GUID aka uniqueidentifier as the PK. Nearly every other table in the database ties back to this table with 4 FK references. When I look at the DB diagram it looks like a 100 lane highway coming out of the User table because of the CreatedBy, CreatedByProxy, UpdatedBy, UpdatedByProxy foreign keys.
I was brought onto this project after it was already past inception and in production already, and with performance issues.
I was wondering if this DB pattern will cause major growing pains when the user list starts to grow. Are we going to run into more performance issues in the future because of this, or if we create an index will keeping the foreign keys cause the indexes to be huge. I just don't remember a website that I've worked on before that had the foreign keys to this extent before, and I'm worried about future proofing/fixing it. I'm really just trying to justify whether or not to keep or remove the foreign keys or modify the structure so that
User Table:
CREATE TABLE [dbo].[aspnet_Users](
[ApplicationId] [uniqueidentifier] NOT NULL,
[UserId] [uniqueidentifier] NOT NULL, -- ***** Here is the PK
[UserName] [nvarchar](256) NOT NULL,
[LoweredUserName] [nvarchar](256) NOT NULL,
[MobileAlias] [nvarchar](16) NULL,
[IsAnonymous] [bit] NOT NULL,
[LastActivityDate] [datetime] NOT NULL,
PRIMARY KEY NONCLUSTERED
(
[UserId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
One other Table that references User:
CREATE TABLE [dbo].[Email](
[EmailId] [int] IDENTITY(1,1) NOT NULL,
[PersonId] [int] NULL,
[InstitutionId] [int] NULL,
[EmailTypeId] [int] NOT NULL,
[EmailAddress] [varchar](254) NOT NULL,
[IsFlaggedImportant] [bit] NOT NULL,
[IsDistrictRecord] [bit] NOT NULL,
[IsActive] [bit] NOT NULL,
[Created] [datetime] NOT NULL,
[CreatedBy] [uniqueidentifier] NOT NULL, -- ***** FK 1
[Proxy] [uniqueidentifier] NULL, -- ***** FK 2
[Updated] [datetime] NULL,
[UpdatedBy] [uniqueidentifier] NULL, -- ***** FK 3
[UpdateProxy] [uniqueidentifier] NULL, -- ***** FK 4
CONSTRAINT [PK_Email] PRIMARY KEY CLUSTERED
(
[EmailId] 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].[Email] ADD CONSTRAINT [DF_Email_IsPrimary] DEFAULT ((0)) FOR [IsFlaggedImportant]
GO
ALTER TABLE [dbo].[Email] ADD CONSTRAINT [DF_Email_IsDistrictRecord] DEFAULT ((0)) FOR [IsDistrictRecord]
GO
ALTER TABLE [dbo].[Email] ADD CONSTRAINT [DF_Email_IsActive] DEFAULT ((0)) FOR [IsActive]
GO
ALTER TABLE [dbo].[Email] ADD CONSTRAINT [DF_Email_Created] DEFAULT (getdate()) FOR [Created]
GO
ALTER TABLE [dbo].[Email] WITH CHECK ADD CONSTRAINT [FK_Email_CreatedByUser] FOREIGN KEY([CreatedBy])
REFERENCES [dbo].[aspnet_Users] ([UserId])
GO
ALTER TABLE [dbo].[Email] CHECK CONSTRAINT [FK_Email_CreatedByUser]
GO
ALTER TABLE [dbo].[Email] WITH CHECK ADD CONSTRAINT [FK_Email_EmailType] FOREIGN KEY([EmailTypeId])
REFERENCES [dbo].[EmailType] ([EmailTypeId])
GO
ALTER TABLE [dbo].[Email] CHECK CONSTRAINT [FK_Email_EmailType]
GO
ALTER TABLE [dbo].[Email] WITH CHECK ADD CONSTRAINT [FK_Email_Institution] FOREIGN KEY([InstitutionId])
REFERENCES [dbo].[Institution] ([InstitutionId])
GO
ALTER TABLE [dbo].[Email] CHECK CONSTRAINT [FK_Email_Institution]
GO
ALTER TABLE [dbo].[Email] WITH CHECK ADD CONSTRAINT [FK_Email_Person] FOREIGN KEY([PersonId])
REFERENCES [dbo].[Person] ([PersonId])
GO
ALTER TABLE [dbo].[Email] CHECK CONSTRAINT [FK_Email_Person]
GO
ALTER TABLE [dbo].[Email] WITH CHECK ADD CONSTRAINT [FK_Email_ProxyByUser] FOREIGN KEY([Proxy])
REFERENCES [dbo].[aspnet_Users] ([UserId])
GO
ALTER TABLE [dbo].[Email] CHECK CONSTRAINT [FK_Email_ProxyByUser]
GO
ALTER TABLE [dbo].[Email] WITH CHECK ADD CONSTRAINT [FK_Email_ProxyUpdateByUser] FOREIGN KEY([UpdateProxy])
REFERENCES [dbo].[aspnet_Users] ([UserId])
GO
ALTER TABLE [dbo].[Email] CHECK CONSTRAINT [FK_Email_ProxyUpdateByUser]
GO
ALTER TABLE [dbo].[Email] WITH CHECK ADD CONSTRAINT [FK_Email_UpdatedByUser] FOREIGN KEY([UpdatedBy])
REFERENCES [dbo].[aspnet_Users] ([UserId])
GO
ALTER TABLE [dbo].[Email] CHECK CONSTRAINT [FK_Email_UpdatedByUser]
GO
ALTER TABLE [dbo].[Email] WITH CHECK ADD CONSTRAINT [CK_Email_Person_Or_Institution] CHECK (([PersonId] IS NOT NULL AND [InstitutionId] IS NULL OR [PersonId] IS NULL AND [InstitutionId] IS NOT NULL))
GO
ALTER TABLE [dbo].[Email] CHECK CONSTRAINT [CK_Email_Person_Or_Institution]
GO
I was brought onto this project after it was already past inception
and in production already, and with performance issues.
You don't say what those performance issues are, so I'll confine my remarks to the foreign keys.
When I look at the DB diagram it looks like a 100 lane highway coming
out of the User table because of the CreatedBy, CreatedByProxy,
UpdatedBy, UpdatedByProxy foreign keys.
When I see columns like that, I have to ask whether they contain information about the business entity--a person's email address in this case--or information about the row that entity happens to inhabit.
It looks like they contain information about the row. (But I could be wrong.)
If they do contain information about the row, and they're not needed in most queries, you can move them to another table. If you move them, you have to be more careful about inserting rows into dbo.Email.
CREATE TABLE [dbo].[Email](
[EmailId] [int] IDENTITY(1,1) NOT NULL,
[PersonId] [int] NULL,
[InstitutionId] [int] NULL,
[EmailTypeId] [int] NOT NULL,
[EmailAddress] [varchar](254) NOT NULL,
[IsFlaggedImportant] [bit] NOT NULL,
[IsDistrictRecord] [bit] NOT NULL,
[IsActive] [bit] NOT NULL,
CONSTRAINT [PK_Email] PRIMARY KEY CLUSTERED ([EmailID])
);
CREATE TABLE [dbo].[Email_audit](
[EmailID] [int] PRIMARY KEY REFERENCES [Email] ([EmailID]),
[Created] [datetime] NOT NULL,
[CreatedBy] [uniqueidentifier] NOT NULL, -- ***** FK 1
[Proxy] [uniqueidentifier] NULL, -- ***** FK 2
[Updated] [datetime] NULL,
[UpdatedBy] [uniqueidentifier] NULL, -- ***** FK 3
[UpdateProxy] [uniqueidentifier] NULL -- ***** FK 4
);
These kinds of tables are commonly used to provide some kind of audit trail. Whether you can cascade deletes is application-dependent. In some apps, you need to store the email address here instead of the email id number, and use no foreign key references. (This allows deleting rows from dbo.Email while retaining some information about what's happened to the row.)
Moving these columns reduces the width of a row in dbo.Email by about 80 bytes, not counting overhead. That usually improves performance of SELECT statements in the tables they're moved from. (Narrower rows; more rows per page.)
Moving these columns complicates inserting and updating rows, though. All inserts and updates have to hit two tables.

How can I force one to one relationship on SQL Server 2008 or 2008 R2

Here is my scenario on SQL Server 2008 R2:
This is my first table:
CREATE TABLE [dbo].[Foos](
[FooId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
CONSTRAINT [PK_Foos] PRIMARY KEY CLUSTERED
(
[FooId] ASC
)
) ON [PRIMARY]
This is the second table which has a relationship to Foos table:
CREATE TABLE [dbo].[Bars](
[BarId] [int] IDENTITY(1,1) NOT NULL,
[FooId] [int] NOT NULL,
[Name] [nvarchar](50) NULL,
CONSTRAINT [PK_Bars] PRIMARY KEY CLUSTERED
(
[BarId] ASC
)
) ON [PRIMARY]
Go
ALTER TABLE [dbo].[Bars] WITH CHECK ADD CONSTRAINT [FK_Bars_Foos] FOREIGN KEY([FooId])
REFERENCES [dbo].[Foos] ([FooId])
ON DELETE CASCADE
GO
But it is not one to one. What should I do to force this to be one to one relationship? Should I use check constraints?
Add a unique constraint to FooId in Bars.
However, you don't need BarID then because they have the same key. So it looks like this
CREATE TABLE [dbo].[Bars] (
[FooId] [int] NOT NULL,
[Name] [nvarchar](50) NULL,
CONSTRAINT [PK_Bars] PRIMARY KEY CLUSTERED (FooId),
CONSTRAINT [FK_Bars_Foos] FOREIGN KEY([FooId])
REFERENCES [dbo].[Foos] ([FooId])
ON DELETE CASCADE
)
GO
However again, you don't need Bars at all: it is one table...
You can keep Identity column(BarID) also. Then Unique key will help you out from this problem.
IF NOT EXISTS(SELECT OBJECT_ID from sys.objects WHERE name ='foo_bars')
alter table bars add constraint foo_bars unique(fooid)

Resources