Subquery returns more than one value? - sql

I'm trying to use a stored procedure I wrote that is supposed to create a new project in a "Project" table. It also checks to see if there is a saved project in a "Saved Project" table with the same ID and deletes it upon successful creation.
It's also supposed to check whether the user_id passed has permission to actually create a project( i.e. isn't a standard user).
Here is the stored procedure:
USE [BugMate_DB]
GO
/****** Object: StoredProcedure [dbo].[create_project] Script Date: 2020-07-08 11:05:30 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[create_project]
#project_name NVARCHAR(40),
#date_started DATETIME,
#project_description NVARCHAR(400),
#project_status NVARCHAR(40),
#project_active BIT,
#next_iteration_date DATETIME,
#created_by_userid INT,
#project_leader_id INT,
#save_id INT = NULL
AS
SET NOCOUNT ON
BEGIN
IF (SELECT TOP 1 roleid from user_info WHERE userid = #created_by_userid) = 'SDU'
BEGIN
RAISERROR ('User does not have permission to do this action. Please contact a manager or administrator to resolve this issue.', 16, 1)
END
ELSE
BEGIN
INSERT INTO project(project_name, date_started, project_description, project_status, project_active, next_iteration_date, created_by_userid, project_leader_id)
VALUES (#project_name, #date_started, #project_description, #project_status, #project_active, #next_iteration_date, #created_by_userid, #project_leader_id)
IF ##ERROR <> 0
BEGIN
RAISERROR('Project creation insert failed.', 16, 1)
END
ELSE
BEGIN
IF #save_id != NULL
BEGIN
IF EXISTS (SELECT TOP 1 save_id FROM saved_project WHERE save_id = #save_id)
BEGIN
DELETE FROM saved_project WHERE save_id = #save_id
END
END
END
END
IF ##ERROR <> 0
BEGIN
RAISERROR('Error creating project', 16, 1)
END
ELSE
BEGIN
INSERT INTO project_member(userid, project_number)
VALUES (#created_by_userid, (SELECT project_number FROM project WHERE created_by_userid = #created_by_userid))
END
END
The problem is that I am getting this error when execute my SP:
Msg 512, Level 16, State 1, Procedure create_project, Line 48
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
I'm not sure why any of my sub queries would be returning multiple values.
Here are the values I am trying to pass:
USE [BugMate_DB]
GO
DECLARE #return_value int
EXEC #return_value = [dbo].[create_project]
#project_name = N'Test',
#date_started = N'12/25/2015 12:00:00 AM',
#project_description = N'This is a test.',
#project_status = N'InDevelopment',
#project_active = 1,
#next_iteration_date = N'12/25/2015 12:00:00 AM',
#created_by_userid = 19,
#project_leader_id = 19,
#save_id = NULL
SELECT 'Return Value' = #return_value
GO
I have tried using "TOP 1" to try and get a single value back but it doesn't seem to be what I'm looking for.
As far as I can tell this isn't an issue of joins either.
I'm new to SQL Server so any help would be appreciated.

Try changing this
IF ( SELECT TOP 1 roleid from user_info WHERE userid = #created_by_userid ) = 'SDU'
BEGIN
RAISERROR ( 'User does not have permission to do this action. Please contact a manager or administrator to resolve this issue.', 16, 1 )
END
To
IF NOT EXISTS ( SELECT * FROM user_info WHERE userid = #created_by_userid AND roleid = 'SDU' )
BEGIN
RAISERROR ( 'User does not have permission to do this action. Please contact a manager or administrator to resolve this issue.', 16, 1 )
END
Edit: Include explicit role access.
IF NOT EXISTS ( SELECT * FROM user_info WHERE userid = #created_by_userid AND roleid IN ( 'SDU', 'MNG', 'ADM ' ) )
BEGIN
RAISERROR ( 'User does not have permission to do this action. Please contact a manager or administrator to resolve this issue.', 16, 1 )
END
Update:
USE [BugMate_DB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[create_project]
#project_name NVARCHAR(40),
#date_started DATETIME,
#project_description NVARCHAR(400),
#project_status NVARCHAR(40),
#project_active BIT,
#next_iteration_date DATETIME,
#created_by_userid INT,
#project_leader_id INT,
#save_id INT = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE #err INT = 0;
DECLARE #output TABLE ( project_number INT );
IF NOT EXISTS ( SELECT * FROM user_info WHERE userid = #created_by_userid AND roleid IN ( 'SDU', 'MNG', 'ADM ' ) )
BEGIN
RAISERROR ( 'User does not have permission to do this action. Please contact a manager or administrator to resolve this issue.', 16, 1 );
END
ELSE
BEGIN
INSERT INTO project (
project_name, date_started, project_description, project_status, project_active, next_iteration_date, created_by_userid, project_leader_id
)
OUTPUT inserted.project_number INTO #output
VALUES (
#project_name, #date_started, #project_description, #project_status, #project_active, #next_iteration_date, #created_by_userid, #project_leader_id
);
-- Capture error value.
SET #err = ##ERROR;
IF #err <> 0
BEGIN
RAISERROR ( 'Project creation insert failed.', 16, 1 );
END
ELSE
BEGIN
IF #save_id IS NULL
BEGIN
IF EXISTS ( SELECT * FROM saved_project WHERE save_id IS NULL )
BEGIN
DELETE FROM saved_project WHERE save_id IS NULL;
END
END
END
END
IF #err <> 0
BEGIN
RAISERROR ( 'Error creating project', 16, 1 );
END
ELSE
BEGIN
INSERT INTO project_member (
userid, project_number
)
VALUES (
#created_by_userid,
( SELECT project_number FROM #output )
);
END
END

Please compare the NULL values using IS NULL like below:
IF #save_id IS NULL
or
IF #save_id IS NOT NULL

I haven't seen this type of query before
INSERT INTO project_member(userid, project_number)
VALUES (#created_by_userid,
(SELECT project_number FROM project WHERE created_by_userid = #created_by_userid))
I would do either
INSERT INTO project_member(userid, project_number)
VALUES (#created_by_userid, #project_id)
if I were just inserting 2 values.
If inserting many values I would do SELECT and not VALUES.
If I were inserting maybe more that 1 row of values:
INSERT INTO project_member(userid, project_number)
SELECT created_by_userid, project_number FROM project
WHERE created_by_userid = #created_by_userid
Im just guessing since that is about line 46
Also, what sacse said about IF (#save_id IS NULL)
maybe there is an answer in there someplace

Related

I'm trying this scenario but it is not giving correct output

//*
WAP which takes deptno,dname as input and
insert that records into dept table.
if deptno is already existing,then increment
its value by 10 and again try inserting.The
insert is suposed to be tried 5 times and
falling all 5 times, print amessage saying
'Try later'
*//
Alter procedure sp_update (
#dno int,
#dname varchar(30)
)
as begin
declare #rowcount int
set #rowcount=##ROWCOUNT
if #rowcount<5
begin
if not exists (select dno from dept where #dno=dno)
begin
insert into dept (dno,dname) values (#dno,#dname)
end
else
begin
print 'Try later'
end
end
else
begin
set #dno=(select max(dno) from dept)
set #dno=#dno+10
insert into dept (dno,dname) values (#dno,#dname)
end
end
exec sp_update 10,'HR'
Please give the required output.
The #rowcount variable at the begining of the procedure is always <5, it's never going to enter the segment where "increment its value by 10 and again try inserting".
EDIT: In order to know how many times a user has executed the SP, you would have to insert into a log table, something like:
CREATE TABLE logSp (Username VARCHAR(200), ProcedureName VARCHAR(200), DateExecuted DATETIME)
And change the logic to this:
ALTER PROCEDURE sp_update (
#dno int,
#dname varchar(30)
)
AS
BEGIN
DECLARE #sp_name VARCHAR(200) = 'sp_update'
DECLARE #maxtimes VARCHAR(200) = 5
DECLARE #user VARCHAR(200) = SUSER_NAME()
DECLARE #times INT
SELECT #times = count(1)
FROM logSp
WHERE Username = #user
AND ProcedureName = #sp_name
IF #times = #maxtimes
BEGIN
PRINT 'Try Again'
RETURN
END
ELSE
BEGIN
INSERT INTO logSp values (#user, #sp_name, GETDATE())
END
IF NOT EXISTS (SELECT dno FROM dept WHERE #dno=dno)
BEGIN
INSERT INTO dept (dno,dname) VALUES (#dno,#dname)
END
ELSE
BEGIN
SET #dno=(SELECT MAX(dno) FROM dept)
SET #dno=#dno+10
INSERT INTO dept (dno,dname) VALUES (#dno,#dname)
END
END
Also, by setting the #dno variable to max(id) + 10 you guarantee that
the new deptno doesn't exist, yout don't have to try 5 times. If you want to try 5 times the INSERT you would have to use a while.

IF EXISTS (SELECT) in SQL Server not working as expected

I have a code like this:
IF EXISTS (SELECT * FROM table WHERE id = #id)
BEGIN
UPDATE table
SET stock = stock + #stock
WHERE id = #id
END
ELSE
BEGIN
INSERT INTO [table] ([id], [name], [stock])
VALUES (#id, #name, #stock)
END
But, this code isn't working and I am unable to find the root cause for the same. Can someone please help me?
I do not see any error in your code, I tried to replicate the process and it is working fine for me. Can you tell me what is the error you are facing exactly.
The following is the code I tried to replicate your scenario:
CREATE TABLE stocks (
id INT
,NAME VARCHAR(100)
,stock BIGINT
)
CREATE PROCEDURE InsertStocks #id INT
,#name VARCHAR(100)
,#stock BIGINT
AS
BEGIN
IF EXISTS (
SELECT *
FROM stocks
WHERE id = #id
)
BEGIN
UPDATE stocks
SET stock = stock + #stock
WHERE id = #id
END
ELSE
BEGIN
INSERT INTO stocks (
[id]
,[name]
,[stock]
)
VALUES (
#id
,#name
,#stock
)
END
END
INSERT INTO stocks
VALUES (
1
,'abc'
,200
)
INSERT INTO stocks
VALUES (
2
,'abc'
,300
)
INSERT INTO stocks
VALUES (
3
,'abc'
,500
)
EXEC Insertstocks 1
,'abc'
,700
This is updated successfully in my case.
table is a reserved keyword. so I guess you have a trivial syntax error: Incorrect syntax near the keyword 'table'. Wrap it with [], as you already did for INSERT statement
IF EXISTS (
SELECT * FROM [table] WHERE id = #id)
BEGIN
UPDATE [table] SET stock = stock + #stock
WHERE id = #id
END
ELSE
BEGIN
INSERT INTO [table] ([id]
,[name]
,[stock])
VALUES
(
#id,#name,#stock
)
END
Your code and syntax is correct. Let's see a sample example:
if EXISTS(select * from dbo.tbName where Id=1)
BEGIN
print 1
END
ELSE
BEGIN
print 2
END

Stored procedure only updates when running it from Management studio

So yea. I have this really weird problem. I have the following stored procedure
ALTER PROCEDURE [CP24SHOP].[sp_part_set_status_bulk]
#user nvarchar(max),
#doerTicket VARCHAR ( 200 ) = null,
#status int,
#items CP24SHOP.udt_parts READONLY
AS
BEGIN
SET NOCOUNT ON;
-- Check security
exec websocket.sp_validate_user
#user,
#doerTicket out
-- foreach row in #items, update the status
MERGE INTO [file].ItemPart WITH ( XLOCK, ROWLOCK ) AS target
USING ( SELECT
item.GID
FROM #items AS item
) AS source
ON ( target.GID = source.GID )
WHEN MATCHED THEN
UPDATE SET
target.[Status] = #status,
target.DateTimeModified = GETDATE();
select 'bob'
RETURN 0
END
and when I run it from Management Studio with this code
declare #user nvarchar(max) = 'websocket'
DECLARE #list CP24SHOP.udt_parts
INSERT INTO #list
(
GID
)
VALUES
(
-7228376
)
select [Status] from [file].ItemPart
where GID = -7228376
exec CP24SHOP.sp_part_set_status_bulk
#user = #user,
#items = #list,
#status = '155'
select [Status], DateTimeModified from [file].ItemPart
where GID = -7228376
it updates the status without problem
but when calling it through our websocket it runs the code and returns "bob" as it should, but when I check the database the status of the item hasn't updated. I'm clueless as to what might be wrong
err - I think you're missing a commit.
Looks to me like you are updating & then rolling back!

Function return table variable

I'm trying to create a function that return a table variable.So firstly i get data from Table1 and put it in another table variable. Here i want check if this variable isempty the function return the parameter result else return the result of the table variable
The function script is bellow :
USE[DATABase1]
GO
IF OBJECT_ID (N'CodeFunc', N'TF') IS NOT NULL DROP FUNCTION dbo.CodeFunc;
GO
CREATE FUNCTION CodeFunc ( #Code nvarchar(4) , #Table nvarchar(40) = '' )
RETURNS #VirtualDAT TABLE
(
RowID INT IDENTITY ( 1 , 1 ),
Code nvarchar(400)
)
AS
BEGIN
DECLARE #CodeM nvarchar(400)
DECLARE #imax INT SET #imax = ##ROWCOUNT
DECLARE #i INT SET #i = 1
DECLARE #SelectDAT TABLE
(
RowID INT IDENTITY ( 1 , 1 ),
Code nvarchar(400)
)
INSERT #SelectDAT
SELECT Code FROM table1
WHERE table1.id = 41
IF(EXISTS (SELECT 1 FROM #SelectDAT))
BEGIN
WHILE (#i <= #imax)
BEGIN
SELECT #CodeM = Code FROM #SelectDAT WHERE RowID = #i
INSERT INTO #VirtualDAT(Code) VALUES (#CodeM)
SET #i = #i + 1
END
END
ELSE
INSERT INTO #VirtualDAT(Code) VALUES (#Code)
RETURN
END
So this script works without put it inside function.
And i test this function like this :SELECT * FROM dbo.CodeFunc( 'toto',Default ) the result is :
IF(EXISTS (SELECT 1 FROM #SelectDAT)) no record returned
esle the result is ok
As VR46 says. The ##ROWCOUNT will be set to 0 because there is no query before it. Any code executing in a function happens as a seperate set of queries. It was probably returning a value outside the function because you had previously used the query window for another unrelated query
You could re-factor this function quite dramatically. Look below, ##ROWCOUNT will work here as it is just after the insert query and will definitely have a value based on the insert.
I have not been able to test this, but I think something like this should do the same job.
USE[DATABase1]
GO
IF OBJECT_ID (N'CodeFunc', N'TF') IS NOT NULL DROP FUNCTION dbo.CodeFunc;
GO
CREATE FUNCTION CodeFunc ( #Code nvarchar(4) , #Table nvarchar(40) = '' )
RETURNS #VirtualDAT TABLE
(
RowID INT IDENTITY ( 1 , 1 ),
Code nvarchar(400)
)
AS
BEGIN
insert into #VirtualDAT
Select Code from table1 where table1.id = 41
if ##ROWCOUNT = 0
begin
INSERT INTO #VirtualDAT(Code) VALUES (#Code)
end
RETURN
END
Since you are assigning #imax with ##ROWCOUNT right after declaration of variable will be initialized with zero.
From MSDN ##ROWCOUNT
Returns the number of rows affected by the last statement.
If am not wrong you need to assign value to #imax after the insert into..select query.
INSERT #SelectDAT
SELECT Code FROM table1
WHERE table1.id = 41
SET #imax= ##ROWCOUNT
You can do the same in SET BASED APPROACH without using while loop.
CREATE FUNCTION Codefunc (#Code NVARCHAR(4),
#Table NVARCHAR(40) = '')
returns #VirtualDAT TABLE (
rowid INT IDENTITY ( 1, 1 ),
code NVARCHAR(400))
AS
BEGIN
IF EXISTS (SELECT code
FROM table1
WHERE table1.id = 41)
BEGIN
INSERT INTO #VirtualDAT
(code)
SELECT code
FROM table1
WHERE table1.id = 41
END
ELSE
INSERT INTO #VirtualDAT
(code)
VALUES (#Code)
RETURN
END

IN Clause is not taking dynamic passed value in SQL 2008

I have below SQL Procedure where I am passing and setting dynamic values.
Code: SQL Procedure Name: GetArchivedData
ALTER PROCEDURE [dbo].[GetArchivedData](#PublicationURL varchar(100),#Number int,#Action varchar(max))
AS
DECLARE #TEST Varchar(max)
IF (#Action = 'ALL')
BEGIN
SET #TEST = '''ADD'''+','+'''UPD'''+','+'''DEL''';
END
ELSE
BEGIN
SET #TEST = #Action
END
IF (#Number !=0)
BEGIN
PRINT 'Inside'+ #TEST
BEGIN TRANSACTION TRAN1
SELECT
1 AS Tag,
NULL AS Parent,
NULL AS [root!1!],
NULL AS [Item!2!Id],
NULL AS [Item!2!Action],
NULL AS [Item!2!Publication_Id],
NULL AS [Item!2!Item_Reference_Id],
NULL AS [Item!2!Item_type],
convert( datetime, '9999-01-01' ) AS [Item!2!Last_Published_Date],
NULL AS [Item!2!Url],
NULL AS [Item!2!Schema_Id]
UNION
SELECT TOP (#Number)
2,
1,
'1',
T.ID,
T.ACTION,
T.PUBLICATION_ID,
T.ITEM_REFERENCE_ID,
T.ITEM_TYPE,
T.LAST_PUBLISHED_DATE,
T.URL,
T.SCHEMA_ID
FROM DBO.AUTN_ITEMS T WHERE FLAG=1 AND ACTION IN (#TEST) AND URL LIKE #PublicationURL+'%'
ORDER BY [Item!2!Last_Published_Date] DESC
FOR XML EXPLICIT
COMMIT TRANSACTION TRAN1
END
ELSE IF (#Number = 0)
BEGIN
PRINT 'Outside'+ #TEST
BEGIN TRANSACTION TRAN2
SELECT
1 AS Tag,
NULL AS Parent,
NULL AS [root!1!],
NULL AS [Item!2!Id],
NULL AS [Item!2!Action],
NULL AS [Item!2!Publication_Id],
NULL AS [Item!2!Item_Reference_Id],
NULL AS [Item!2!Item_type],
convert( datetime, '9999-01-01' ) AS [Item!2!Last_Published_Date],
NULL AS [Item!2!Url],
NULL AS [Item!2!Schema_Id]
UNION
SELECT
2,
1,
'1',
T.ID,
T.ACTION,
T.PUBLICATION_ID,
T.ITEM_REFERENCE_ID,
T.ITEM_TYPE,
T.LAST_PUBLISHED_DATE,
T.URL,
T.SCHEMA_ID
FROM DBO.AUTN_ITEMS T WHERE FLAG=1 AND ACTION IN (#TEST) AND URL LIKE #PublicationURL+'%'
ORDER BY [Item!2!Last_Published_Date] DESC
FOR XML EXPLICIT
COMMIT TRANSACTION TRAN2
END
RETURN
Excuting SQL Procedure:
DECLARE #return_value int
EXEC #return_value = [dbo].[GetArchivedData]
#PublicationURL = N'/in',
#Number = 0,
#Action = N'ALL'
SELECT 'Return Value' = #return_value
I can see the values are getting set properly, if I am printing it in the procedure however one value works perfectly but when I am setting SET #TEST = '''ADD'''+','+'''UPD'''+','+'''DEL'''; no results are returned
Please suggest!!
Use Table Variable instead of #Test as string like this,
DECLARE #ActionTbl table ([Action] varchar(3))
IF (#Action = 'ALL')
BEGIN
INSERT INTO #ActionTbl SELECT 'Add' AS ID
UNION ALL
SELECT 'UPD' AS ID
UNION ALL
SELECT 'DEL' ID
END
ELSE
BEGIN
INSERT INTO #ActionTbl VALUES(#Action)
END
And in query use
ACTION IN (Select Action from #ActionTbl)
Instead of
ACTION IN (#TEST)
First of all the main issue here - you CAN'T use IN to search one string in another. IN is used to search value in the row set of values. Also the #TEST STRING value is 'ADD,'UPD','DEL' (each item is with quotas). I guess the Action field contains values without quotas so additional y to other answers there is one more way:
Replace
ACTION IN (#TEST)
with
#TEST LIKE '%'''+ACTION+'''%'