Update existing value in database using Stored Procedure - sql

I want to update my table contentment, which has the following attributes:
employeeid, questionid, date, score, comment
The words in bold are the primary key of this table. My stored procedure needs to update a questionid for a employeeid. Only the questionid needs to be changed. The employeeid, date, score and comment need to stay the same.
I have the following:
create procedure [dbo].[spUpdateContentment]
(
#employeeid int,
#questionid int,
#date date
)
as
begin
update contentment
set questionid= #questionid
where employeeid= #employeeid and questionid= #questionid and date = #date
else
RAISERROR(#ErrMsg,16,1)
end
But this is not working, it does nothing. I think this is because my stored procedure does not exactly know where he needs to update it, and which questionid he needs to update. But I'm not sure. I use SQL Server

You are not changing any values, therefore it does nothing. You may need the following:
create procedure [dbo].[spUpdateContentment]
(
#employeeid int,
#new_questionid int,
#old_questionid int,
#date date
)
as
begin
update contentment
set questionid= #new_questionid
where employeeid= #employeeid and questionid= #old_questionid and date = #date
else
RAISERROR(#ErrMsg,16,1)
end

I think you want:
update contentment
set questionid = #questionid
where employeeid = #employeeid and date = #date;
The condition and questionid = #questionid is going to prevent the row being found. If it is found, the update won't be changing any values.
It is possible that the logic requires two #questionids, one for the old value and one for the new value.

Related

New to SQL - Why is my Insert into trying to insert NULL into primary key?

What I want to do is insert a range of dates into multiple rows for customerID=1. I have and insert for dbo.Customer(Dates), specifying my that I want to insert a record into the Dates column for my Customer table, right? I am getting error:
Cannot insert the value NULL into column 'CustomerId', table 'dbo.Customers'
Sorry if I am way off track here. I have looked at similar threads to find out what I am missing, but I'm not piecing this together. I am thinking it wants to overwrite the existing customer ID as NULL, but I am unsure why exactly since I'm specifying dbo.Customer(Dates) and not the existing customerID for that record.
declare #date_Start datetime = '03/01/2011'
declare #date_End datetime = '10/30/2011'
declare #date datetime = #date_Start
while #date <= #date_End
begin
insert into dbo.Customer(Dates) select #date
if DATEPART(dd,#date) = 0
set #date = DATEADD(dd, -1, DATEADD(mm,1,#date))
else
set #date = DATEADD(dd,1,#date)
end
select * from dbo.Customer
The primary key is customerId, but you are not inserting a value.
My guess is that you declared it as a primary key with something like this:
customerId int primary key,
You want it to be an identity column, so the database assigns a value:
customerId int identity(1, 1) primary key
Then, you don't need to assign a value into the column when you insert a new row -- the database does it for you.
Your Customer table has a column named CustomerId and which column is NOT Nullable so you have to provide that column value as well. If your column type is Int try the bellow code:
declare #date_Start datetime = '03/01/2011'
declare #date_End datetime = '10/30/2011'
declare #date datetime = #date_Start
DECLARE #cusId INT
SET #cusId = 1
while #date <= #date_End
begin
insert into dbo.Customer(CustomerId, Dates) select #cusId, #date
if DATEPART(dd,#date) = 0
set #date = DATEADD(dd, -1, DATEADD(mm,1,#date))
else
set #date = DATEADD(dd,1,#date)
SET #cusId = #cusId + 1;
end
select * from dbo.Customer
thank you for the feedback. I think I'm scrapping this and going to go with creating a separate table to JOIN. Not sure why I didn't start doing that before

Is it safe to save previous value of a column as a variable in update?

Think of a simple update stored procedure like this:
CREATE PROCEDURE [UpdateMyTable] (
#Id int,
#ModifiedOn datetime,
#GeneratedOn datetime
)
AS
UPDATE
[MyTable]
SET
[ModifiedOn] = #ModifiedOn
WHERE
[Id] = #Id AND [ModifiedOn] <= #GeneratedOn
Now, to return a result based on previous value of ModifiedOn, I changed it like this:
ALTER PROCEDURE [UpdateMyTable] (
#Id int,
#ModifiedOn datetime,
#GeneratedOn datetime
)
AS
DECLARE #PreviousModifiedOn datetime
UPDATE
[MyTable]
SET
[ModifiedOn] = #ModifiedOn,
#PreviousModifiedOn = [ModifiedOn]
WHERE
[Id] = #Id AND [ModifiedOn] <= #GeneratedOn
IF #PreviousModifiedOn <= #GeneratedOn
SELECT #ModifiedOn
ELSE
SELECT -1
Is it safe to fill #PreviousModifiedOn variable, with previous value of ModifiedOn, in SET part? Or is it possible that ModifiedOn value changes before it is saved into variable?
UPDATE
Same query using OUTPUT:
ALTER PROCEDURE [UpdateMyTable] (
#Id int,
#ModifiedOn datetime,
#GeneratedOn datetime
)
AS
DECLARE #PreviousModifiedOn AS TABLE (ModifiedOn datetime)
UPDATE
[MyTable]
SET
[ModifiedOn] = #ModifiedOn
OUTPUT
Deleted.[ModifiedOn] INTO #PreviousModifiedOn
WHERE
[Id] = #Id AND [ModifiedOn] <= #GeneratedOn
IF EXISTS (SELECT * FROM #PreviousModifiedOn WHERE [ModifiedOn] <= #GeneratedOn)
SELECT #ModifiedOn
ELSE
SELECT -1
It seems that OUTPUT is the correct way to solve the problem, but because of the variable table, I think it has more performance cost.
So my question is... Why using OUTPUT is better than my solution? Is there anything wrong with my solution? Which one is better in terms of performance and speed?
I believe that this is safe. Although variable assignment is a proprietary extension, the rest of the SET clause follows the SQL Standard here - all assignments are computed as if they occur in parallel. That is, all expressions on the right of assignments are computed based on pre-update values for all columns.
This is e.g. why UPDATE Table SET A=B, B=A will swap the contents of two columns, not set them both equal to whatever B was previously.
The one thing to be wary of here, for me, would be that the UPDATE may have performed no assignments (due to the WHERE clause) and so still be NULL, or may have performed multiple assignments; In the latter case, your variable will be set to one of the previous values but it is not guaranteed which row's value it will have retained.
It is not required, since MS SQL Server 2005 you can use OUTPUT for this kind of scenarios.
ALTER PROCEDURE [UpdateMyTable] (
#Id int,
#ModifiedOn datetime,
#GeneratedOn datetime
)
AS
DECLARE #PreviousModifiedOn datetime
--Declare a table variable for storing the info from Output
DECLARE #ModifiedOnTable AS TABLE
(
ModifiedOn DATETIME
)
UPDATE
[MyTable]
SET
[ModifiedOn] = #ModifiedOn,
#PreviousModifiedOn = [ModifiedOn]
OUTPUT DELETED.ModifiedOn INTO #ModifiedOnTable
WHERE
[Id] = #Id AND [ModifiedOn] <= #GeneratedOn
IF #PreviousModifiedOn <= #GeneratedOn
SELECT ModifiedOn FROM #ModifiedOnTable
ELSE SELECT -1

Understanding a stored procedure SQL-Server

i am trying to understand what a stored procedure is doing but i am struggling.
Here is the code
DECLARE
#person int,
#year int,
#default float = 0
IF EXISTS (SELECT 1 FROM Table1 WHERE PERSONID = #person AND YEAR1 = #year
AND TYPE1 = 'A')
BEGIN
UPDATE Table1 SET DAY1 = #default
WHERE PERSONID = #person AND YEAR1 = #year AND TYPE1 = 'A'
END
ELSE
BEGIN
INSERT INTO Table1 (PERSONID, YEAR1, DAY1, TYPE1)
VALUES (#person, #year, #default, 'A')
This procedure takes data from a website and inserts it into a table into a database from my knowledge. But i cant see where it takes it from. Its just updating or inserting the existing table. Can anyone give any advice as to what this might be doing?
Thanks
Assuming #person, #year and #default are inputs to the stored procedure it is checking whether any record(s) exist in the Table1 table with the specified #person and #year and the Type1 field equal to the value A
If the record(s) exists it is updating that table's Day field with the specified #default value.
If the record(s) do not exist, it is inserting a new record with the specified #person, #year and #default and the Type1 value of A.

Is there a way to dynamically set a parameter in a stored procedure to the result of a query?

I would like to be able to set a parameter of a stored procedure dynamically, based on the results of a SQL query. The stored procedure calculates the distance traveled between a particular date and today. That particular date could be different for each record in the database. (The date is calculated in a separate stored procedure.) See the example.
The stored procedure has two parameters: #DateFrom and #DateTo. #DateFrom should be the date in the DateFrom column, which, as you can see, is different for every record. Is there a way to loop through or something and set the #DateFrom parameter to the value in the DateFrom column for each record? #DateTo will always be today's date. Any help is greatly appreciated.
This is what I got from your question, it's my first answer to a post please excuses typos or code format
USE tempdb
GO
IF OBJECT_ID(N'Tempdb.dbo.#DataTest') IS NULL
BEGIN
CREATE TABLE #DataTest
(
ID INT IDENTITY
,Name VARCHAR(100)
,DateTo DATETIME
,DateFrom DATETIME
)
END
GO
INSERT INTO #DataTest (Name,DateTo,DateFrom) VALUES ('DataValues1', '20151201',GETDATE() + 1)
INSERT INTO #DataTest (Name,DateTo,DateFrom) VALUES ('DataValues3', '20151203',GETDATE() + 2)
INSERT INTO #DataTest (Name,DateTo,DateFrom) VALUES ('DataValues5', '20151205',GETDATE() + 3)
INSERT INTO #DataTest (Name,DateTo,DateFrom) VALUES ('DataValues7', '20151207',GETDATE() + 4)
INSERT INTO #DataTest (Name,DateTo,DateFrom) VALUES ('DataValues9', '20151209',GETDATE() + 5)
GO
CREATE PROC #CalculateData
(
#DateTo DATETIME,
#DateFrom DATETIME
)
AS
SELECT DATEDIFF(SECOND,#DateTo,#DateFrom) AS DataResult
GO
DECLARE #Count INT = (SELECT MIN(ID) FROM #DataTest)
DECLARE #DateToParam DATETIME
DECLARE #DateFromToParam DATETIME
WHILE #Count IS NOT NULL
BEGIN
SET #DateToParam = (SELECT DateTo FROM #DataTest WHERE ID = #Count)
SET #DateFromToParam = (SELECT DateFrom FROM #DataTest WHERE ID = #Count)
EXEC #CalculateData #DateToParam, #DateFromToParam
SET #Count = (SELECT MIN(ID) FROM #DataTest WHERE ID > #Count)
END
GO
DROP TABLE #DataTest
DROP PROCEDURE #CalculateData

how to perform sorting and filtering in stored procedure with performance optimization?

I want to perform sorting and filtering in my stored procedure.
My create table for Holiday table:
CREATE TABLE [dbo].[Holiday](
[HolidaysId] [int] IDENTITY(1,1) NOT NULL,
[HolidayDate] [date] NULL,
[HolidayDiscription] [nvarchar](500) NULL,
[Name] [nvarchar](max) NULL,
CONSTRAINT [PK_Holiday] PRIMARY KEY CLUSTERED
(
[HolidaysId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
My filtering criteria would be as:
Starts With
Is Equal to
Not Equal to.
Note:Please ignore HolidayId in filter comparision.
My Table:Holiday
HolidaysId int,Name nvarchar(500),HolidayDate date.
Sample Input:
HolidayId Name Date
1 abc 1/1/2015
2 pqr 1/2/2015
3 xyz 1/3/2015
Output:
Case 1:Starts with(This is just for name column only.likewise i want to do for HolidayDate column too)
Input:ab(filtering parameter)
Query:where Name like '%ab%' order by Name(when sort column name is passed as parameter in stored procedure for column to sort(for eg:Name))
output:1,abc,1/1/2015
Case 2:Is Equal to(Same as above)
Input:prr(filtering parameter)
output:2,pqr,1/2/2015
Case 3:Not Equal to(Same as above)
Input:bbb(filtering parameter)
output:All Records
This is my stored procedure so far:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sp_PagedItems]
(
#Page int,
#RecsPerPage int
)
AS
-- We don't want to return the # of rows inserted
-- into our temporary table, so turn NOCOUNT ON
SET NOCOUNT ON
--Create a temporary table
CREATE TABLE #TempItems
(
ID int,
Name varchar(50),
HolidayDate date
)
-- Insert the rows from tblItems into the temp. table
INSERT INTO #TempItems (ID, Name,HolidayDate)
SELECT HolidaysId,HolidayDiscription,HolidayDate FROM holiday
-- Find out the first and last record we want
DECLARE #FirstRec int, #LastRec int
SELECT #FirstRec = (#Page - 1) * #RecsPerPage
SELECT #LastRec = (#Page * #RecsPerPage + 1)
-- Now, return the set of paged records, plus, an indiciation of we
-- have more records or not!
SELECT *,
MoreRecords =
(
SELECT COUNT(*)
FROM #TempItems TI
WHERE TI.ID >= #LastRec
)
FROM #TempItems
WHERE ID > #FirstRec AND ID < #LastRec
-- Turn NOCOUNT back OFF
SET NOCOUNT OFF
Now there are 4 things i would send to my stored procedure are:
Page no
PageSize(number of records to retrive)
Sorting Column Name(Name Or HolidayDate)
My filter Column name(Name of Holidaydate) and Operator like StartWith or Equal to or not equal to.(ColumnName and Operator)
Can anybody help me to perform sorting and filtering and if any performance optimization related changes is there then please please do suggest me.
I've not tested this, but something like this you can use as starter and do modifications to make it stable:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sp_PagedItems]
(
#ID int = NULL,
#Name varchar(50) = NULL,
#HolidayDate date = NULL,
#SortCol varchar(20) = '',
#Page int=1,
#RecsPerPage int=10 -- default size, you can change it or apply while executing the SP
)
AS
BEGIN
-- We don't want to return the # of rows inserted
-- into our temporary table, so turn NOCOUNT ON
SET NOCOUNT ON
--Create a temporary table
CREATE TABLE #TempItems
(
ID int,
Name varchar(50),
HolidayDate date
)
-- Insert the rows from tblItems into the temp. table
INSERT INTO #TempItems (ID, Name,HolidayDate)
SELECT HolidaysId, HolidayDiscription, HolidayDate
FROM holiday
-- Find out the first and last record we want
DECLARE #FirstRec int, #LastRec int
SELECT #FirstRec = (#Page - 1) * #RecsPerPage
SELECT #LastRec = (#Page * #RecsPerPage + 1)
-- Now, return the set of paged records, plus, an indiciation of we
-- have more records or not!
; WITH CTE_Results
AS (
SELECT ROW_NUMBER() OVER (ORDER BY
CASE WHEN #SortCol = 'ID_Asc' THEN ID
END ASC,
CASE WHEN #SortCol = 'ID_Desc' THEN ID
END DESC,
CASE WHEN #SortCol = 'Name_Asc' THEN Name
END ASC,
CASE WHEN #SortCol = 'Name_Desc' THEN Name
END DESC,
CASE WHEN #SortCol = 'HolidayDate_Asc' THEN HolidayDate
END ASC,
CASE WHEN #SortCol = 'HolidayDate_Desc' THEN HolidayDate
END DESC
) AS ROWNUM,
ID,
Name,
HolidayDate
FROM #TempItems
WHERE
(#ID IS NULL OR ID = #ID)
AND (#Name IS NULL OR Name LIKE '%' + #Name + '%')
AND (#HolidayDate IS NULL OR HolidayDate = #HolidayDate)
)
SELECT
ID,
Name,
HolidayDate
FROM CTE_Results
WHERE
ROWNUM > #FirstRec
AND ROWNUM < #LastRec
ORDER BY ROWNUM ASC
-- Turn NOCOUNT back OFF
SET NOCOUNT OFF
END
GO
You can check the blog posts I've written on:
Creating Stored Procedures with Dynamic Search (filter)
Creating Stored Procedures with Dynamic Search & Paging (Pagination)
Creating Stored Procedure with Dynamic Search, Paging and Sorting
You can also use the FETCH-OFFSET clause for Pagination if you are on SQL 2012 or more, link.
This is how i have done and i am getting expected output but still i want to take improvement suggestion from all of you if there is any.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[HolidayFetchList]
#pageno int,
#pagesize int,
#sortorder varchar(10),
#sortcolumn varchar(100),
#filter varchar(max),
#count int OUTPUT
AS
BEGIN
declare #Start int=(#pageno)*#pagesize;
declare #End int=#Start+#pagesize;
SET NOCOUNT ON;
DECLARE #tblHoliday AS TABLE
(HolidaysId int,HolidayDate date,HolidayDiscription nvarchar(500),HolidayName nvarchar(max),RN int)
declare #sql varchar(max)= '
select HolidaysId,HolidayDate,HolidayDiscription,HolidayDiscription as HolidayName,ROW_NUMBER() OVER
(ORDER BY '+#sortcolumn + ' '+#sortorder+' ) AS RN from Holiday
WHERE 1=1 '+#filter
print #sql
INSERT INTO #tblHoliday
exec (#sql)
select #count=COUNT(*) from #tblHoliday
print #count
select * from #tblHoliday where RN>#Start and RN<=#End order by RN
END
Please do give me any suggestion if you have any.
It's NOT recommended to use #temp tables because you can affect the RAM on your server. But, bad news :(, you should NOT use the exec command either... now you are susceptible to SQL Injection in your application. So, I think there are at least two options: 1) Using views (include flag values), table valued-functions and other components; 2) Filtering inside the WHERE statement as shown below:
SELECT * FROM Holiday
WHERE
CASE WHEN #paramStartDate Is Not Null THEN HolidayDate ELSE '' END
>= CASE WHEN #paramStartDate Is Not Null THEN #paramStartDate ELSE '' END
AND
CASE WHEN #paramEndDate Is Not Null THEN HolidayDate ELSE '' END
<= CASE WHEN #paramEndDate Is Not Null THEN #paramEndDate ELSE '' END
AND
CASE WHEN #paramName Is Not Null THEN [Name] ELSE '' END
LIKE CASE WHEN #paramName Is Not Null THEN '%' + #paramName + '%' ELSE '' END
You should keep in mind that this method can increment the time of process. If so, you have the possibility of creating several stored procedures, one for HolidayDate search, another one for Name search and another one that combines filters. Your application must be able to decide which one to use depending on the input parameters.
For pagination (ad-hoc reports) I would use OFFSET and FETCH. Use some advantage of T-SQL, then you won't need temporary tables and any of that mess.