I'm trying to write a value to an output parameter. In my THEN statement I'd like to either return outParam as 1 or 0 but not sure how to modify the below statement to do so. Can anyone help? Thanks!
CREATE PROCEDURE [dbo].[myProc]
#var1 UNIQUEIDENTIFIER,
#outParam BIT OUTPUT
AS
SELECT
CASE
WHEN EXISTS (SELECT 1
FROM [Table1] AS [i]
WHERE (CONVERT(VARCHAR(36), [i].[var1]) = #var1)
AND ([i].[BitColumn] = 1))
THEN CAST(1 AS BIT)
ELSE CAST(0 AS BIT)
END
GO
You're ever so close! Just set it and go.
CREATE PROCEDURE [dbo].[myProc] #var1 uniqueidentifier,
#outParam bit OUTPUT
AS
SELECT #outParam = CASE
WHEN EXISTS (
SELECT 1
FROM [Table1] AS [i]
WHERE (CONVERT(VARCHAR(36), [i].[var1]) = #var1) AND ([i].[BitColumn] = 1))
THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END;
GO
Related
I'm trying to write a function in SQL Server that checks if a tuple exists in one of my tables or not. if so, it returns 1 else it should return 0.
This is what I have written:
create function allow_user_to_score
(#user_id char(6), #app_id char(7))
returns smallint
as
begin
declare #result
select #result = case
when (#user_id, #app_id) in (select * from installed_app)
then 1
else 0
from installed_app
return (#result)
end
but I get this error and I don't know why:
An expression of non-boolean type specified in a context where a condition is expected
You have multiple errors on your code. I'm assuming that you have 2 columns in your installed_app table called user_id and app_id.
CREATE FUNCTION allow_user_to_score(
#user_id char(6),
#app_id char(7)
)
RETURNS SMALLINT
AS
BEGIN
DECLARE #result smallint;
SELECT #result = CASE WHEN EXISTS (SELECT *
FROM installed_app
WHERE user_id = #user_id
AND app_id = #app_id)
THEN 1
ELSE 0 END;
RETURN #result;
END
As mentioned by Sean, this can easily be converted to an inline table-valued function for a great boost in performance.
CREATE FUNCTION allow_user_to_score(
#user_id char(6),
#app_id char(7)
)
RETURNS TABLE
AS RETURN
SELECT result = CASE WHEN EXISTS (SELECT *
FROM installed_app
WHERE user_id = #user_id
AND app_id = #app_id)
THEN 1
ELSE 0 END;
Try this:
declare #result smallint
select #result = sign(count(*)) from installed_app
where userId=#userId and appId=#appId
return (#result)
If you know there can only be 1 record (or 0), you can remove the sign() function
I'm writing a stored procedure that runs different selects based on the user role , this is just an example to showcase my intention since i have a lot of roles to work with.
IF OBJECT_ID('dbo.spSelectArtigos') IS NOT NULL
DROP PROCEDURE spSelectArtigos
GO
CREATE PROCEDURE spSelectArtigos
#IdUser int,
#Acesso nvarchar(20)
AS
BEGIN
IF(#Acesso = 'User')
SELECT col1,col2 from table1 where IdUser = #IdUser
IF(#Acesso = 'Logistica')
SELECT col1,col2,col3 from table1 where IdUser = #IdUser
IF(#Acesso = 'Admin')
SELECT * From table1
END
Is there a more effective way to do this with less code using some sort of logic?
IDK why i added a check to make sure the user actually exists but it isnt a bad idea
IF OBJECT_ID('dbo.spSelectArtigos') IS NOT NULL
DROP PROCEDURE spSelectArtigos
GO
CREATE PROCEDURE spSelectArtigos
#IdUser int,
#Acesso nvarchar(20)
AS
BEGIN
CASE
WHEN #Acesso ='User'
AND
EXISTS (SELECT * FROM table1 WHERE IdUser = #IdUser)
THEN
SELECT col1,col2 from table1 where IdUser = #IdUser
WHEN #Acesso ='Logistica' Then
AND
EXISTS (SELECT * FROM table1 WHERE IdUser = #IdUser)
THEN
SELECT col1,col2,col3 from table1 where IdUser = #IdUser
WHEN #Acesso ='Admin'
AND
EXISTS (SELECT * FROM table1 WHERE IdUser = #IdUser)
THEN
SELECT * From table1
END
END
Here is a kludgy way but it is concise.
IF OBJECT_ID('dbo.spSelectArtigos') IS NOT NULL
DROP PROCEDURE spSelectArtigos
GO
CREATE PROCEDURE spSelectArtigos
#IdUser int,
#Acesso nvarchar(20)
AS
BEGIN
DECLARE #Result TABLE(A INT,B INT,C INT,D INT)
INSERT #Result SELECT A,B,C,D FROM table1 WHERE idUser=#idUser
IF(#Acesso = 'User')
UPDATE #Result SET C=NULL, D=NULL
IF(#Acesso = 'Logistica')
UPDATE #Result SET D=NULL
SELECT * From #Result
END
Another way but dealing with ID's
SELECT
A=CASE WHEN #Acesso >= 10 THEN A ELSE NULL END,
B=CASE WHEN #Acesso >= 20 THEN B ELSE NULL END,
C=CASE WHEN #Acesso >= 99 THEN C ELSE NULL END
FROM
table1
WHERE
idUser=#idUser
Below is my scalar function:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [CheckClients]
(
#UserId Varchar(3),
#DbrNo varchar(10),
#V_DBR_CLIENT varchar(6)
)
RETURNS int
AS
BEGIN
Declare #Flag int
set #Flag=1
if(#V_DBR_CLIENT='XXXXXX')
BEGIN
if((select COUNT(USR_CLI)
from USRAGYCLI
inner join DBR on DBR_CLIENT = USR_CLI
where USR_CODE = #UserId and DBR_SERIES like #DbrNo +'T') <>
(select COUNT(DBR_CLIENT)
from DBR
where DBR_SERIES like #DbrNo + 'T') OR
(select COUNT(DBR_CLIENT)
from DBR
where DBR_SERIES like #DbrNo +'T') <= 0)
BEGIN
set #Flag=0
END
END
RETURN #Flag
END
This is my stored procedure:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [SEL_CLI]
#V_USER_ID VARCHAR(3),
#V_NUMBER_OF_ROWS INT,
#V_STARTS_WITH INT
AS
BEGIN
CREATE TABLE #tmpDbrNo
(
Code VARCHAR(10),
Name VARCHAR(100),
NumberOfDebtors int,
rownum int
)
;WITH Temp AS
(
SELECT
CLT_NO AS Code,
CLT_NAME AS Name,
COUNT(DBR_NO) AS NumberOfDebtors
FROM
DBR
JOIN
USRAGYCLI ON DBR_CLIENT = USR_AGY_CLI
JOIN
CLT ON DBR_CLIENT = CLT_NO
WHERE
AND USR_CODE = #V_USER_ID
AND 1 = CheckClients(#V_USER_ID, DBR_NO, DBR_CLIENT)
GROUP BY
CLT_NO, CLT_NAME
)
INSERT INTO #tmpDbrNo
SELECT
Code, Name, NumberOfDebtors,
ROW_NUMBER() OVER (ORDER by Code) rownum
FROM
Temp
SELECT
Code, Name, NumberOfDebtors
FROM
#tmpDbrNo
WHERE
rownum BETWEEN #V_STARTS_WITH AND #V_STARTS_WITH + #V_NUMBER_OF_ROWS
END
Above query takes about 25 sec to execute which is too long to wait. And if I comment out the line where I have called the scalar function in the where clause, it takes 0 secs to execute the query.
Can anybody suggest better way which may take minimum secs to execute the query? I have tried to put call to function in case like as below, but no success.
AND 1 = CASE WHEN DBR_CLIENT='XXXXXX' THEN CheckClients(#V_USER_ID,DBR_NO,DBR_CLIENT) ELSE 1 END
This is just a shot in the dark because we were not provided with any ddl or much to work with. I think I interpreted the existing logic in your scalar function correctly. As a general rule you should probably avoid using flags. This is a very old school mindset and is not suited to relational data very well at all. I suspect this could be greatly improved with an understanding of the actual requirements but this is the best I could do with the limited details.
CREATE FUNCTION [CheckClients]
(
#UserId Varchar(3),
#DbrNo varchar(10),
#V_DBR_CLIENT varchar(6)
)
RETURNS table as return
with RowCounts as
(
select
(
select COUNT(DBR_CLIENT)
from DBR
where DBR_SERIES like #DbrNo + 'T'
) as ClientCount
,
(
select COUNT(USR_CLI)
from USRAGYCLI u
inner join DBR d on d.DBR_CLIENT = u.USR_CLI
where u.USR_CODE = #UserId
and d.DBR_SERIES like #DbrNo +'T'
) as UserCount
)
select case
when #V_DBR_CLIENT = 'XXXXXX' then
Case when rc.UserCount <> rc.ClientCount then 0
when rc.ClientCount < 0 then 0
else 1
end
else 1
end as Flag
from RowCounts rc
You can optimize your scalar function query to reduce doing multiple read. Like:
ALTER FUNCTION [CheckClients] (
#UserId VARCHAR(3),
#DbrNo VARCHAR(10),
#V_DBR_CLIENT VARCHAR(6)
)
RETURNS INT
AS
BEGIN
DECLARE #Flag INT
SET #Flag = 1
IF (#V_DBR_CLIENT = 'XXXXXX')
BEGIN
DECLARE #Count INT = ISNULL((
SELECT COUNT(DBR_CLIENT)
FROM DBR
WHERE DBR_SERIES LIKE #DbrNo + 'T'
), 0);
IF (
(ISNULL((
SELECT COUNT(USR_CLI)
FROM USRAGYCLI
INNER JOIN DBR ON DBR_CLIENT = USR_CLI
WHERE USR_CODE = #UserId
AND DBR_SERIES LIKE #DbrNo + 'T'
), 0) <> #Count)
OR (#Count <= 0)
)
BEGIN
SET #Flag = 0
END
END
RETURN #Flag
END
Also, you need to study your execution plan of the query to find out where the query is having high cost of execution time. And create non-clustered index if necessary.
-- EDITED LATER --
The non-Sargable Problem (Calling Scalar Function):
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [SEL_CLI]
#V_USER_ID VARCHAR(3),
#V_NUMBER_OF_ROWS INT,
#V_STARTS_WITH INT
AS
BEGIN
CREATE TABLE #tmpDbrNo
(
Code VARCHAR(10),
Name VARCHAR(100),
NumberOfDebtors int,
rownum int
)
;WITH Temp AS
(
SELECT
CLT_NO AS Code,
CLT_NAME AS Name,
COUNT(DBR_NO) AS NumberOfDebtors
FROM
DBR
JOIN
USRAGYCLI ON DBR_CLIENT = USR_AGY_CLI
JOIN
CLT ON DBR_CLIENT = CLT_NO
WHERE
USR_CODE = #V_USER_ID
AND 1 =
(CASE
WHEN (#V_DBR_CLIENT = 'XXXXXX') THEN
(CASE
WHEN (
ISNULL((
SELECT COUNT(USR_CLI)
FROM USRAGYCLI
INNER JOIN DBR ON DBR_CLIENT = USR_CLI
WHERE USR_CODE = #UserId
AND DBR_SERIES LIKE #DbrNo + 'T'
), 0) <> ISNULL((
SELECT COUNT(DBR_CLIENT)
FROM DBR
WHERE DBR_SERIES LIKE #DbrNo + 'T'
), 0)
)
OR (ISNULL((
SELECT COUNT(DBR_CLIENT)
FROM DBR
WHERE DBR_SERIES LIKE #DbrNo + 'T'
), 0) <= 0)
THEN 0
ELSE 1
END)
ELSE 1
END)--CheckClients(#V_USER_ID, DBR_NO, DBR_CLIENT)
GROUP BY
CLT_NO, CLT_NAME
)
INSERT INTO #tmpDbrNo
SELECT
Code, Name, NumberOfDebtors,
ROW_NUMBER() OVER (ORDER by Code) rownum
FROM
Temp
SELECT
Code, Name, NumberOfDebtors
FROM
#tmpDbrNo
WHERE
rownum BETWEEN #V_STARTS_WITH AND #V_STARTS_WITH + #V_NUMBER_OF_ROWS
END
As you can see, the scalar function can be included in the same query, but if you study the function nicely than it is clear that the query in scalar function is not fully dependent on the query in the store procedure. It is making count and will reread and manipulate the data from the table every time.
So, with this type of query making non-Sargable to Sargable will not improve the performance. The possible solution to the problem will be
To previously add the required data in the table and check from there.
To study your query plans(Design and Execution) and optimize it accordingly.
I'm trying to create a procedure in SQL Server 2008 that inserts data from a temp table into an already existing table. I think I've pretty much figured it out, I'm just having an issue with a loop. I need the row count from the temp table to determine when the loop should finish.
I've tried using ##ROWCOUNT in two different ways; using it by itself in the WHILE statement, and creating a variable to try and hold the value when the first loop has finished (see code below).
Neither of these methods have worked, and I'm now at a loss as to what to do. Is it possible to use ##ROWCOUNT in this situation, or is there another method that would work better?
CREATE PROCEDURE InsertData(#KeywordList varchar(max))
AS
BEGIN
--create temp table to hold words and weights
CREATE TABLE #tempKeywords(ID int NOT NULL, keyword varchar(10) NOT NULL);
DECLARE #K varchar(10), #Num int, #ID int
SET #KeywordList= LTRIM(RTRIM(#KeywordList))+ ','
SET #Num = CHARINDEX(',', #KeywordList, 1)
SET #ID = 0
--Parse varchar and split IDs by comma into temp table
IF REPLACE(#KeywordList, ',', '') <> ''
BEGIN
WHILE #Num > 0
BEGIN
SET #K= LTRIM(RTRIM(LEFT(#KeywordList, #Num - 1)))
SET #ID = #ID + 1
IF #K <> ''
BEGIN
INSERT INTO #tempKeywords VALUES (#ID, #K)
END
SET #KeywordList = RIGHT(#KeywordList, LEN(#KeywordList) - #Num)
SET #Num = CHARINDEX(',', #KeywordList, 1)
--rowcount of temp table
SET #rowcount = ##ROWCOUNT
END
END
--declaring variables for loop
DECLARE #count INT
DECLARE #t_name varchar(30)
DECLARE #key varchar(30)
DECLARE #key_weight DECIMAL(18,2)
--setting count to start from first keyword
SET #count = 2
--setting the topic name as the first row in temp table
SET #t_name = (Select keyword from #tempKeywords where ID = 1)
--loop to insert data from temp table into Keyword table
WHILE(#count < #rowcount)
BEGIN
SET #key = (SELECT keyword FROM #tempKeywords where ID = #count)
SET #key_weight = (SELECT keyword FROM #tempKeywords where ID = #count+2)
INSERT INTO Keyword(Topic_Name,Keyword,K_Weight)
VALUES(#t_name,#key,#key_weight)
SET #count= #count +2
END
--End stored procedure
END
To solve the second part of your problem:
INSERT INTO Keyword(Topic_Name,Keyword,K_Weight)
SELECT tk1.keyword, tk2.keyword, tk3.keyword
FROM
#tempKeywords tk1
cross join
#tempKeywords tk2
inner join
#tempKeywords tk3
on
tk2.ID = tk3.ID - 1
WHERE
tk1.ID = 1 AND
tk2.ID % 2 = 0
(This code should replace everything in your current script from the --declaring variables for loop comment onwards)
You could change:
WHILE(#count < #rowcount)
to
WHILE(#count < (select count(*) from #tempKeywords))
But like marc_s commented, you should be able to do this without a while loop.
I'd look at reworking your query to see if you can do this in a set based way rather than row by row.
I'm not sure I follow exactly what you are trying to achieve, but I'd be tempted to look at the ROW_NUMBER() function to set the ID of your temp table. Used with a recursive CTE such as shown in this answer you could get an id for each of your non empty trimmed words. An example is something like;
DECLARE #KeywordList varchar(max) = 'TEST,WORD, ,,,LIST, SOME , WITH, SPACES'
CREATE TABLE #tempKeywords(ID int NOT NULL, keyword varchar(10) NOT NULL)
;WITH kws (ord, DataItem, Data) AS(
SELECT CAST(1 AS INT), LEFT(#KeywordList, CHARINDEX(',',#KeywordList+',')-1) ,
STUFF(#KeywordList, 1, CHARINDEX(',',#KeywordList+','), '')
union all
select ord + 1, LEFT(Data, CHARINDEX(',',Data+',')-1),
STUFF(Data, 1, CHARINDEX(',',Data+','), '')
from kws
where Data > ''
), trimKws(ord1, trimkw) AS (
SELECT ord, RTRIM(LTRIM(DataItem))
FROM kws
)
INSERT INTO #tempKeywords (ID, keyword)
SELECT ROW_NUMBER() OVER (ORDER BY ord1) as OrderedWithoutSpaces, trimkw
FROM trimKws WHERE trimkw <> ''
SELECT * FROM #tempKeywords
I don't fully understand what you are trying to acheive with the second part of your query , but but you could just build on this to get the remainder of it working. It certainly looks as though you could do what you are after without while statements at least.
I have the following stored procedure that I working on. I have noticed that every 5th or 6th time I refresh my results there are new values in there. Which considering that the data is in a static environment and no one is making any changes to the data at this time I really can't understand. Can someone please enlighten me as to why I would see different results even though I am running this procedure with the exact same parameters. I even tried it in query analyzer and still see the same strange results.
I am running in Sql 2008.
Here is the proc:
ALTER PROCEDURE [dbo].[SelectSearchBy_Category]
#userId INT,
#page INT,
#results INT,
#category NVARCHAR(50),
#searchTerm NVARCHAR(200) = NULL
AS
BEGIN
SET NOCOUNT ON
SET ROWCOUNT #results
DECLARE #categoryId INT
IF (#category IS NOT NULL) BEGIN
SET #categoryId = ( SELECT categoryId FROM Category WHERE categoryDescription = #category )
END
DECLARE #rowEnd INT
DECLARE #rowStart INT
SET #rowEnd = (#page * #results)
SET #rowStart = #rowEnd - #results
;WITH OrderedItems AS
(
SELECT
i.itemId,
title,
i.[description],
i.url,
i.categoryId,
i.ratingId,
i.requirements,
ISNULL(i.rating, 0) AS tating,
ISNULL(i.raters, 0) AS raters,
i.urlFriendlyPath,
ROW_NUMBER() OVER
(
ORDER BY i.dateAdded, (ISNULL(i.rating, 0) * ISNULL(i.raters, 0))
) AS RowNumber
FROM
[dbo].[Item] i
LEFT JOIN
UserItemIgnore uii ON uii.itemId = i.itemId AND uii.userId = #userId
INNER JOIN
ItemLanguage il ON il.itemId = i.itemId
WHERE
(#searchTerm IS NULL OR a.title LIKE '%' + #searchTerm + '%') AND
i.categoryId = #categoryId AND
il.languageId = 1 AND
uii.itemId IS NULL
)
SELECT *
FROM OrderedItems
WHERE RowNumber BETWEEN #rowStart AND #rowEnd
END
You will probably have consistent results if you put an order by clause in your OrderedItems temporary table definition.
Try using
ROW_NUMBER() OVER (ORDER BY i.dateAdded,
(ISNULL(i.rating, 0) * ISNULL(i.raters, 0)),
i.itemId)
i.itemId will act as a tie breaker to ensure that the results of ROW_NUMBER are deterministic in the event you have rows with equal ranks for i.dateAdded, (ISNULL(i.rating, 0) * ISNULL(i.raters, 0))