Best Practice for a scheduled stored procedure - sql

I have a stored procedure that takes user input from a webform and updates a database.
CREATE TABLE AccountTable
(
RowID int IDENTITY(1, 1),
AccountID varchar(2),
AccountName varchar(50),
SeqNum int,
SeqDate datetime
)
CREATE PROCEDURE [ACCOUNTTABLE_UPDATE]
(
#SeqNum int,
#SeqDate datetime,
#Account_ID varchar(2)
)
AS
SET NOCOUNT ON
BEGIN
UPDATE AccountTable
SET SeqNum = #SeqNum, SeqDate = #SeqDate
WHERE AccountID = #AccountID
END
Each time the user runs the webapp, the table updates the SeqNum and SeqDate columns. I would like to have the SeqNum column reset after ever 24 hours to NULL. Would just putting in the stored procedure checking if the current date is greater than the dates column be ideal or implementing a scheduled task?

Simply try running your SP as Sql Agent service's task.

If you really want to this I think you should use SQL Agent for scheduling.

Related

How can I do what the trigger does with the store procedure?

I have a trigger on my tbl_permissions table.
Trigger is;
ALTER TRIGGER [dbo].[trig_permissions] ON [dbo].[tbl_permissions]
FOR UPDATE
AS
IF(UPDATE(email) OR UPDATE(gsm))
BEGIN
INSERT INTO dbo.tbl_permissions_log
(customer_id,type,email_new_value,email_old_value,gsm_new_value,gsm_oldu_value,modify_user_id,modify_date)
SELECT
i.customer_id,
d.type,
i.email,
d.email,
i.gsm,
d.gsm,
i.modify_user_id,
GETDATE()
FROM inserted i, deleted d ,dbo.tbl_permissions c
WHERE c.pk_id = i.pk_id AND c.pk_id = d.pk_id AND
(RTRIM(d.email) <> RTRIM(i.email)
OR (RTRIM(d.gsm) <> RTRIM(i.gsm)))
The trigger is triggering in 2 store procedure.
First One;
ALTER PROC [dbo].[sp_activation_email_update]
(
#sEmail NVARCHAR(100),
#lModifyUserId INT,
#bEML BIT
)
AS
BEGIN
UPDATE dbo.tbl_permissions
SET email=#bEML,
modify_user_id=#lModifyUserId,
modify_date=GETDATE()
WHERE customer_id
IN(SELECT customer_id FROM dbo.tbl_contact_info WHERE email=#sEmail)
END
Second;
ALTER PROC [dbo].[sp_activation_sms_update]
(
#sGsmNo NVARCHAR(15),
#lModifyUserId INT,
#bGsm BIT
)
AS
BEGIN
UPDATE dbo.tbl_permissions
SET gsm=#bGsm,
modify_user_id=#lModifyUserId,
modify_date=GETDATE()
WHERE customer_id
IN(SELECT customer_id FROM dbo.tbl_contact_info WHERE gsm_no=RIGHT(#sGsmNo, 10))
END
I want to remove trigger because of performance problem. So How can I perform the work of the trigger in the store procedure?
I tried the call another store procedure inside update store procedures and perform operations in the this new store procedure but I cant.
In your stored procedures, you can use the OUTPUT clause to capture data from the UPDATE statement, and then use that captured data to insert rows into the log table.
Something like the following:
DECLARE #Updated TABLE (pk_id int, newEmail, oldEmail, modify_user_id int)
UPDATE dbo.tbl_permissions
SET email=#bEML,
modify_user_id=#lModifyUserId,
modify_date=GETDATE()
OUTPUT inserted.pk_id, inserted.email, deleted.email, u.modify_user_id
INTO #Updated
WHERE customer_id
IN(SELECT customer_id FROM dbo.tbl_contact_info WHERE email=#sEmail)
INSERT INTO dbo.tbl_permissions_log
(customer_id,type,email_new_value,email_old_value,gsm_new_value,gsm_oldu_value,modify_user_id,modify_date)
SELECT
p.customer_id,
p.type,
u.newEmail,
u.oldEmail,
p.gsm,
p.gsm,
u.modify_user_id,
GETDATE()
FROM #Updated u
JOIN dbo.tbl_permissions p ON p.pk_id = u.pk_id
WHERE u.newEmail <> u.oldEmail
You can also direct output directly into the log table, but the statement is getting pretty cluttered at that point.
UPDATE dbo.tbl_permissions
SET email=#bEML,
modify_user_id=#lModifyUserId,
modify_date=GETDATE()
OUTPUT
inserted.customer_id,
inserted.type,
inserted.email,
deleted.email,
inserted.gsm,
deleted.gsm,
inserted.modify_user_id,
GETDATE()
INTO dbo.tbl_permissions_log
(customer_id,type,email_new_value,email_old_value,gsm_new_value,gsm_oldu_value,modify_user_id,modify_date)
WHERE customer_id
IN(SELECT customer_id FROM dbo.tbl_contact_info WHERE email=#sEmail)
AND email <> #bEML
In both cases we limit the log insert to cases where the updated value actually changed. In the latter case, this necessitates applying that condition to the actual update statement. This would modify the original behavior by also inhibiting the update to modify_user_id and modify_date when email is unchanged. (This might be a positive change.)

SQL Stored Procedure Simultaneously Call Issue

I have stored procedure in the sql server 2008, my stored procedure calculate and get the last number "not primary key" from column from table B and add one ( +1 ) to this number to use it on the next statement on the same stored procedure.
My issue that i have a duplicate number some times, i think this happened when multiple users call the stored procedure on the same time. is this the issue and how can i solve it
my code is like the below:-
DECLARE #ID AS NVARCHAR(10)
SET #ID = (
SELECT TOP 1 MyNo
FROM Employee
WHERE (
(TypeID = #TypeID) AND
(Year = #Year)
)
ORDER BY ID DESC
)
SET #ID = ISNULL(#ID,0) + 1
INSERT INTO Employee (name,lname,MyNo) VALUES (#name,#lname,#MyNo)
You can lock a table for the duration of a transaction with the WITH (TABLOCKX, HOLDLOCK) syntax:
BEGIN TRANSACTION
DECLARE #ID AS NVARCHAR(10)
SET #ID = (
SELECT TOP 1 MyNo
FROM Employee WITH (TABLOCKX, HOLDLOCK)
WHERE (
(TypeID = #TypeID) AND
(Year = #Year)
)
ORDER BY ID DESC
)
SET #ID = ISNULL(#ID,0) + 1
INSERT INTO Employee (name,lname,MyNo) VALUES (#name,#lname,#MyNo)
COMMIT TRANSACTION
You can find more information about TABLOCK and TABLOCKX here:
https://learn.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql-table
Per discussion, the best lock to use in this case would be:
(UPDLOCK,HOLDLOCK)
If you cannot use Identity column or the Table lock, another alternative is to use sp_getapplock
The advantage with this mechanism is that this kind of lock can be used across multiple stored procedures that should not run concurrently or for operations that span multiple tables. It also allows for handling timeout and other kinds of behavior if the lock is not available.
You have to be careful when using this feature and ensure you acquire and release locks properly or you will create more problems than you solve.

Stored procedure that calls another stored procedure filters columns by partial name, then compares the second SP's return number with column amounts

The goal is to get the amount of passwords the user has, compare it with the company's rules. If it exceeds the number they allow, we insert the new one (with the date) and delete the last one (or delete the last one then insert the new one. I honestly have figured out which way I want to go yet).
I have two stored procedures: Delete_Oldest_Password_And_Date and Number_Of_Passwords
Delete_Oldest_Password_And_Date calls Number_Of_Passwords because I need that amount.
I'm pretty sure the beginning of my stored procedure is good. It's the second half, starting with #Temp_Password_Table.
CREATE Procedure Delete_Oldest_Password_And_Date
#ua_pk uniqueidentifier
AS
DECLARE #Temp_Table table
(
numberOfPasswords INT
)
INSERT INTO #Temp_Table
EXEC Number_Of_Passwords #ua_pk
SELECT *
FROM
#Temp_Table
DECLARE #Temp_Password_Table table
(
password varchar(max)
)
SELECT *
FROM User_Passwords
WHERE User_Passwords.ua_fk = #ua_pk
AND up_Password LIKE '%Password%'
GO
GRANT EXEC ON Delete_Oldest_Password_And_Date TO WEB
GO
I'm not worried about the delete just yet. I'm more worried about getting the correct information first.
Keep in mind User_Passwords is dynamic per person. The plan (I haven't gotten to this part yet) is to have the newest go into a column named "up_Password1", then the second newest into column "up_Password2", etc.
Here's a proc that will check the number of passwords a user has, and will delete the oldest password(s) until there are Max#ofPasswords-1, thus setting up for a new password to be inserted that still follows the policy for number of historical passwords. The proc assumes the following table:
CREATE TABLE dbo.User_Passwords(
ua_fk UNIQUEIDENTIFIER,
password_hash NVARCHAR(100),
start_date DATETIME,
end_date DATETIME)
Password_hash can be whatever you decide to store the password column as (just please don't store actual passwords in plain text here).
CREATE Procedure Delete_Oldest_Password_And_Date
#ua_pk uniqueidentifier
AS
DECLARE #numberOfPasswords INT,
#MaxPasswordNum INT = 10
SELECT #numberOfPasswords = COUNT(*)
FROM User_Passwords
WHERE ua_fk = #ua_pk
IF #numberOfPasswords >= #MaxPasswordNum
BEGIN
WITH T
AS (SELECT TOP (#numberOfPasswords - (#MaxPasswordNum-1)) *
FROM User_Passwords
WHERE ua_fk = #ua_pk
ORDER BY end_date ASC)
DELETE FROM T;
END
GO

Stored procedure timing out on particular connection pool

I have a stored procedure which occasionally times out when called from our website (through the website connection pool). Once it has timed out, it has always been locked into the time-out, until the procedure is recompiled using drop/create or sp_recompile from a Management Studio session.
While it is timing out, there is no time-out using the same parameters for the same procedure using Management Studio.
Doing an "ALTER PROCEDURE" through Management Studio and (fairly drastically) changing the internal execution of the procedure did NOT clear the time out - it wouldn't clear until a full sp_recompile was run.
The stored procedure ends with OPTION (RECOMPILE)
The procedure calls two functions, which are used ubiquitously throughout the rest of the product. The other procedures which use these functions (in similar ways) all work, even during a period where the procedure in question is timing out.
If anyone can offer any further advice as to what could be causing this time out it would be greatly appreciated.
The stored procedure is as below:
ALTER PROCEDURE [dbo].[sp_g_VentureDealsCountSizeByYear] (
#DateFrom AS DATETIME = NULL
,#DateTo AS DATETIME = NULL
,#ProductRegion AS INT = NULL
,#PortFirmID AS INT = NULL
,#InvFirmID AS INT = NULL
,#SpecFndID AS INT = NULL
) AS BEGIN
-- Returns the stats used for Market Overview
DECLARE #IDs AS IDLIST
INSERT INTO #IDs
SELECT IDs
FROM dbo.fn_VentureDealIDs(#DateFrom,#DateTo,#ProductRegion,#PortFirmID,#InvFirmID,#SpecFndID)
CREATE TABLE #DealSizes (VentureID INT, DealYear INT, DealQuarter INT, DealSize_USD DECIMAL(18,2))
INSERT INTO #DealSizes
SELECT vDSQ.VentureID, vDSQ.DealYear, vDSQ.DealQuarter, vDSQ.DealSize_USD
FROM dbo.fn_VentureDealsSizeAndQuarter(#IDs) vDSQ
SELECT
yrs.Years Heading
,COUNT(vDSQ.VentureID) AS Num_Deals
,SUM(vDSQ.DealSize_USD) AS DealSize_USD
FROM tblYears yrs
LEFT OUTER JOIN #DealSizes vDSQ ON vDSQ.DealYear = yrs.Years
WHERE (
((#DateFrom IS NULL) AND (yrs.Years >= (SELECT MIN(DealYear) FROM #DealSizes))) -- If no minimum year has been passed through, take all years from the first year found to the present.
OR
((#DateFrom IS NOT NULL) AND (yrs.Years >= DATEPART(YEAR,#DateFrom))) -- If a minimum year has been passed through, take all years from that specified to the present.
) AND (
((#DateTo IS NULL) AND (yrs.Years <= (SELECT MAX(DealYear) FROM #DealSizes))) -- If no maximum year has been passed through, take all years up to the last year found.
OR
((#DateTo IS NOT NULL) AND (yrs.Years <= DATEPART(YEAR,#DateTo))) -- If a maximum year has been passed through, take all years up to that year.
)
GROUP BY yrs.Years
ORDER BY Heading DESC
OPTION (RECOMPILE)
END
If you wanted to recompile SP each time it is executed, you should have declared it with recompile; your syntax recompiles last select only:
ALTER PROCEDURE [dbo].[sp_g_VentureDealsCountSizeByYear] (
#DateFrom AS DATETIME = NULL
,#DateTo AS DATETIME = NULL
,#ProductRegion AS INT = NULL
,#PortFirmID AS INT = NULL
,#InvFirmID AS INT = NULL
,#SpecFndID AS INT = NULL
) WITH RECOMPILE
I could not tell which part of your procedure causes problems. You might try commenting out select part to see if creating temp tables from table functions produces performance issue; if it does not, then the query itself is a problem. You might rewrite filter as following:
WHERE (#DateFrom IS NULL OR yrs.Years >= DATEPART(YEAR,#DateFrom))
AND (#DateTo IS NULL OR yrs.Years <= DATEPART(YEAR,#DateTo))
Or, perhaps better, declare startYear and endYear variables, set them accordingly and change where like this:
declare #startYear int
set #startYear = isnull (year(#DateFrom), (SELECT MIN(DealYear) FROM #DealSizes))
declare #endYear int
set #endYear = isnull (year(#DateTo), (SELECT MAX(DealYear) FROM #DealSizes))
...
where yrs.Year between #startYear and #endYear
If WITH RECOMPILE does not solve the problem, and removing last query does not help either, then you need to check table functions you use to gather data.

SQL Server 2008 - Stored Procedure Gets Stuck When Called From WPF Service Using a SqlDataAdapter

This has me pulling my hair out. We have a workflow, hosted as a WCF service, which makes a call to another WCF service which then calls a stored procedure. Store procedure calls a merge, then iterates through a cursor that calls another sproc. The cursor count is the same as the source count in the merge. If the source count is high (~120k), the sproc never returns. Disk activity and CPU utilization is nill, and memory is not being taxed. If I then call the sproc from SSMS, it completes in about an hour.
We are using a SQLDataAdapter to make the actual call. Is the SDA somehow receiving updates on every cursor iteration and then failing, causing SQL to stall as it waits? Or is something else going on?
I bring up the SDA because I spent the early part of the week tracking down the cause of the workflow faulting, and it turned out it was a repeated ANSI warning message being returned to the SDA and causing an out of memory exception. Which leads me to wonder if there is something else going on under the cover here, causing the problem.
I seriously doubt the WCF host is the appropriate environment to hold a database call for one hour duration... I recommend you have a look at Asynchronous procedure execution which allows you to make long database calls from and ASP/WCF process in a reliable fashion, and call let the HTTP call return back to the caller.
It's hard to tell what your code is actually doing, but you mention that your stored proc is using cursors and that your stored proc is slow. I write my stored procs to use temporary tables, and I iterate over the temporary tables as the following example indicates. This made my procs run in seconds to minutes when they use to take minutes to hours when I used cursors. If you are using cursors and profiling indicates the cursor code in your stored proc is slow give temporary tables a try.
You'll never got back to cursors.
-- create a temporary table
DECLARE #FrontDeskRows table (
id int,
Arrival datetime,
Departure datetime,
CheckedIn int,
OwnerID varchar(50),
GuestID varchar(50),
[LName] varchar (256),
[FName] varchar (256),
[Address] varchar (256),
[City] varchar (256),
[State] varchar (256),
[Zip] varchar (256),
[phone] varchar (256),
[Status] int )
-- load your temporary table
INSERT INTO #FrontDeskRows
SELECT id,
Arrival,
Departure,
CheckedIn,
OwnerID,
GuestID,
[LName],
[FName],
[Address],
[City],
[State],
[Zip],
[phone],
[Status]
FROM FrontDesk
ORDER BY Id ASC
DECLARE #arrival as DateTime
DECLARE #departure as DateTime
DECLARE #id as int
-- loop over the temprary table
SELECT #id = (SELECT MIN(id) FROM #FrontDeskRows)
SELECT #arrival = Arrival FROM #FrontDeskRows Where id = #id
SELECT #departure = Departure FROM #FrontDeskRows Where id = #id
WHILE #id IS NOT NULL
BEGIN
-- PROCESS EACH ROW HERE
-- get the next item in the temporary table
SELECT #id = (SELECT MIN(id) FROM #FrontDeskRows WHERE id > #id)
SELECT #arrival = Arrival FROM #FrontDeskRows Where id = #id
SELECT #departure = Departure FROM #FrontDeskRows Where id = #id
END
Turns out it was a very bad case of parameter sniffing. Very, very bad case...