Problems with IF EXISTS() - sql

When I try to run update a credit card type by passing in the #ID and #Name I get an error that says:
Msg 2786, Level 16, State 1, Procedure sp_SaveCreditCardType, Line 29
The data type of substitution parameter 1 does not match the expected type of the format specification.
The problem is with my piece of code that checks for the existence of the id in the CreditCardTypes table using this statement:
-- make sure the ID is a valid number
IF NOT EXISTS (SELECT * FROM CreditCardTypes WHERE ID = #ID)
BEGIN
RAISERROR('The Credit Card ID ''%s'' does not exist. Update Failed.', 15, 1, #ID)
RETURN -100
END
Does anyone have any idea why this may be giving me an error? I have seen many examples of using the the if exists() in this way, but for some reason it's giving me an error.
Here is the entire proc.
CREATE PROCEDURE dbo.sp_SaveCreditCardType
(
#ID int = null,
#Name varchar(50),
#Description varchar(150) = null
)
AS
DECLARE
#Err INT
BEGIN
SET NOCOUNT ON
-- check to make sure a Name was passed in
IF #Name IS NULL
BEGIN
RAISERROR('A Name was not specified. Execution aborted.', 15, 1, #Name)
RETURN -100
END
-- check to see if an ID is passed
IF #ID IS NOT NULL AND #ID <> 0
BEGIN
-- make sure the ID is a valid number
IF NOT EXISTS (SELECT * FROM CreditCardTypes WHERE ID = #ID)
BEGIN
RAISERROR('The Credit Card ID ''%s'' does not exist. Update Failed.', 15, 1, #ID)
RETURN -100
END
-- update an existing credit card type
UPDATE CreditCardTypes
SET Name = #Name,
[Description] = #Description
WHERE ID = #ID
SET #Err = ##ERROR
IF #Err <> 0 GOTO ErrorHandler
END
ELSE
BEGIN
-- first check to make sure the credit card type doesn't already exist
IF NOT EXISTS (SELECT * FROM CreditCardTypes WHERE Name = #Name)
BEGIN
-- insert a new credit card type
INSERT INTO CreditCardTypes (Name, [Description])
VALUES (#Name, #Description)
SET #Err = ##ERROR
IF #Err <> 0 GOTO ErrorHandler
END
ELSE
RAISERROR('The Credit Card Type ''%s'' already exists. Insert failed.', 15, 1, #Name)
RETURN -100
END
SET #Err = ##ERROR
IF #Err <> 0 GOTO ErrorHandler
RETURN 0
ErrorHandler:
RAISERROR('An error occured while saving the credit card type ''%s''', 16, 1, #Name) WITH LOG
RETURN -100
END
GO

Change:
RAISERROR('The Credit Card ID ''%s'' does not exist. Update Failed.', 15, 1, #ID)
To:
RAISERROR('The Credit Card ID ''%d'' does not exist. Update Failed.', 15, 1, #ID)
%s is used for substituting strings... but %d is the substitution parameter for ints.
RAISERROR in MSDN

Related

Checking if name already exists and also type of name in SQL Server

I am checking for if name exist in the stored procedure. So basically checking if attr_name exists in the ibui_attribute table.
Here the attr_name can be either of the two types. There is column in the table called is_group that tells you if the attribute is a group or an attribute. I need to throw the error accordingly and hence written and if else condition. Do I need to write a query again to check if the attribute is group or an attribute for the if else condition or can I do that in the if exist SQL statement that has been written
CREATE PROCEDURE [IBT].[save_ibui_attribute]
#p_attr_id INT = NULL,
#p_is_group BIT = 0,
#p_attr_name VARCHAR (20),
#p_attr_desc VARCHAR (250),
#p_parent_attr_id INT,
#p_attr_level VARCHAR (20),
#p_attr_data_type VARCHAR (8),
#p_last_updt_user_nm VARCHAR (30) = NULL,
#p_last_updt_dtime DATETIME = NULL,
#debug AS BIT = 0
AS
BEGIN
BEGIN TRY
SET NOCOUNT ON;
DECLARE
#logfile VARCHAR (MAX),
#msg VARCHAR (255);
SET #msg = 'IBT.save_ibui_attribute Starts';
IF #debug = 1
BEGIN
SELECT 'IBT.save_ibui_attribute DEBUG START';
END;
IF #p_attr_id IS NULL
BEGIN
IF EXISTS (SELECT attr_name
FROM IBT.ibt_attribute
WHERE UPPER(attr_name) = UPPER(#p_attr_name))
BEGIN
IF ATTRIBUTE
RAISERROR ( 50001, 16, 1, 'ERROR! : New attribute has a name that is already in use. Please use a different attribute name' );
ELSE
IF GROUP
RAISERROR (50001, 16, 1, 'ERROR! : New group has a name that is already in use. Please use a different group name' );
END;
END;
END;
Do I do something like this
declare #isGroup bit;
IF #p_attr_id IS NULL
BEGIN
SET #isGroup = (SELECT is_group
FROM IBT.ibt_attribute
WHERE UPPER(attr_name) = UPPER(#p_attr_name))
IF #isGroup = 0
RAISERROR ( 50001, 16, 1, 'ERROR! : New attribute has a name that is aleady in use. Please use a different attribute name' );
ELSE IF #isGroup = 1
RAISERROR ( 50001, 16, 1, 'ERROR! : New group has a name that is aleady in use. Please use a different group name' );
END;
END;

Subquery returns more than one value?

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

How to union multiple select statements while they are not near together?

I have an sql query which check for existence of some records, if those records exist rise error for them otherwise insert them to database. In my query as I need to return error messages for every record, I need to select some custom texts, problem is that they are showing as separate tables, not in one table, which I want (as I am calling this query from nodejs app and it returns an array for me so it only returns first table (error message) for me).
I searched and reach these two options:
1- Use UNION (which is not solving my case)
2- Insert all records in another table and then get all it's record (which isn't beautiful! :) )
DECLARE #errorCOUNT int
SET #errorCOUNT = 0
BEGIN TRANSACTION [Tran1]
IF EXISTS (SELECT * FROM Categories WHERE CategoryName = 'myCat1')
BEGIN
SELECT 'This is error for is = 4' As err
SET #errorCOUNT = #errorCOUNT + 1
END
ELSE
BEGIN
INSERT INTO Categories VALUES ('myCat1')
END
----------------------------
IF EXISTS (SELECT * FROM Categories WHERE CategoryName = 'myCat2')
BEGIN
SELECT 'This is error for is = 5' AS err
SET #errorCOUNT = #errorCOUNT + 1
END
ELSE
BEGIN
INSERT INTO Categories VALUES ('myCat2')
END
----------------------------
IF #errorCOUNT > 0
BEGIN
ROLLBACK TRANSACTION [Tran1]
END
ELSE
BEGIN
COMMIT TRANSACTION [Tran1]
END
As I mentioned I want all these select statements to be shown in one table so they return to my server as one array.
I just think it is good to mention that my query completes in a loop, so it may have different amount of IF...ELSE (between --- lines).
I hope I was clear. Thanks in advance.
Try this one, would work:
BEGIN TRANSACTION [Tran1]
DECLARE #err AS TABLE ( msg NVARCHAR(MAX) NOT NULL )
DECLARE #errorCOUNT AS INT = 0
IF EXISTS (SELECT * FROM Categories WHERE CategoryName = 'myCat1')
BEGIN
INSERT INTO #err (msg) VALUES ('This is error for is = 4')
SET #errorCOUNT = #errorCOUNT + 1
END
ELSE
BEGIN
INSERT INTO Categories VALUES ('myCat1')
END
IF EXISTS (SELECT * FROM Categories WHERE CategoryName = 'myCat2')
BEGIN
INSERT INTO #err (msg) VALUES ('This is error for is = 5')
SET #errorCOUNT = #errorCOUNT + 1
END
ELSE
BEGIN
INSERT INTO Categories VALUES ('myCat2')
END
IF #errorCOUNT > 0
BEGIN
SELECT * FROM #err
ROLLBACK TRANSACTION [Tran1]
END
ELSE
BEGIN
COMMIT TRANSACTION [Tran1]
END
I don't understand what you're really want to do there, but here is a tip using MERGE statement and OUTPUT clause maybe it's what you're after
DECLARE #T TABLE(CategoryName VARCHAR(45));
MERGE INTO T
USING (VALUES('MyCat1'), ('MyCat2')) TT(CategoryName)
ON T.CategoryName = TT.CategoryName -- Or <> instead of =
WHEN NOT MATCHED THEN
INSERT VALUES(TT.CategoryName)
OUTPUT TT.CategoryName INTO #T;
SELECT CASE WHEN CategoryName = 'MyCat1'
THEN 'This is error for is = 4'
WHEN CategoryName = 'MyCat2'
THEN 'This is error for is = 5'
END Res
FROM #T;
Also, I don't think you need to the #ErrorCount variable, since you already have ##ROWCOUNT which you can use it instead.
Here is a db<>fiddle where you can see how it's working.

expression of non-boolean type specified when merging?

I have the following code to create a stored procedure, but I seem to be getting a lot of errors below this line:
ON (Target.Email = Source.Email AND Target.PromoName = Source.PromoName)
The error on this line is "expression of non-boolean type specified".
What am I doing wrong here?
CREATE PROCEDURE [dbo].InsertUpdateBasicPromoEntry
#Email nvarchar(100)
,#PromoName varchar(100)
,#MaxEntries tinyint
AS
BEGIN
SET NOCOUNT ON;
MERGE [dbo].[BasicCodePromoEntries] AS Target
USING (
SELECT
#Email as Email
,#PromoName as PromoName
) AS Source
ON (Target.Email = Source.Email AND Target.PromoName = Source.PromoName)
WHEN MATCHED
THEN
BEGIN
IF Target.PeriodExpires < GETDATE()
BEGIN
UPDATE SET
Target.PeriodExpires = GETDATE()
,Target.PeriodEntries = 1
END
ELSE IF Target.PeriodExpires > GETDATE() AND Target.PeriodEntries < #MaxEntries
BEGIN
UPDATE SET
Target.PeriodEntries = Target.PeriodEntries + 1
END
ELSE
BEGIN
RETURN 1
END
RETURN 0
END
WHEN NOT MATCHED BY Target
THEN
BEGIN
INSERT INTO Target (Email, PromoName, PeriodEntries, PeriodExpires) VALUES (#Email, #PromoName, 1, DATEADD(day, 1, GETDATE()))
RETURN 0
END
END
PS: Every BEGIN and END is underlined, saying, incorrect syntax near...

Stored Procedure that accepts all table fields and updates those values

I'm working through a couple practice questions and I've run across this problem, I keep getting an error when trying to execute the procedure that says
Msg 156, Level 15, State 1, Line 1
Incorrect syntax near the keyword 'Procedure'."
Can someone please help?
Write a procedure UpdateTitle that accepts all the Title table columns and will update the title with those values. Raise error messages for the following: The ISBN does not exist The Category and/or Publisher Codes are not valid.
Create PROCEDURE UpdateTitle (#ISBN char(10), #SuggestedPrice smallmoney,#NumberInStock smallint,#PublisherCode int,#CategoryCode int)
AS
BEGIN
IF #ISBN is null or #CategoryCode is null or #PublisherCode is null
BEGIN
RAISERROR ('ISBN,CategoryCode, or PublisherCode is not valid please enter valid data',16,1)
END
ELSE
BEGIN
IF (SELECT COUNT(*) FROM Title WHERE ISBN = #ISBN) = 0
BEGIN
RAISERROR ('ISBN does not exist.',16,1)
END
ELSE
BEGIN
SELECT 'Table Sucessfully Updated.';
UPDATE Title
SET SuggestedPrice = #SuggestedPrice
WHERE ISBN = #ISBN;
BEGIN
IF (SELECT COUNT(*) FROM Title WHERE ISBN = #ISBN) = 0
BEGIN
RAISERROR ('ISBN does not exist.',16,1)
END
ELSE
BEGIN
SELECT 'Table Sucessfully Updated.';
UPDATE Title
SET NumberInStock = #NumberInStock
WHERE ISBN = #ISBN;
END
BEGIN
IF (SELECT COUNT(*) FROM Title WHERE ISBN = #ISBN) = 0
BEGIN
RAISERROR ('ISBN does not exist.',16,1)
END
ELSE
BEGIN
SELECT 'Table Sucessfully Updated.';
UPDATE Title
SET PublisherCode = #PublisherCode
WHERE ISBN = #ISBN;
END
BEGIN
IF (SELECT COUNT(*) FROM Title WHERE ISBN = #ISBN) = 0
BEGIN
RAISERROR ('ISBN does not exist.',16,1)
END
ELSE
BEGIN
SELECT 'Table Sucessfully Updated.';
UPDATE Title
SET CategoryCode = #CategoryCode
WHERE ISBN = #ISBN;
END
END
END
END
END
END
END
GO
Then
Execute Procedure UpdateTitle #ISBN ='1021031040', #suggestedproce ='40' , #NumberInStock ='10', #PublisherCode = '200', #CategoryCode = '1'
Execute Procedure UpdateTitle ...
Should be:
EXEC dbo.UpdateTitle ...
Some other comments:
ISBN is no longer limited to 10 characters (this change happened in 2007, if you believe WikiPedia).
Always use the schema prefix when creating or referencing objects.
You only need to check that the ISBN is valid once. And you shouldn't do so using a count IMHO, especially since - presumably - that is the key and it could only ever return 0 or 1 anyway.
You shouldn't select "update successful" and then perform the update. You should make sure the update was successful before telling the user it was successful.
There is no reason to separate this out into multiple updates either.
Please be liberal with carriage returns, indenting and whitespace. The value in readability is worth the extra cost in typing it (since you only type it once, but you will read it multiple times).
Use RETURN; as an exit mechanism so that you don't have to nest IF and ELSE multiple times.
Always use SET NOCOUNT ON; at the beginning of your procedures.
You probably want to customize the message to tell the user which parameter(s) were invalid.
Oh yeah, and please future-proof your code by using semi-colons to terminate statements.
Here is a much more concise version that satisfies all of your requirements:
CREATE PROCEDURE dbo.UpdateTitle
#ISBN CHAR(10),
#SuggestedPrice SMALLMONEY,
#NumberInStock SMALLINT,
#PublisherCode INT,
#CategoryCode INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #msg VARCHAR(255);
IF #ISBN IS NULL OR #CategoryCode IS NULL OR #PublisherCode IS NULL
BEGIN
SELECT #msg = 'The following parameter(s) were invalid:'
+ CASE WHEN #ISBN IS NULL THEN ' #ISBN' ELSE '' END
+ CASE WHEN #CategoryCode IS NULL THEN ' #CategoryCode' ELSE '' END
+ CASE WHEN #PublisherCode IS NULL THEN ' #PublisherCode' ELSE '' END;
RAISERROR (#msg, 11, 1);
RETURN;
END
IF NOT EXISTS (SELECT 1 FROM dbo.Title WHERE ISBN = #ISBN)
BEGIN
SET #msg = 'ISBN %s does not exist.';
RAISERROR(#msg, 11, 1, #ISBN);
RETURN;
END
BEGIN TRY
UPDATE dbo.Title
SET SuggestedPrice = #SuggestedPrice,
NumberInStock = #NumberInStock,
PublisherCode = #PublisherCode,
CategoryCode = #CategoryCode
WHERE ISBN = #ISBN;
SELECT 'Update successful.';
END TRY
BEGIN CATCH
SET #msg = ERROR_MESSAGE();
RAISERROR(#msg, 11, 1);
END CATCH
END
GO