sql statement problem when running multiple executions simultaneously - sql-server-2005

The problem is whenever I execute the stored procedured "usp_Execute100K" in the Query Windows simultaneously the server could not locate the record and it creating a new record even though the record is already there. You have to run the "usp_Execute100K" multiple windows simultaneously to see this problem.
What I want is to make sure the server only create 1 record per day, if the record does exist then only update the "StatisticTotal" column to the next number.
This problem is taught.
The below are 1 table and two stored procedures
Statistic /Table name/
usp_Statistic_InsertOrUpdate /this will update the "StatisticTotal" column or insert if the record does not exist/
usp_Execute100K /* this will execute the usp_Statistic_InsertOrUpdate 100 thousand times*/
PLEASE COPY THE SCRIPT BELOW TO RECREATE THE TABLE AND STORED PROCEDURES.
GO
/****** Object: StoredProcedure [dbo].[usp_Execute100K] Script Date: 09/07/2011 15:37:29 ******/
DROP PROCEDURE [dbo].[usp_Execute100K]
GO
/****** Object: StoredProcedure [dbo].[usp_Statistic_InsertOrUpdate] Script Date: 09/07/2011 15:37:29 ******/
DROP PROCEDURE [dbo].[usp_Statistic_InsertOrUpdate]
GO
/****** Object: Table [dbo].[Statistic] Script Date: 09/07/2011 15:37:28 ******/
DROP TABLE [dbo].[Statistic]
GO
/****** Object: Table [dbo].[Statistic] Script Date: 09/07/2011 15:37:28 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Statistic](
[StatisticID] [uniqueidentifier] NOT NULL,
[StatisticAccount] [uniqueidentifier] NOT NULL,
[StatisticTotal] [float] NOT NULL,
[StatisticCreatedDate] [datetime] NOT NULL,
[DebugDate] [datetime] NULL,
CONSTRAINT [PK_Statistics] PRIMARY KEY CLUSTERED
(
[StatisticID] 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
/****** Object: StoredProcedure [dbo].[usp_Statistic_InsertOrUpdate] Script Date: 09/07/2011 15:37:29 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[usp_Statistic_InsertOrUpdate](
#StatisticAccount uniqueidentifier
)
AS
SET NOCOUNT OFF; --this can be turn on or off, it has no effects
DECLARE #NowDate date
SET #NowDate=CONVERT(datetime, CONVERT(char, GETDATE(), 106))--remove all time.
--UPDATE ONLY IF IT HAS THE SAME DATE
UPDATE TOP (1) Statistic SET StatisticTotal =StatisticTotal+1 WHERE (StatisticAccount=#StatisticAccount AND StatisticCreatedDate = #NowDate)
if ##ROWCOUNT=0--If the above statement return no effects then create a new record
BEGIN
INSERT TOP (1)INTO Statistic(StatisticID, StatisticAccount, StatisticTotal, StatisticCreatedDate,DebugDate) VALUES (NEWID(),#StatisticAccount,1,#NowDate,#NowDate)
END
GO
/****** Object: StoredProcedure [dbo].[usp_Execute100K] Script Date: 09/07/2011 15:37:29 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[usp_Execute100K]
AS
SET NOCOUNT OFF; --this can be turn on or off, it has no effects
declare #current float
set #current=0
while (#current <100000)
begin
--INSERT THIS STATEMENT for 100 thousand times
exec usp_Statistic_InsertOrUpdate'4c34eea5-fe17-4b11-8e06-0039577e7421'
set #current = #current + 1
end
GO

Have you considered wrapping your UPDATE/INSERT combo in a transaction? While the constraint and TRY/CATCH will certainly prevent a second row, it will discard the attempt as well. If you wrap it in a transaction, and use TABLOCK (I assume you are potentially doing a lot of writes to this table, but not a ton of reads), you hamper concurrency slightly but you can better ensure that one user will only see the state of the table after the other user is done with it. I could get two rows reliably as is, but I couldn't get two rows when I used this:
ALTER PROCEDURE [dbo].[usp_Statistic_InsertOrUpdate]
#StatisticAccount UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON; -- this should always be on
DECLARE #NowDate DATE = CURRENT_TIMESTAMP;
-- by definition, you don't need to convert to remove time from date
BEGIN TRANSACTION;
--UPDATE ONLY IF IT HAS THE SAME DATE
UPDATE dbo.Statistic WITH (TABLOCK)
SET StatisticTotal += 1
WHERE StatisticAccount = #StatisticAccount
AND StatisticCreatedDate = #NowDate;
IF ##ROWCOUNT = 0
-- If the above statement return no effects then create a new record
BEGIN
INSERT dbo.Statistic
(
StatisticID,
StatisticAccount,
StatisticTotal,
StatisticCreatedDate,
DebugDate
)
SELECT
NEWID(),
#StatisticAccount,
1,
#NowDate,
#NowDate;
END
COMMIT TRANSACTION;
END
GO
Error trapping, rollback, constraints etc. I'll leave as an exercise.

1) create a unique index on Statistic.StatisticAccount + Statistic.StatisticCreatedDate or just Statistic.StatisticCreatedDate what ever your business rules dictate.
2) wrap your INSERT WITH:
BEGIN TRY
INSERT ...
END TRY
BEGIN CATCH
END CATCH
it will just ignore/throw away the error generated by the failed duplicate insert.

Related

Update stored procedure using C#?

I have 10 database servers and most procedures are the same.
So I plan to make procedure distribute program.
For convenient application, I want to use "the procedure modify code made by tool" itself.
For example, when I click modify button of the procedure on SSMS the code is like below.
USE [DB]
GO
/****** Object: StoredProcedure [dbo].[HongTestProcedure] Script Date: 2020-08-28 오전 11:09:02 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: hong
-- Create date: 2020-03-07
-- =============================================
ALTER PROCEDURE [dbo].[HongTestProcedure]
-- Add the parameters for the stored procedure here
#ID varchar(10)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT *
FROM table
END
And then, if I want to modify the parameter of the procedure.
Maybe the code is like below:
USE [DB]
GO
/****** Object: StoredProcedure [dbo].[HongTestProcedure] Script Date: 2020-08-28 오전 11:09:02 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: hong
-- Create date: 2020-03-07
-- =============================================
ALTER PROCEDURE [dbo].[HongTestProcedure]
-- Add the parameters for the stored procedure here
#ID varchar(10)
#ID2 varchar(10) -- it is added.
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT *
FROM table
END
I consider that program has a textbox and button.
and I want to insert all above code.
maybe it looks like it:
So, I try the code like below:
public bool UpdateProcedure(string dbip, string query)
{
DataSet ds = new DataSet();
SqlConnection sqlConn = new SqlConnection("server = " + dbip + dbInfo);
sqlConn.Open();
SqlCommand sqlComm = new SqlCommand(query, sqlConn);
sqlComm.ExecuteNonQuery();
return true;
}
If it run successfully, I will run query on 10 database servers.
But it return error message. even it can be run in SSMS.
incorrect syntax near 'GO'
CREATE/ALTER PROCEDURE must be the first statement in a query batch
Can I solve it?
Yes. Since you can construct it as an Dynamic SQL in C# and Open an SQL Connection and pass the Dynamic SQL to EXEC sp_executesql #DynamicSQL. This will create the required SP in the required Database.

How to add header comments when altering stored procedures in SQL Server

I have a stored procedure where there are no header comments. I want to add them, but whenever I try, it is not included.
In SQL Server Management Studio I :
1.Right-click my stored procedure and click modify
USE [ABigDB]
GO
/****** Object: StoredProcedure [dbo].[spDoWork] Script Date: 21/08/2015 14:11:45 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[spDoWork]
#Id uniqueidentifier,
#Session nvarchar(50),
#XMLData xml
WITH EXECUTE AS OWNER
AS
BEGIN
--etc etc...
END
2.I paste comments above the stored procedure and run the script :
-- Stored Procedure
-- Author: Dave
-- Create date: 21/08/2015
-- Description: Does Stuff
-- Change history
-- 07/08/2015 - Overlord - Done stuff
-- 06/08/2015 - Kerrigan - Done more stuff
USE [ABigDB]
GO
/****** Object: StoredProcedure [dbo].[spDoWork] Script Date: 21/08/2015 14:11:45 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[spDoWork]
#Id uniqueidentifier,
#Session nvarchar(50),
#XMLData xml
WITH EXECUTE AS OWNER
AS
BEGIN
--etc etc...
END
3.When I modify the same stored procedure it appears as :
USE [ABigDB]
GO
/****** Object: StoredProcedure [dbo].[spDoWork] Script Date: 21/08/2015 14:11:45 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[spDoWork]
#Id uniqueidentifier,
#Session nvarchar(50),
#XMLData xml
WITH EXECUTE AS OWNER
AS
BEGIN
--etc etc...
END
So how do I get the comments to appear there?
I solved it by doing the following:
USE [ABigDB]
GO
/****** Object: StoredProcedure [dbo].[spDoWork] Script Date: 21/08/2015 14:11:45 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- Stored Procedure
-- Author: Dave
-- Create date: 21/08/2015
-- Description: Does Stuff
-- Change history
-- 07/08/2015 - Overlord - Done stuff
-- 06/08/2015 - Kerrigan - Done more stuff
ALTER PROCEDURE [dbo].[spDoWork]
#Id uniqueidentifier,
#Session nvarchar(50),
#XMLData xml
WITH EXECUTE AS OWNER
AS
BEGIN
--etc etc...
END
Consider using the meta data in addition to your procedures, tables, columns, etc. for documentation purposes.
See the following that helps when reviewing your db objects.
Is it possible to add a description/comment to a table in Microsoft SQL 2000+

SQL stored procedure change logging trigger not saving all text

I created a stored procedure to log changes made to stored procedures. The issue is that the larger ones aren't being saved in their entirety and are being chopped off.
I need to be able to save the entire procedure text and it is also used as a means of reverting the a previous revision.
The trigger:
/****** Object: DdlTrigger [StoredProcUpdateInsert] Script Date: 12/05/2015 14:05:05 ******/
DROP TRIGGER [StoredProcUpdateInsert] ON DATABASE
GO
/****** Object: DdlTrigger [StoredProcUpdateInsert] Script Date: 12/05/2015 14:05:05 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [StoredProcUpdateInsert]
ON DATABASE
FOR CREATE_PROCEDURE, ALTER_PROCEDURE
AS
BEGIN
SET NOCOUNT ON
DECLARE #data XML
SET #data=EVENTDATA()
INSERT INTO dbo.ProcedureChanges
( ProcName ,
ProcText ,
ModifiedBy ,
DateTimeLastUpdated
)
VALUES ( #data.value('(/EVENT_INSTANCE/ObjectName)[1]','nvarchar(max)') , -- ProcName - nvarchar(450)
(SELECT TOP 1 text FROM syscomments WHERE id=OBJECT_ID(#data.value('(/EVENT_INSTANCE/ObjectName)[1]','nvarchar(max)'))) , -- ProcText - nvarchar(max)
#data.value('(/EVENT_INSTANCE/LoginName)[1]','nvarchar(max)') , -- ModifiedBy - nvarchar(250)
GETDATE() -- DateTimeLastUpdated - datetime
)
END
GO
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER OFF
GO
ENABLE TRIGGER [StoredProcUpdateInsert] ON DATABASE
GO
According to this article: https://msdn.microsoft.com/en-us/library/ms186293.aspx you shouldn't use syscommentsand instead should use sys.sqlmodules
The definition of syscomments.textfrom the article above says it's defined as nvarchar(4000). Presumably this means any DDL longer than 4000 characters is truncated and therefore you're only getting the first 4000 characters in your log table.
I haven't tested this but you could try:
INSERT INTO dbo.ProcedureChanges
( ProcName ,
ProcText ,
ModifiedBy ,
DateTimeLastUpdated
)
VALUES ( #data.value('(/EVENT_INSTANCE/ObjectName)[1]','nvarchar(max)') , -- ProcName - nvarchar(450)
(SELECT Definition FROM sys.sql_modules WHERE object_id=OBJECT_ID(#data.value('(/EVENT_INSTANCE/ObjectName)[1]','nvarchar(max)'))) , -- ProcText - nvarchar(max)
#data.value('(/EVENT_INSTANCE/LoginName)[1]','nvarchar(max)') , -- ModifiedBy - nvarchar(250)
GETDATE() -- DateTimeLastUpdated - datetime
)

SQLDependency throwing thousands of invalid notifications with stored proc

I've built a vb.net service to update Exchange appointments as appointment changes are made in our accounting suite. Everything works fine if I run the SQL query straight from VB.net but if I reference a stored proc with the exact same code I receive thousands of "invalid" notifications. I was wondering if anyone could tell me why?
I'm using VS2012, SQL Server 2008, and .NET 4.0
I pass each "option" into the query string and execute it before starting the dependency with this query:
select Dispatch_Id, Schedule_Time, Dispatch_Time, Arrival_Time, Departure_Time from dbo.Ticket_Dispatch
The stored proc is:
USE [database]
GO
/****** Object: StoredProcedure [dbo].[watch] Script Date: 1/22/2014 5:28:28 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:
-- Create date:
-- Description:
-- =============================================
ALTER PROCEDURE [dbo].[watch]
-- Add the parameters for the stored procedure here
AS
BEGIN
SET ANSI_NULLS ON;
SET ANSI_PADDING ON;
SET ANSI_WARNINGS ON;
SET CONCAT_NULL_YIELDS_NULL ON;
SET QUOTED_IDENTIFIER ON;
SET NUMERIC_ROUNDABORT ON;
SET ARITHABORT ON;
select Dispatch_Id, Schedule_Time, Dispatch_Time, Arrival_Time, Departure_Time from dbo.Ticket_Dispatch
END
Before any of this I test for permissions. The user has ownership over the table and the proc so there shouldn't be any problems there.

Mix Create Stored Procedure and Insert Statements in Sql Server

I created a number of scripts to be run separately but was asked to combine them all so the DBA only has to do it once. The problem is that I cannot seem to combine them to run together. Only the first item in the query gets run. How do I format these to run together in one big script?
USE [DEV]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
Create PROCEDURE [dbo].[Projects]
#ProjectID int,
#ClientID int
AS
BEGIN
.....Cool procedure here
END
GRANT EXECUTE ON [dbo].[Projects] TO Admin, Employee
INSERT INTO random_table(stuff)
VALUES (stuff)
Add a GO between statements
USE [DEV]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
Create PROCEDURE [dbo].[Projects]
#ProjectID int,
#ClientID int
AS
BEGIN
.....Cool procedure here
END
GO -- Add GO here
GRANT EXECUTE ON [dbo].[Projects] TO Admin, Employee
GO -- Add GO here
INSERT INTO random_table(stuff)
VALUES (stuff)
USE [DEV]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
Create PROCEDURE [dbo].[Projects]
#ProjectID int,
#ClientID int
AS
BEGIN
.....Cool procedure here
END
GRANT EXECUTE ON [dbo].[Projects] TO Admin, Employee
GO -- added this "go" statement
INSERT INTO random_table(stuff)
VALUES (stuff)
Insert GO after each statement.
USE [DEV]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
Create PROCEDURE [dbo].[Projects]
#ProjectID int,
#ClientID int
AS
BEGIN
.....Cool procedure here
END
GO //add Go after every statement
GRANT EXECUTE ON [dbo].[Projects] TO Admin, Employee
GO
INSERT INTO random_table(stuff)
VALUES (stuff)
Although, I would recommend you to generate script of Database schema (including Stored procedures, functions, table creation, insertion, updation and deletion) from your SQL server database and save it with .SQL file and you don't have to place these GOs manually. Take a look at this fine example