Stored procedures multiple filters - all filters in one procedure or separate into own procedures per filter - sql

I have a stored procedure where I pass parameter with type of filter, and second parameter with value of filter. It can be game type, user type etc.
I want to filter data based on different type. If it is game type it should filter by game_name column(whatever it is passed as parameter), if user type by user type name.
I am wondering from perspective of design, is it better to put multiple case statements in one stored procedure or create each stored procedure for different filter type, which in the end I would end with 5-6 different stored procedures with same core sql(select statement).
Example of procedure:
ALTER PROCEDURE [dbo].[Reports_UserStatsDaily] #network VARCHAR(9) = NULL,
#playerAddress VARCHAR(42) = NULL,
#year INT = NULL,
#month INT = NULL,
#from VARCHAR(15) = null,
#to VARCHAR(15) = null
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET nocount ON;
-- Insert statements for procedure here
SELECT [playeraddress],
[network],
[rounds],
[sessions],
[handle],
[hold],
Datefromparts([year], [month], [day]) AS [Date]
FROM [dbo].[userstatsdaily]
WHERE ( #network IS NULL
OR ( network = Upper(#network) ) )
AND ( #playerAddress IS NULL
OR ( playeraddress = Upper(#playerAddress) ) )
AND ( #year IS NULL
OR [year] = #year )
AND ( #month IS NULL
OR [month] = #month )
AND ( #from IS NULL
OR ( Datefromparts([year], [month], [day]) BETWEEN
Cast(#from AS DATETIME2) AND Cast(#to AS DATETIME2)
)
)
AND (len(Handle) > 9 or len(Hold) > 9)
ORDER BY [year] ASC,
[month] ASC,
[day] ASC
END
Problem here is, more filters I put, I put more optional parameters, and append in the end in WHERE clause. How to achieve separation of concerns in stored procedures?

Related

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

Stored Function with Multiple Queries and Different Selected Columns

I have series of queries based on a report type. For simplicity here is an example of what i'm trying to do:
If #Reporttype = '1'
Select lcustomerid, lname, fname
from customers
Where dtcreated > #startdate
Else if #Reporttype = '2'
Select barcode, lname, fname
from employees
where dtcreated > #startdate
Else if #reporttype = '3'
Select thetime, lname, name, barcode, lcustomerid
from Customers
where dtcreated > #startdate
You'll notice that I run 3 separate queries, based on the report type being passed. You'll also notice I am returning different columns and the number of columns.
I'd like to make this a stored function, and return the columns I need based on the report type I pass. However, I know that since the number of columns, and the column names are different - that's not going to work as a stored function as I'd like it to.
The major problem here will be reporting this information - I don't want to have separate functions, because i'll have to maintain different reports for each report type.
Is there a way I can make this work?
You can use multi-statement function but you need to specify all columns which will be returned by 3 select statements. It seems it's impossible return multiple result sets.
User-defined functions can not return multiple result sets. Use a
stored procedure if you need to return multiple result sets. https://msdn.microsoft.com/en-us/library/ms191320.aspx
This is one inconvenience but in report you can use only columns you need, others will be nulls.
CREATE FUNCTION MyFun
(
#Reporttype int,
#startdate datetime
)
RETURNS
#Result TABLE
(
lcustomerid int,
lname nvarchar(50),
fname nvarchar(50),
barcode int,
thetime datetime,
name nvarchar(50)
)
AS
BEGIN
If #Reporttype = '1'
insert into #Result (lcustomerid, lname, fname)
select lcustomerid, lname, fname
from customers
Where dtcreated > #startdate
Else if #Reporttype = '2'
insert into #Result (barcode, lname, fname)
Select barcode, lname, fname
from employees
where dtcreated > #startdate
Else if #reporttype = '3'
insert into #Result (thetime, lname, name, barcode, lcustomerid)
Select thetime, lname, name, barcode, lcustomerid
from customers
where dtcreated > #startdate
RETURN
END
So, you can call function in this way
SELECT * FROM dbo.MyFun (1, getdate())
If you cannot use stored procedure and you need to use a function, you can UNPIVOT the data and than in the client side you can PIVOT it.
I need to do something like this when different number of columns are returned to SQL Server Reporting Services report. For example, the following code is always returning three columns - RowID, Column, Value:
DECLARE #Table01 TABLE
(
[ID] INT
,[Value01] INT
,[Value02] NVARCHAR(256)
,[Value03] SMALLINT
);
DECLARE #Table02 TABLE
(
[ID] INT
,[Value01] INT
);
INSERT INTO #Table01 ([ID], [Value01], [Value02], [Value03])
VALUES (1, 111, '1V2', 7)
,(2, 222, '2V2', 8)
,(3, 333, '3V2', 9);
INSERT INTO #Table02 ([ID], [Value01])
VALUES (1, 111)
,(2, 222)
,(3, 333);
-- your function starts here
DECLARE #Mode SYSNAME = 'Table01' -- try with 'Table02', too
DECLARE #ResultSet TABLE
(
[RowID] INT
,[Column] SYSNAME
,[Value] NVARCHAR(128)
);
IF #Mode = 'Table01'
BEGIN;
INSERT INTO #ResultSet ([RowID], [Column], [Value])
SELECT [ID]
,[Column]
,[Value]
FROM
(
SELECT [ID]
,CAST([Value01] AS NVARCHAR(256))
,CAST([Value02] AS NVARCHAR(256))
,CAST([Value03] AS NVARCHAR(256))
FROM #Table01
) DS ([ID], [Value01], [Value02], [Value03])
UNPIVOT
(
[Value] FOR [Column] IN ([Value01], [Value02], [Value03])
) UNPVT
END;
ELSE
BEGIN;
INSERT INTO #ResultSet ([RowID], [Column], [Value])
SELECT [ID]
,[Column]
,[Value]
FROM
(
SELECT [ID]
,CAST([Value01] AS NVARCHAR(256))
FROM #Table02
) DS ([ID], [Value01])
UNPIVOT
(
[Value] FOR [Column] IN ([Value01])
) UNPVT
END;
SELECT *
FROM #ResultSet;
Then in the reporting I need to perform pivot operation again. This is workaround with many limitations:
the unpivot data must be cast to its largest type (usually, string)
unnecessary operations are performed (pivot -> unpivot) instead of just rendering the data;
it does not working well with large amount of data (it is slow)
and others..
For This you may create a scalar value function that return an xml type column and then you can populate that xml tag values to your report screen
CREATE FUNCTION ReportFunc
(
#intReporttype int,
#dtStartdate datetime
)
RETURNS XML
BEGIN
Declare #xmlResult xml
If #intReporttype = '1'
SET #xmlResult = (
select lcustomerid, lname, fname
from customers
Where dtcreated > #dtStartdate
FOR XML PATH (''), TYPE
)
Else if #intReporttype = '2'
SET #xmlResult = (
Select barcode, lname, fname
from employees
where dtcreated > #dtStartdate
FOR XML PATH (''), TYPE
)
Else if #intReporttype = '3'
SET #xmlResult = (
Select thetime, lname, name, barcode, lcustomerid
from customers
where dtcreated > #dtStartdate
FOR XML PATH (''), TYPE
)
RETURN #xmlResult
END
In SQL it is difficult to create something similar so generic or abstract, especially when it has to do with SELECT of colums. If your purpose is to write as less code as you can in order your sql script to be maintained easily and to be able to add new report types in the future with just minor changes I would suggest to use a stored procedure with dynamic sql. You cannot use a function while you wisk your SELECT to be dynamic, its the wrong method. I would write something like that
CREATE PROCEDURE MyProcedure
(
#ReportType int,
#startdate datetime
)
AS
BEGIN
DECLARE #colNames varchar(MAX),#tblName varchar(MAX),#sSQL varchar(MAX);
SELECT #colNames = CASE
WHEN #ReportType = 1 THEN
'lcustomerid, lname, fname' --You can also add alias
WHEN #ReportType = 2 THEN
'barcode, lname, fname'
WHEN #ReportType = 3 THEN
'thetime, lname, name, barcode, lcustomerid'
ELSE
RAISEERROR('Error msg');
END,
#tblName = CASE
WHEN #ReportType = 1 OR #ReportType = 3 THEN
'customers' --You can also add alias
WHEN #ReportType = 2 THEN
'employees'
ELSE
RAISEERROR('Error msg');
END
SET #sSQL =
'Select '+#colNames+'
from '+#tblName +'
where dtcreated > '''+CONVERT(varchar(10), #startdate, 121)+''''
EXEC(#sSQL)
END
And you will call it as
EXEC MyProcedure 1,'20170131'
for example
With this code every time you want a new report type you will need to add just another line in case with the requested column names. I have used this way in working with Crystal reports and I think it is the best possible solution
If you can use Stored Procedures then for maintainability I would look at using a master stored procedure which calls other stored procedures to return different result sets:
CREATE PROCEDURE MyProc_1(#startdate DateTime)
AS
BEGIN
SELECT lcustomerid, lname, fname
FROM customers WHERE dtcreated > #startdate
END
GO
CREATE PROCEDURE MyProc_2(#startdate DateTime)
AS
BEGIN
SELECT barcode, lname, fname
FROM employees where dtcreated > #startdate
END
GO
CREATE PROCEDURE MyProc_3(#startdate DateTime)
AS
BEGIN
SELECT thetime, lname, name, barcode, lcustomerid
FROM Customers WHERE dtcreated > #startdate
END
GO
CREATE PROCEDURE MyProc(#Reporttype char(1), #startdate DateTime)
AS
BEGIN
IF #Reporttype = '1' EXEC MyProc_1 #startdate
ELSE IF #Reporttype = '2' EXEC MyProc_2 #startdate
ELSE IF #reporttype = '3' EXEC MyProc_3 #startdate
END
GO
And to use:
DECLARE #dt datetime = getdate()
EXEC MyProc 1, #dt
CREATE Proc Emp_det
(
#Reporttype INT,
#startdate DATETIME
)
AS
BEGIN
If #Reporttype = '1' BEGIN
Select lcustomerid, lname, fname
FROM customers
WHERE dtcreated > #startdate
END
ELSE IF #Reporttype = '2' BEGIN
Select barcode, lname, fname
FROM employees
WHERE dtcreated > #startdate
END
ELSE IF #reporttype = '3' BEGIN
Select thetime, lname, name, barcode, lcustomerid
FROM Customers
WHERE dtcreated > #startdate
END
END
GO
Exec Emp_det 1,GETDATE()

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.

How to Update a row with some declard and setted Values

Hi i struggle with my stored-procedure which adds 'NULL' instead of a number
So why does the following procedure adds 'NULL' instead of a value between 0 and infinity?
Here is my procedure
ALTER PROCEDURE [dbo].[Plan_Abschluss]
-- My parameters for the stored procedure
#date AS datetime2(7),
#Einrichtung AS Int,
#Mitarbeiter AS Int
AS
BEGIN
-- declare my parameters
DECLARE #PlanStunden AS decimal(18, 2)= null,
#PlanUrlaub AS Int= null,
#oldDate AS datetime2(7)= null,
#oldUrlaubskonto AS Int= null,
#oldStundenKonto AS decimal(18, 2)= null;
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- set the previous month
SET #oldDate= DATEADD(month, -1, #date);
-- get some values from the previous row and set it to my parameters
SELECT #oldUrlaubskonto = ISNULL(CurrentUrlaubskonto,0) ,
#oldStundenKonto = ISNULL(CurrentStundenKonto,0)
FROM [Plan]
WHERE [Jahr] = YEAR(#oldDate)
AND [Monat] = MONTH(#oldDate)
AND [RefMitarbeiterId] = #Mitarbeiter
AND [RefEinrichtungId] = #Einrichtung;
-- get some values from the row i want to update and set it to my parameters
SELECT #PlanStunden = ISNULL(PlanStunden,0) ,
#PlanUrlaub = ISNULL(PlanUrlaub,0)
FROM [Plan]
WHERE [Jahr] = YEAR(#date)
AND [Monat] = MONTH(#date)
AND [RefMitarbeiterId] = #Mitarbeiter
AND [RefEinrichtungId] = #Einrichtung;
-- update the row and do a calculation with my parameters
UPDATE [Plan]
SET Abgeschlossen = 1,
CurrentUrlaubskonto = #oldUrlaubskonto+ #PlanUrlaub,
CurrentStundenKonto = #oldStundenKonto+ #PlanStunden
WHERE [Jahr] = YEAR(#date)
AND [Monat] = MONTH(#date)
AND [RefMitarbeiterId] = #Mitarbeiter
AND [RefEinrichtungId] = #Einrichtung
END
Variables will not be set in a select if no rows are returned. My guess is that the first select using #OldDate simply doesn't match any rows.
In particular, the variables #oldUrlaubskonto and #oldStundenKonto are initialized to NULL, so they are never set, when there is no matching record. One easy way to fix this is to use aggregation -- you are expecting one row anyway, so that is okay:
SELECT #oldUrlaubskonto = ISNULL(max(CurrentUrlaubskonto), 0) ,
#oldStundenKonto = ISNULL(max(CurrentStundenKonto), 0
You can also set the value afterwards, if it is still NULL.

One SQL statement that updates two tables

I have a SQL script that returns one unique row. I want to update one column in that row and a counter in a different table
I model a kind of a work item queue and when one element is selected from the queue, its state gets set to "executing" but I also want to keep track of how many items of a certain priority have been selected.
Following is the script without the table that is used for counting
IF OBJECT_ID(N'[Queue]', N'U') IS NOT NULL
DROP TABLE [Queue]
CREATE TABLE [Queue](
[ID] int IDENTITY(1,1) NOT NULL,
[Priority] int NOT NULL default 3,
[State] int NOT NULL default 0,
[Command] nvarchar(max),
[Queued] datetime2 NOT NULL default GetDate(),
[Assigned] datetime2 NULL
)
INSERT INTO [Queue] ([Command], [Priority], [Queued]) VALUES
('systeminfo', 1, DATEADD(MILLISECOND, -40, GETDATE())),
('systeminfo', 2, DATEADD(MILLISECOND, -30, GETDATE())),
('systeminfo', 1, DATEADD(MILLISECOND, -20, GETDATE())),
('systeminfo', 3, DATEADD(MILLISECOND, -20, GETDATE()))
GO
IF OBJECT_ID(N'Dequeue', N'P') IS NOT NULL
DROP PROCEDURE Dequeue
GO
CREATE PROCEDURE Dequeue
AS
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
UPDATE selected
SET [State] = 1, Assigned = GETDATE()
OUTPUT inserted.*
FROM (
SELECT TOP(1) *
FROM [Queue]
WHERE [State] = 0
ORDER BY [Priority] ASC, [Queued] ASC
) selected
The update statement in the stored procedure now should also do something like
UPDATE [Counter]
SET [Counter].[Count] = [Counter].[Count] + 1
WHERE [Counter][Priority] = selected.[Priority]
Do I have to temporarily store the selected row in a Temp Table or variable, or is there a more elegant way?
You can use option with variable of type table
DECLARE #Priority TABLE (Priority int)
UPDATE selected
SET [State] = 1, Assigned = GETDATE()
OUTPUT inserted.Priority INTO #Priority(Priority)
FROM (
SELECT TOP(1) *
FROM [Queue]
WHERE [State] = 0
ORDER BY [Priority] ASC, [Queued] ASC
) selected
UPDATE c
SET c.[Count] = ISNULL(c.[Count], 0) + 1
FROM [Counter] c JOIN #Priority p ON c.[Priority] = p.[Priority]
Have you tried adding a trigger to the Queue table thad runs your update on the Counter table?