Understanding a stored procedure SQL-Server - sql

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.

Related

How can I solve my performance issue in my stored procedure?

How can I get better performance with my sql query in a SP? It has a lot of memory usage.if you look at below my execution pan you will see that :
IF NOT EXISTS(SELECT * FROM Common.[CustomerxxxIds] WHERE xyzType = #xyzType AND CustomerId = #CustomerId)[/code]
has alot of memory usage. How can I reduce that?
ALTER PROCEDURE [Common].[SaveCustomerxxxIds]
(
#xyzType NVARCHAR(128),
#CustomerId INT,
#xxxId INT OUTPUT
)
AS
BEGIN
SET NOCOUNT ON;
IF NOT EXISTS(SELECT * FROM Common.[CustomerxxxIds] WHERE xxxType = #xxxType AND CustomerId = #CustomerId)
BEGIN
INSERT INTO Common.[CustomerxxxIds]
([xxxId]
,[CustomerId]
,[xxxType])
VALUES
(0
,#CustomerId
,#xxxType)
END
UPDATE Common.[CustomerxxxIds]
SET [xxxId] = ([xxxId]) + 1
WHERE [xxxType] = #xxxType
AND CustomerId = #CustomerId
SELECT #xxxId = xxxId
FROM Common.[CustomerxxxIds]
WHERE [xxxType] = #xxxType
AND CustomerId = #CustomerId
END
You can do things to avoid "re-read" of the table to get the output value.
After the INSERT
( INSERT INTO Common.[CustomerxxxIds])
Use SCOPE_IDxxx() to get the newly created surrogate key.
The above will only work for IDxxx columns. From your question, you may not actually have an IDxxx column.
See
https://learn.microsoft.com/en-us/sql/t-sql/functions/scope-idxxx-transact-sql?view=sql-server-2017
.........
with the UPDATE and/or INSERT, you could use OUTPUT functionality to get the value.
https://learn.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql?view=sql-server-2017
This AVOIDS the last select statement (the "re-read" as I am calling it) to get the desired output value.
Obviously completely removing a SELECT statement will improve performance.
..
Below is a simple but complete Northwind database example of using OUTPUT for INSERT and UPDATE
SELECT 'Before' as Looksie, [ShipperID]
,[CompanyName]
,[Phone]
FROM [Northwind].[dbo].[Shippers]
--
DECLARE #MyInsertAuditTable table( AuditShipperID INT,
AuditCompanyName nvarchar(40),
AuditPhone nvarchar(24));
INSERT [Northwind].[dbo].[Shippers] (CompanyName , Phone )
OUTPUT INSERTED.ShipperID, INSERTED.CompanyName, INSERTED.Phone
INTO #MyInsertAuditTable (AuditShipperID, AuditCompanyName , AuditPhone )
SELECT TOP 1
--(SELECT MAX(ShipperID) + 1 from dbo.Shippers )
'Shipper' + LEFT(CONVERT(VARCHAR(38), NEWID()), 12)
, '(555) 555-5555'
FROM sys.objects
--Display the result set of the table variable.
SELECT AuditShipperID, AuditCompanyName, AuditPhone FROM #MyInsertAuditTable;
DECLARE #MyUpdateAuditTable table( AuditShipperID INT,
AuditCompanyName nvarchar(40),
AuditOldPhone nvarchar(24),
AuditNewPhone nvarchar(24));
UPDATE [Northwind].[dbo].[Shippers]
SET Phone = '(777) 555-7777'
OUTPUT inserted.ShipperID, inserted.CompanyName ,
deleted.Phone,
inserted.Phone
INTO #MyUpdateAuditTable ( AuditShipperID, AuditCompanyName, AuditOldPhone , AuditNewPhone)
FROM [Northwind].[dbo].[Shippers] shippers
JOIN #MyInsertAuditTable insAudit on shippers.ShipperID = insAudit.AuditShipperID
SELECT * from #MyUpdateAuditTable
SELECT 'After' as Looksie, [ShipperID]
,[CompanyName]
,[Phone]
FROM [Northwind].[dbo].[Shippers]
--
Results
Looksie ShipperID CompanyName Phone
Before 1 Speedy Express (503) 555-9831
Before 2 United Package (503) 555-3199
Before 3 Federal Shipping (503) 555-9931
..
AuditShipperID AuditCompanyName AuditPhone
9 Shipper3C062D46-EEA (555) 555-5555
...
AuditShipperID AuditCompanyName AuditOldPhone AuditNewPhone
9 Shipper3C062D46-EEA (555) 555-5555 (777) 555-7777
..
Looksie ShipperID CompanyName Phone
After 1 Speedy Express (503) 555-9831
After 2 United Package (503) 555-3199
After 3 Federal Shipping (503) 555-9931
After 9 Shipper3C062D46-EEA (777) 555-7777
You can achieve this by changing SELECT * to SELECT 1. it might help
IF NOT EXISTS (SELECT 1 FROM Common.[CustomerxxxIds]
WHERE xyzType = #xyzType AND CustomerId = #CustomerId)
Try this
ALTER PROCEDURE [Common].[SaveCustomerxxxIds]
(
#xyz NVARCHAR(128),
#CustomerId INT,
#xxxId INT OUTPUT
)
AS
BEGIN
set #xxxId=null
--Get xxxId
SELECT #xxxId=[xxxId] FROM Common.[CustomerxxxIds] WHERE xyz = #xyz AND CustomerId = #CustomerId
--If #xxxId means no record we should insert
if (#xxxId is null)
begin
--When insert we always insert xxxId as 0 then update to one then we collect the value (one) from db and return it.
--Better set value directly as one insert it to DB and return it as one. Instead of insert, update, select
--just insert
set #xxxId = 1
INSERT INTO Common.[CustomerxxxIds]
([xxxId]
,[CustomerId]
,[xyz])
VALUES
(#xxxId
,#CustomerId
,#xyz)
end
else
begin
--If we have the value we add one to it update the record and return it.
--better than update table then select.
--We already have the value we add one to it then update table and return the value we have
set #xxxId+=1
UPDATE Common.[CustomerxxxIds] SET [xxxId] = #xxxId
WHERE [xyz] = #xyz AND CustomerId = #CustomerId
END
end

Update existing value in database using Stored Procedure

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.

SQL Query data based on a variable

I have a simple query that I am trying to adapt to a new situation. For this situation, if the variable locationID=1 then I need all records, regardless of its locationID.
However, if it is not equal to 1, I need to only provide records that match that of the #locationID.
Here is my setup example:
DECLARE #locationID INT = 1; -- All Results Regardless of locationID
--DECLARE #locationID INT = 2; -- Only results that match this locationID (2)
--DECLARE #locationID INT = 3; -- Only results that match this locationID (3)
-- Temp Data
DECLARE #temp AS TABLE (color VARCHAR(10), locationID INT, name VARCHAR(20))
INSERT INTO #temp( color, locationID, name )VALUES ( 'Blue', 1, 'Test 1' )
INSERT INTO #temp( color, locationID, name ) VALUES ( 'Red', 2, 'Test 2' )
INSERT INTO #temp( color, locationID, name ) VALUES ( 'Red', 1, 'Test 3' )
INSERT INTO #temp( color, locationID, name ) VALUES ( 'Red', 2, 'Test 4' )
INSERT INTO #temp( color, locationID, name ) VALUES ( 'Red', 3, 'Test 5' )
-- Query
SELECT *
FROM #temp
WHERE
locationID = ...
I am trying to figure out if I need to use a CASE WHEN or some other method for this.
WHERE (locationID = #LocationId OR #locationID = 1)
Try this:
...WHERE (#LocationId = 1 AND 1=1)
OR (#LocationId <> 1 AND LocationId = #LocationId)
This seems devilishly simple, perhaps I am not understanding your question. If you want to query by a variable value, then just use the variable value:
DECLARE #LocationId INT = 1
SELECT *
FROM #temp
WHERE (#LocationId = 1 AND 1=1)
OR (#LocationId <> 1 AND LocationId = #LocationId)
A couple of other ideas. Since using a constant of 1 is not exactly intuitive or self-documenting, you could default that to NULL. And then say:
WHERE LocationID = COALESCE(#LocationID, LocationID);
If LocationID column is nullable, then instead you could use some token value that is at least slightly more self-documenting than 1, like -1 (since I don't think anyone looking at the code will immediately know that there isn't really a valid row where LocationID = 1):
WHERE LocationID = COALESCE(NULLIF(#LocationID, -1), LocationID);
This is susceptible to parameter sniffing, so you might want to add OPTION (RECOMPILE) if you find you are often switching between "all rows" and "very specific rows." You can also use dynamic SQL to optionally build the WHERE clause, assuming your real scenario uses a real table:
DECLARE #sql nvarchar(max) = N'SELECT ... FROM dbo.RealTable'
IF #LocationID <> 1 -- or IS NOT NULL or <> -1 or what have you
BEGIN
SET #sql += N' WHERE LocationID = #LocationID';
END
EXEC sys.sp_executesql #sql, N'#LocationID int', #LocationID;
This way you get a different plan when the parameter is included vs. when it is not, which doesn't really make the scan (where you need all rows) any better, but it guards against the case where you use a seek + lookup against all rows, which can be really bad. More often, it prevents you from getting the dreadful full table/index scan when you really could have used a seek. But which way that goes is completely dependent on which case is more likely the first time the query runs.
And even here you may want to use OPTION (RECOMPILE) at the end of #sql if the distribution of data across different LocationID values is (or might later become) heavily skewed.
I call this type of query "the kitchen sink," and have written about it here:
#BackToBasics : An Updated "Kitchen Sink" Example
So the variable should be 1 or the locationid?
You could use an IN for that.
... WHERE #LocationID IN (1, LocationID)
Example snippet:
declare #locationID int;
-- Test data using a table variable
declare #varTable table (id int identity(1,1) primary key, locationID int, name varchar(20), color varchar(10));
insert into #varTable (locationID, name, color) values
(1,'Test 1','Blue'),
(2,'Test 2','Red'),
(1,'Test 3','Red'),
(2,'Test 4','Red'),
(3,'Test 5','Red');
-- Returning all records
set #locationID = 1;
SELECT t.* FROM #varTable t WHERE #locationID IN (1, t.locationID);
-- Only returning those with locationID = 2
set #locationID = 2;
SELECT t.* FROM #varTable t WHERE #locationID IN (1, t.locationID);

Adding column to a resultset in stored procedure

I'm working on SP, I want to add a column to a resultset. Normally this would not be a proble, but here I'm using an Exec to fill one temp-table. To that temp-table I want to add one column.
Some prestuff that puts data in one of the temp-tables with some conditions
declare #RowCount int
set #RowCount = 1
create table #Temp_HS (row int IDENTITY (1, 1) NOT NULL, h varchar(30))
Create table #tmpS (K varchar(100),
U varchar(100), Counter int, H varchar(100))
--Puts data in one temp_table with employees
insert into #Temp_HS (h)
select Login from database.dbo.Users
where Old <> 1
and TC in ('A_1', 'A_2')
and Login not in ('Steve', 'Peter', 'Gabs')
--Declaring my counter here, it sets the MaxRow which is 19 in this case
declare #Counter int
set #Counter = (select Max(row) from #Temp_HS)
select * from #Temp_HS
-- Looping, That my RowCount must be less or Equal to Counter which is 19.
while #RowCount <= #Counter
begin
Set User which was originally from the Database Login which is declared as H in the temp table.
declare #user varchar(30)
select #user = h from #Temp_HS where row = #RowCount
Here comes the tricky part, this is the Stored procedure that inserts 3 columns into a temp
table, here I want to add one colum which in this case is h from Temp_HS to the resultset.
INSERT INTO #tmpS
EXEC Database.dbo.getListCount #user,
param,
param,
param,
'something',
param
set #RowCount = #RowCount +1
end
drop table #Temp_HS
If you need any further information just ask! :)
Basically I want to add one more column to the results of my Exec SP that inserts the result into a temp_table
INSERT INTO .. EXEC requires that the table you are inserting into already exists, e.g.
-- Given this preexisting proc
CREATE PROC dbo.getListCount #user INT, -- other params
AS
SELECT #User as Col1,
'SomeVarChar' as Col2
FROM [SomeTable];
-- In your code, create the temp table to hold the data
CREATE TABLE #tmpS
(
Col1 INT,
Col2 NVARCHAR(100),
NewColumnH VARCHAR(30) -- Add the additional column up front
-- etc.
);
This is called as
INSERT INTO #tmpS(Col1, Col2)
EXEC dbo.getListCount, #User;
If you then need to do do further processing on your temp table, do this after the PROC call:
UPDATE ts
SET NewColumnH = t.h
FROM #tmpS ts INNER JOIN #Temp_HS th on th.row = #RowCount;
Actually inner join doesnt work as desireed on temp tables that is why I used this solution. Since I already had #User in a variable I choose to do this update instead.
UPDATE ts
SET NewColumnH = #User
FROM #tmpS ts
where ts.Column is null

SQL update numeric column from decimal datatype

I am trying to update a column of type numeric(5,2) from decimal(10,2) but the column is remaining null and I have no error messages.
DECLARE #newDurationAsDec decimal(10,2)
DECLARE #newDurationAsNum numeric(5,2)
--#newDurationAsDec is set by some logic from another table and holds the correct value e.g 2.00
set #newDurationAsNum = CAST(#newDurationAsDec AS numeric(5,2))
--selecting #newDurationAsNum contains the correct value e.g. 2.00
UPDATE table
SET Duration = #newDurationAsNum
WHERE ID = #ID AND
Duration IS NULL AND
OtherColumn = 'T'
No errors are retruned and the column is not updated. Changing the update to a select returns the correct row. can someone point out my mistake?
Thanks in advance.
Works fine for me below. Check there are no triggers that might be interfering with things.
If you are on at least SQL Server 2005 you can use the OUTPUT clause to see the row(s) updated as illustrated below.
CREATE TABLE #T
(
ID INT PRIMARY KEY,
Duration NUMERIC(5,2),
OtherColumn CHAR(1)
)
INSERT INTO #T VALUES (1,NULL,'T')
DECLARE #ID INT = 1
DECLARE #newDurationAsDec DECIMAL(10,2) = 2
DECLARE #newDurationAsNum NUMERIC(5,2)
SET #newDurationAsNum = CAST(#newDurationAsDec AS NUMERIC(5,2))
SELECT #newDurationAsNum
UPDATE #T
SET Duration = #newDurationAsNum
OUTPUT inserted.*, deleted.*
WHERE ID = #ID AND
Duration IS NULL AND
OtherColumn = 'T'
SELECT * FROM #T
DROP TABLE #T
Run a select to see what you will be updating. I embed my updates statements as shown below so it is easy to move back and forth from the select to the update just by commenting and uncommenting lines of code
--UPDATE t
--SET Duration = #newDurationAsNum
Select *, #newDurationAsNum
FROM table t
WHERE ID = #ID AND
Duration IS NULL AND
OtherColumn = 'T'