Empty statement in T-SQL - sql-server

Is there an empty statement keyword in T-SQL in Sql Server 2005 or newer? Something like NULL statement in PL/SQL.

You can declare a label to do nothing.
DECLARE #value INT
IF #value IS NULL
BEGIN
no_op1:
END

ugly happens sometimes. I believe their is a valid use. In a lengthy/complicated decision branching structure with several if else statements, some of those statements may contain conditions in which you specifically desire no action. You also don't want those condition falling thru do the default else, where certain work is done. In that case, it's a valid use.
Here are two ways to do this - see B and C
Declare #status as char(1)
set #status = 'D'
If (#status = 'A')
select 'Great!'
Else if (#status = 'B')
begin
if null=null select null -- predicate never resolves true
end
Else if (#status = 'C')
set #status = #status -- set a variable to itself
Else
select 'Needs work!'
Note, this is an over-simplified example. It is best used for readability when conditions are complex.

I also believe there are sometimes legitimate uses for a nothing script (automatically generated scripts for example).
Although it's an old thread, I'll put in my two cents. I think declaring a variable is one of the most benign statements you can use. The statement doesn't even show up in execution plans:
IF (#variable = 0)
BEGIN
DECLARE #placeHolder BIT;
END

No. There is not a "No operation" equivalent.
For a stored proc, you'd have at least SET NOCOUNT ON.
For an IF/ELSE, if a condition is empty omit it

while 1 = 0 break
In function I can not use:
if 1 = 0 print ''
if 1 = 0 set nocount on
if 1 = 0 waitfor delay '00:00'
if 1 = 0 throw 50000, '', 10
if 1 = 0 select 1
if 1 = 0 return
Labels or variables cannot be declared twice.

Related

When a SQL job starts, what is the value of ##ROWCOUNT?

I have been looking for an answer to this question, but I believe it may be a useful piece of information for others as well.
I am working with TSQL in SQL server management studio. Due to the way our system processes information, it's desirable to do updates in smaller batches. A trick we use is to wrap updates in a while loop as such:
while (##Rowcount <> 0)
begin
update top (800) etc etc
end
I have created a job to do this update regularly and while this works in a query window, it does not seem to work in a job. Is the rowcount value populated when a job begins?
##ROWCOUNT is 0 at the beginning of a statement, what is happening for you is that when SSMS first opens up a connection it executes a series of queries behind the scenes (you can capture the specific queries with a trace), so you get a residual value for ##ROWCOUNT of 1.
When doing batch updates like this, I tend to take a slightly different approach:
WHILE 1 = 1
BEGIN
UPDATE TOP (100) ....
SET ...
IF ##ROWCOUNT = 0
BREAK;
END
I don't think this has any benefit whatsoever over doing something like:
SELECT 1;
WHILE ##ROWCOUNT > 0
BEGIN
...
END
And is more long winded, but doing a pointless select or assignment just feels odd to me, perhaps owing to some minor OCD.
##Rowcount is system function with output INT NOT NULL --> default value for int is 0
You can get it by:
if ##rowcount = 0
print 1
else
print 0
But don't try select ##rowcount at first row it's statements that make a simple assignment and is always set the ##ROWCOUNT value to 1. :)
So solution is add select 0 before your while. It works because ##rowcount of select 0 is one row..
select 0
while (##Rowcount <> 0)
begin
update top (800) etc etc
end
Saving the value of ##RowCount in a variable allows you to avoid any assumption about the initial value and any problems with losing the value when another statement is executed.
declare #Samples as Table ( Sample Int );
insert into #Samples ( Sample ) values ( 1 ), ( 2 ), ( 5 );
declare #RowCount as Int;
-- If there is any work to do then initialize #RowCount to 1 , otherwise 0 .
set #RowCount = case when exists ( select 42 from #Samples where Sample < 10 ) then 1 else 0 end;
declare #NewSample as Int, #OldSample as Int;
-- Loop through updating one row at a time.
while #RowCount > 0
begin
update Ph
set #OldSample = Sample, #NewSample = Sample *= 2
from ( select top 1 Sample from #Samples where Sample < 10 order by Sample ) as Ph
set #RowCount = ##RowCount;
-- Do what you will without losing the count of rows updated.
select #RowCount as 'RowsProcessed', #OldSample as 'OldSample', #NewSample as 'NewSample'
end

Check if there are any records in a table which may not exist

I have to check if some records with specific conditions exist in a table which may not exist and I must do this in a scalar function.
Here is my code:
CREATE FUNCTION CheckIfRecordsExistInTestTable()
RETURNS INT
BEGIN
DECLARE #Result INT
SELECT #Result =
CASE WHEN OBJECT_ID('TestTable') IS NULL THEN 0
ELSE
CASE WHEN EXISTS(SELECT * FROM TestTable) THEN
1
ELSE
0
END
END
RETURN #Result
END
While trying it in SQL Server executing the following statement:
SELECT dbo.CheckIfRecordsExistInTestTable()
Whenever TestTable exists it returns my expected result. But whenever it does not, SQL Server raises an exception (Invalid object name 'TestTable') and I cannot get what I expect (I want a zero return value in this situation).
So what do you propose to do for this problem which can be coded to a scalar function?
The other answer gives a correct workaround.
As to why you are getting the problem...
This is a compile time error.
If the statement references a non existent object compilation is deferred until just before execution, but still eventually the whole statement needs to be compiled into an execution plan before it is executed.
This fails when the table doesn't exist and execution of that statement doesn't even begin.
(Execution plan that it tries to create - using a passthru predicate to avoid evaluation of the condition if the CASE not met)
In the workaround the SELECT against testtable is moved into a different statement. Compilation of this statement is still deferred and as the statement is never executed all works fine.
Try changing the function like this
CREATE FUNCTION Checkifrecordsexistintesttable()
returns INT
BEGIN
DECLARE #Result INT
IF Object_id('TestTable') IS NULL
SET #Result = 0
ELSE
SELECT #Result = CASE
WHEN EXISTS(SELECT 1 FROM testtable) THEN 1
ELSE 0
END
RETURN #Result
END;
To know more about the reason behind the error you are getting check Martin's answer.
update function like this:
CREATE FUNCTION CheckIfRecordsExistInTestTable()
RETURNS INT
BEGIN
DECLARE #Result INT
SELECT #Result = case when count(1) = 0 then 0 else 1 end from sys.tables where name = 'TestTable'
RETURN #result
END

SQL Server : Select Local Variable After Some Process

CREATE PROCEDURE dbo.test2
AS
BEGIN
DECLARE #status as int
DECLARE #error as int
SET #status = 1
SET #Error = ##ERROR
UPDATE dbo.BView
SET bview='dar'
WHERE pt='foo'
IF #Error > 0
print 'ERROR'
else
SELECT #status as 'status'
END;
Why does this query always return the default value of the local variable #status regardless of what was assigned to it?
status
------
0
Server version: MS SQL Server 2014 Express
In sql server 2005 : Run this in your sql server.
You cannot assign a default value at declaration.
and post the result in command.
DECLARE #status int
SET #status = 1
SELECT #status as 'status'
Suggestions. Although can't find some thing amiss, why not do it the normal way.
Always Declare a variable first before your update,insert or any action.
CREATE PROCEDURE dbo.sp_test2
AS
BEGIN
DECLARE #status as int
SET #status = 1
UPDATE dbo.BView
SET bview='dar'
WHERE pt='foo'
SELECT #status as 'status'
END
Second Always use the keyword AS then variable type , the followed by the size. If you dont follow by the variable size, it mostly return 0, note nvarchar types/varchar types mostly. Example
DECLARE #status as nvarchar
Often return 0 when use against a string of length 20 . Where as ,
Declare #status as nvacrhar(20)
Works fine.
There might exist an error in your update/Insert etc processes and i can see u put the declaration and the select process in the same node(i.e begin and end). You can check for an error like.
CREATE PROCEDURE dbo.sp_test2
AS
BEGIN
DECLARE #status as int
DECLARE #error as int
SET #status = 1
SET #Error = ##ERROR
--Some statement, update or insert, let's say:
UPDATE dbo.BView
SET bview='dar'
WHERE pt='foo'
IF #Error > 0
print 'ERROR'
else
SELECT #status as 'status'
END
These are suggestions though, I often do this way and it never fails me. FOr better understanding of variable in SQL Here and . Raising Errors in MSSql here and many more. Hope it helps.

SQL Server 2008 R2 - Scalar UDF results in infinite loop

The following code is resulting in an infinite loop or really really slow execution:
CREATE FUNCTION [dbo].[CleanUriPart]
(
-- Add the parameters for the function here
#DirtyUriPart nvarchar(200)
)
RETURNS nvarchar(200)
AS
BEGIN;
-- Declare the return variable here
DECLARE #Result nvarchar(200);
DECLARE #i int;
SET #i = 1;
WHILE 1 = 1
BEGIN;
SET #i = PATINDEX('%[^a-zA-Z0-9.~_-]%', #DirtyUriPart COLLATE Latin1_General_BIN);
IF #i > 0
SET #DirtyUriPart = STUFF(#DirtyUriPart, #i, 1, '-');
ELSE
BREAK;
END;
-- Add the T-SQL statements to compute the return value here
SELECT #Result = #DirtyUriPart;
-- Return the result of the function
RETURN #Result;
END;
The input/output should be as follows:
'abcdef' -> 'abcdef' works ok
'abc-def' -> 'abc-def' results in infinite loop
'abc*def' -> 'abc-def' results in infinite loop
etc.
Please help!
SELECT PATINDEX('%[^a-]%', N'aaa-def' COLLATE Latin1_General_BIN),
PATINDEX('%[^-a]%', N'aaa-def' COLLATE Latin1_General_BIN),
PATINDEX('%[^a-]%', 'aaa-def' COLLATE Latin1_General_BIN),
PATINDEX('%[^-a]%', 'aaa-def' COLLATE Latin1_General_BIN)
Returns
----------- ----------- ----------- -----------
1 5 5 5
So it seems that for varchar datatypes a trailing - is treated as being part of a set whereas for nvarchar it is ignored (treated as a malformed range as a is ignored too?)
The BOL entry for LIKE doesn't explicitly talk about how to use - within [] to get it to be treated as part of a set but does have the example
LIKE '[-acdf]'
to match -, a, c, d, or f so I assume that it needs to be the first item in a set (i.e. that [^a-zA-Z0-9.~_-] needs to be altered to [^-a-zA-Z0-9.~_]). That also matches the result of my testing above.
Any chance #DirtyUriPart can evaluate to NULL? ON the PATINDEX function, if either pattern or expression is NULL, PATINDEX returns NULL and a NULL in this case will cause a infinite loop
It looks like you could fix the problem by casting #DirtyUriPart as VARCHAR(200) in PATINDEX, which will cause the dash to be recognized along with the other characters in the class:
DECLARE #DirtyUriPart nvarchar(200)='abc-def';
-- Returns 0
SELECT PATINDEX('%[^a-zA-Z0-9.~_-]%', CAST(#DirtyUriPart AS VARCHAR(200)) COLLATE Latin1_General_BIN);
-- Returns 4
SELECT PATINDEX('%[^a-zA-Z0-9.~_-]%', #DirtyUriPart COLLATE Latin1_General_BIN);

Case statement in SQL Server 2005

I am not sure what to use in this scenario, but I think a Case statement is apt.
I do not know the syntax however. Can someone please guide me?
I have a variable called #Action which can have about 30 different values.
I want to do something like this
CASE
WHEN #Action = 'InsertTbl1' THEN
BEGIN
-- Some Insert statements and one update statements
END
WHEN #Action = 'RecalculateCol3' THEN
BEGIN
-- Some update statements
END
WHEN #Action = 'Closed' THEN
BEGIN
-- Some delete statements and some update statements
END
--- and so on.....
ELSE
BEGIN
END
END
Suggest a structure of IF and ELSE IF to mimic a switch.
IF #MyVar = 'Foo'
BEGIN
--react to Foo
END
ELSE IF #MyVar = 'Bar'
BEGIN
--react to Bar
END
ELSE
BEGIN
--default case.
END
Yes, you can use Else If. For example:
declare #temp int
set #temp = 3
if #temp = 1
print '1'
else if #temp > 1 and #temp < 3
print '2'
else if #temp >= 3
print '3'
I would still think about breaking it up into separate procedures as suggested by others

Resources