Strange T-SQL if statement behaviour - sql

I have the following T-SQL if statement:
if #changeType = 'ChangeFrom'
begin
print 'yep'
end
else
begin
print 'nope'
end
If #changeType = 'ChangeFrom' I get yep.
If #changeType = 'ChangeTo' I get nope.
If #changeType ='ChangeFromfsjkfh' I get yep! Whats going on there?

What length have you declared the #ChangeType variable as - it looks like it's getting truncated?

Related

Breakline in Raiseerror

is there anyway to breakline and continue on next line in RaiseError?
<br/> and /n aren't working
Below is my code:
BEGIN
BEGIN TRY
SELECT 16/0
END TRY
BEGIN CATCH
WHILE(#Count != 0)
BEGIN
SET #MaterialName = (SELECT Material FROM #T WHERE Quantity = 0 AND ID = #Count)
SET #MSG = 'Material %s was not offered.'
RAISERROR(#MSG,16,1,#MaterialName);
SET #Count = #Count - 1
END
END CATCH
END
I dont want to make changes in the data access layer, we only support web, not window app. I want each loop msg to be printed on next line using sql only.
Is it possible?
if you include the line break in the text it will work.
SET #MSG = 'Material %s was not offered.
'
Note that the end quote is on a new line.
I'm no pro in sql but I think this will help
Char(10)
Inserts a new line break a good guide about it is here: https://www.itsupportguides.com/knowledge-base/sql-server/sql-how-to-insert-new-lineline-break-in-string/
I've also seen \n used before but I don't know if that can be used in your circumstance

SQL Server: Why does adding a null to a variable not cause an error?

I have a stored procedure that has a loop based on a counter. When the counter becomes NULL the loop ends without any error. Why doesn't SQL Server at least display a warning or error message like other programming languages?
Here is a code sample which exhibits the problem:
DECLARE #MasterCount int = 0;
DECLARE #Count int; -- initialized to NULL by SQL Server
PRINT 'Starting'
IF (#MasterCount IS NULL)
PRINT '#MasterCount IS NULL';
ELSE
PRINT '#MasterCount ' + CAST(#MasterCount AS varchar(10))
IF (#Count IS NULL)
PRINT '#Count IS NULL';
WHILE (#MasterCount IS NOT NULL)
BEGIN
SET #MasterCount += #Count;
IF ##ERROR <> 0 PRINT 'Error occured!'
PRINT 'Loop #Count ' + CAST(#Count AS varchar(10))
SET #Count -= 1;
END
IF ##ERROR <> 0 PRINT 'Error occured!'
IF (#MasterCount IS NULL)
PRINT '#MasterCount IS NULL';
ELSE
PRINT '#MasterCount ' + CAST(#MasterCount AS varchar(10))
PRINT 'Ending'
Produces the following output:
Starting
#MasterCount 0
#Count IS NULL
#MasterCount IS NULL
Ending
It doesn't raise an error because this is defined, documented behaviour.
If you have two apples and you know the weight of only one then it makes sense that the weight of both of them added together is not known.
You can actually get a warning to appear if you slightly alter the formulation.
Instead of
SET #MasterCount += #Count;
You could use
SELECT #MasterCount = SUM(C)
FROM (VALUES(#Count),
(#MasterCount )) V(C);
In which case it gives
Warning: Null value is eliminated by an aggregate or other SET
operation.
This does change the semantics however. As the null value was entirely ignored you would end up with #MasterCount simply being assigned back its original value rather than being set to null in your scenario.

T-SQL and decision making constructs

Please help me with the syntax of this query. It doesn't compile. It says there's a syntax error near the keyword END.
Obviously, I've got these BEGINs and ENDS mixed up.
I am using Microsoft SQL Server 2008 R2. I am not sure of the syntax of these BEGINS and ENDs.
Please don't mind the condition 1 = 0. That's something that will be replaced with a proper predicate later.
IF EXISTS (SELECT * FROM StringCategory WHERE ResourceKeyId = 18134 AND CategoryId = 0)
BEGIN
UPDATE StringCategory
SET CategoryId = 0
WHERE ResourceKeyId = 18134
END
ELSE
BEGIN
IF 1 = 0
BEGIN
DELETE FROM StringCategory WHERE ResourceKeyId = 18134
END
ELSE
BEGIN
INSERT INTO StringCategory
VALUES(18134, 0)
END
END
END
Your last END is an extra. You can think of the BEGINs and ENDs like { and } in C# for the IF constructs (They serve to mark the beginning and end of the block to be executed in the IF/ELSE statement).
Here is a much simpler way to do what you appear to be attempting.
insert into StringCategory
(ResourceKey, CategoryId)
select 18134, 0
where not exists (
SELECT *
FROM StringCategory
WHERE ResourceKeyId = 18134
AND CategoryId = 0)

Is there a way to exec a SP, check the result with an if statement and return the value, all within just one line?

I know this works but does anybody know of a way of doing all these in one line of code:
EXEC #ResultInt = NameofAStoredProcedure
IF #ResultInt <> 0
RETURN #ResultInt
LIKE:
-- I'm trying something like this but it does not work we want to do it all in
-- one line to do a Find-Replace throughout our code base
IF (EXEC #ResultInt = NameofAStoredProcedure) <> 0 RETURN ResultInt
Maybe I'm getting something wrong, but why not simply removing the whitespaces?
EXEC #ResultInt = NameofAStoredProcedure IF #ResultInt <> 0 RETURN #ResultInt;
If all you want is to have it in one line for easier editing, that should work. You even could include the declaration of #ResultInt:
DECLARE #ResultInt int; EXEC #ResultInt = NameofAStoredProcedure IF #ResultInt <> 0 RETURN #ResultInt;
Tested with MS SQL 2005.
Instead of trying to crush everything down to one row to make mass find and replaces easier,
I would recommend using regular expressions to handle carriage returns and line feeds while maintaining code readability. For instance, let's say you wanted to change "IF #ResultInt <> 0" to "IF #ResultInt <> 1" and add row "SET #ResultInt = 1", then do the following:
ENABLE REGULAR EXPRESSIONS IN FIND AND REPLACE
In Find and Replace in both Visual Studio and SQL Server Management Studio, expand the Find Options -> Select Use -> Select Regular expressions.
FIND WHAT STATEMENT
EXEC \#ResultInt = NameofAStoredProcedure\nIF \#ResultInt \<\> 0\nRETURN \#ResultInt
REPLACE WITH STATEMENT
EXEC \#ResultInt = NameofAStoredProcedure\nIF \#ResultInt \<\> 1\nSET \#ResultInt = 1\nRETURN \#ResultInt
RESULTS
EXEC #ResultInt = NameofAStoredProcedure
IF #ResultInt <> 1
SET #ResultInt = 1
RETURN #ResultInt
EDIT. As noted this doesn't work, but I'll leave it in place to show what you cannot do :)
Without chaining items all together onto one ugly line, the only way I can think of is to wrap the call in a Scalar Function.
But then it's not something you can re-use easily and will create allot of wrapper objects if your doing this allot. Is great way to keep code clean if your only doing this for one or two procedures, more then it just makes allot of objects to manage in the DB.
Example
CREATE FUNCTION dbo.SPWrapper() RETURNS int
AS
BEGIN
DECLARE #out int;
EXEC #out=NameOfStoredProcedure;
RETURN #out;
END
GO
-- Usage
IF (SELECT dbo.SPWrapper() ) <> 0

The "right" way to do stored procedure parameter validation

I have a stored procedure that does some parameter validation and should fail and stop execution if the parameter is not valid.
My first approach for error checking looked like this:
create proc spBaz
(
#fooInt int = 0,
#fooString varchar(10) = null,
#barInt int = 0,
#barString varchar(10) = null
)
as
begin
if (#fooInt = 0 and (#fooString is null or #fooString = ''))
raiserror('invalid parameter: foo', 18, 0)
if (#barInt = 0 and (#barString is null or #barString = ''))
raiserror('invalid parameter: bar', 18, 0)
print 'validation succeeded'
-- do some work
end
This didn't do the trick since severity 18 doesn't stop the execution and 'validation succeeded' is printed together with the error messages.
I know I could simply add a return after every raiserror but this looks kind of ugly to me:
if (#fooInt = 0 and (#fooString is null or #fooString = ''))
begin
raiserror('invalid parameter: foo', 18, 0)
return
end
...
print 'validation succeeded'
-- do some work
Since errors with severity 11 and higher are caught within a try/catch block another approach I tested was to encapsulate my error checking inside such a try/catch block. The problem was that the error was swallowed and not sent to the client at all. So I did some research and found a way to rethrow the error:
begin try
if (#fooInt = 0 and (#fooString is null or #fooString = ''))
raiserror('invalid parameter: foo', 18, 0)
...
end try
begin catch
exec usp_RethrowError
return
end catch
print 'validation succeeded'
-- do some work
I'm still not happy with this approach so I'm asking you:
How does your parameter validation look like? Is there some kind of "best practice" to do this kind of checking?
I don't think that there is a single "right" way to do this.
My own preference would be similar to your second example, but with a separate validation step for each parameter and more explicit error messages.
As you say, it's a bit cumbersome and ugly, but the intent of the code is obvious to anyone reading it, and it gets the job done.
IF (ISNULL(#fooInt, 0) = 0)
BEGIN
RAISERROR('Invalid parameter: #fooInt cannot be NULL or zero', 18, 0)
RETURN
END
IF (ISNULL(#fooString, '') = '')
BEGIN
RAISERROR('Invalid parameter: #fooString cannot be NULL or empty', 18, 0)
RETURN
END
We normally avoid raiseerror() and return a value that indicates an error, for example a negative number:
if <errorcondition>
return -1
Or pass the result in two out parameters:
create procedure dbo.TestProc
....
#result int output,
#errormessage varchar(256) output
as
set #result = -99
set #errormessage = null
....
if <errorcondition>
begin
set #result = -1
set #errormessage = 'Condition failed'
return #result
end
I prefer to return out as soon an possible, and see not point to having everything return out from the same point at the end of the procedure. I picked up this habit doing assembly, years ago. Also, I always return a value:
RETURN 10
The application will display a fatal error on positive numbers, and will display the user warning message on negative values.
We always pass back an OUTPUT parameter with the text of the error message.
example:
IF ~error~
BEGIN
--if it is possible to be within a transaction, so any error logging is not ROLLBACK later
IF XACT_STATE()!=0
BEGIN
ROLLBACK
END
SET #OutputErrMsg='your message here!!'
INSERT INTO ErrorLog (....) VALUES (.... #OutputErrMsg)
RETURN 10
END
I always use parameter #Is_Success bit as OUTPUT. So if I have an error then #Is_success=0. When parent procedure checks that #Is_Success=0 then it rolls back its transaction(with child transactions) and sends error message from #Error_Message to client.
As you can see from this answer history I followed this question and accepted answer, and then proceeded to 'invent' a solution that was basically the same as your second approach.
Caffeine is my main source of energy, due to the fact that I spend most of my life half-asleep as I spend far too much time coding; thus I didn't realise my faux-pas until you rightly pointed it out.
Therefore, for the record, I prefer your second approach: using an SP to raise the current error, and then using a TRY/CATCH around your parameter validation.
It reduces the need for all the IF/BEGIN/END blocks and therefore reduces the line count as well as puts the focus back on the validation. When reading through the code for the SP it's important to be able to see the tests being performed on the parameters; all the extra syntactic fluff to satisfy the SQL parser just gets in the way, in my opinion.