When should I use semicolons in SQL Server? - sql-server

While checking some code on the web and scripts generated by SQL Server Management Studio I have noticed that some statements are ended with a semicolon.
So when should I use it?

From a SQLServerCentral.Com article by Ken Powers:
The Semicolon
The semicolon character is a statement terminator. It is a part of the ANSI SQL-92 standard, but was never used within Transact-SQL. Indeed, it was possible to code T-SQL for years without ever encountering a semicolon.
Usage
There are two situations in which you must use the semicolon. The first situation is where you use a Common Table Expression (CTE), and the CTE is not the first statement in the batch. The second is where you issue a Service Broker statement and the Service Broker statement is not the first statement in the batch.

By default, SQL statements are terminated with semicolons. You use a semicolon to terminate statements unless you've (rarely) set a new statement terminator.
If you're sending just one statement, technically you can dispense with the statement terminator; in a script, as you're sending more than one statement, you need it.
In practice, always include the terminator even if you're just sending one statement to the database.
Edit: in response to those saying statement terminators are not required by [particular RDBMS], while that may be true, they're required by the ANSI SQL Standard. In all programming, if we can adhere to a Standard without loss of functionality, we should, because then neither our code or our habits are tied to one proprietary vendor.
With some C compilers, it's possible to have main return void, even though the Standard requires main to return int. But doing so makes our code, and ourselves, less portable.
The biggest difficulty in programming effectively isn't learning new things, it's unlearning bad habits. To the extent that we can avoid acquiring bad habits in the first place, it's a win for us, for our code, and for anyone reading or using our code.

You must use it.
The practice of using a semicolon to terminate statements is standard and in fact is a requirement
in several other database platforms. SQL Server requires the semicolon only in particular
cases—but in cases where a semicolon is not required, using one doesn’t cause problems.
I strongly recommend that you adopt the practice of terminating all statements with a semicolon.
Not only will doing this improve the readability of your code, but in some cases it can
save you some grief. (When a semicolon is required and is not specified, the error message SQL
Server produces is not always very clear.)
And most important:
The SQL Server documentation indicates that not terminating T-SQL statements with
a semicolon is a deprecated feature. This means that the long-term goal is to enforce use
of the semicolon in a future version of the product. That’s one more reason to get into the
habit of terminating all of your statements, even where it’s currently not required.
Source: Microsoft SQL Server 2012 T-SQL Fundamentals by Itzik Ben-Gan.
An example of why you always must use ; are the following two queries (copied from this post):
BEGIN TRY
BEGIN TRAN
SELECT 1/0 AS CauseAnException
COMMIT
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE()
THROW
END CATCH
BEGIN TRY
BEGIN TRAN
SELECT 1/0 AS CauseAnException;
COMMIT
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE();
THROW
END CATCH

In SQL2008 BOL they say that in next releases semicolons will be required. Therefore, always use it.
Reference:
Transact-SQL Syntax Conventions (Transact-SQL)
Deprecated Database Engine Features in SQL Server 2008 R2 ("Features Not Supported in a Future Version of SQL Server" section, "Transact-SQL" area)

If I read this correctly, it will be a requirement to use semicolons to end TSQL statements.
http://msdn.microsoft.com/en-us/library/ms143729%28v=sql.120%29.aspx
EDIT:
I found a plug-in for SSMS 2008R2 that will format your script and add the semicolons. I think it is still in beta though...
http://www.tsqltidy.com/tsqltidySSMSAddin.aspx
EDIT:
I found an even better free tool/plugin called ApexSQL...
http://www.apexsql.com/

Personal opinion: Use them only where they are required. (See TheTXI's answer above for the required list.)
Since the compiler doesn't require them, you can put them all over, but why? The compiler won't tell you where you forgot one, so you'll end up with inconsistent use.
[This opinion is specific to SQL Server. Other databases may have more-stringent requirements. If you're writing SQL to run on multiple databases, your requirements may vary.]
tpdi stated above, "in a script, as you're sending more than one statement, you need it." That's actually not correct. You don't need them.
PRINT 'Semicolons are optional'
PRINT 'Semicolons are optional'
PRINT 'Semicolons are optional';
PRINT 'Semicolons are optional';
Output:
Semicolons are optional
Semicolons are optional
Semicolons are optional
Semicolons are optional

I still have a lot to learn about T-SQL, but in working up some code for a transaction (and basing code on examples from stackoverflow and other sites) I found a case where it seems a semicolon is required and if it is missing, the statement does not seem to execute at all and no error is raised. This doesn't seem to be covered in any of the above answers. (This was using MS SQL Server 2012.)
Once I had the transaction working the way I wanted, I decided to put a try-catch around it so if there are any errors it gets rolled back. Only after doing this, the transaction was not committed (SSMS confirms this when trying to close the window with a nice message alerting you to the fact that there is an uncommitted transaction.
So this
COMMIT TRANSACTION
outside a BEGIN TRY/END TRY block worked fine to commit the transaction, but inside the block it had to be
COMMIT TRANSACTION;
Note there is no error or warning provided and no indication that the transaction is still uncommitted until attempting to close the query tab.
Fortunately this causes such a huge problem that it is immediately obvious that there is a problem. Unfortunately since no error (syntax or otherwise) is reported it was not immediately obvious what the problem was.
Contrary-wise, ROLLBACK TRANSACTION seems to work equally well in the BEGIN CATCH block with or without a semicolon.
There may be some logic to this but it feels arbitrary and Alice-in-Wonderland-ish.

According to Transact-SQL Syntax Conventions (Transact-SQL) (MSDN)
Transact-SQL statement terminator. Although the semicolon is not required for most statements in this version of SQL Server, it will be required in a future version.
(also see #gerryLowry 's comment)

It appears that semicolons should not be used in conjunction with cursor operations: OPEN, FETCH, CLOSE and DEALLOCATE. I just wasted a couple of hours with this. I had a close look at the BOL and noticed that [;] is not shown in the syntax for these cursor statements!!
So I had:
OPEN mycursor;
and this gave me error 16916.
But:
OPEN mycursor
worked.

When using either a DISABLE or ENABLE TRIGGER statement in a batch that has other statements in it, the statement just before it must end with a semicolon. Otherwise, you'll get a syntax error. I tore my hair out with this one... And afterwards, I stumbled on this MS Connect item about the same thing. It is closed as won't fix.
see here

If you like getting random Command Timeout errors in SQLServer then leave off the semi-colon at the end of your CommandText strings.
I don't know if this is documented anywhere or if it is a bug, but it does happen and I have learnt this from bitter experience.
I have verifiable and reproducible examples using SQLServer 2008.
aka -> In practice, always include the terminator even if you're just sending one statement to the database.

Note: This answers the question as written, but not the problem as stated. Adding it here, since people will be searching for it
Semicolon is also used before WITH in recursive CTE statements:
;WITH Numbers AS
(
SELECT n = 1
UNION ALL
SELECT n + 1
FROM Numbers
WHERE n+1 <= 10
)
SELECT n
FROM Numbers
This query will generate a CTE called Numbers that consists of integers [1..10]. It is done by creating a table with the value 1 only, and then recursing until you reach 10.

Semicolons do not always work in compound SELECT statements.
Compare these two different versions of a trivial compound SELECT statement.
The code
DECLARE #Test varchar(35);
SELECT #Test=
(SELECT
(SELECT
(SELECT 'Semicolons do not always work fine.';);););
SELECT #Test Test;
returns
Msg 102, Level 15, State 1, Line 5
Incorrect syntax near ';'.
However, the code
DECLARE #Test varchar(35)
SELECT #Test=
(SELECT
(SELECT
(SELECT 'Semicolons do not always work fine.')))
SELECT #Test Test
returns
Test
-----------------------------------
Semicolons do not always work fine.
(1 row(s) affected)

Related

Issue testing error handling with update statement inside a stored procedure

I am not able to understand why the code is not working. There is data in vendorproducts table, however no data is retrieved after executing and it is also not printing error when I am passing in #vendor=1
ALTER PROCEDURE HW5UD1
#vendor VARCHAR(64),
#Perct DECIMAL(3,2)
AS
BEGIN TRANSACTION
BEGIN TRY
UPDATE VendorProducts
SET paidPrice = paidPrice * (1 + #perct)
WHERE vendor = #vendor
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
PRINT 'Error occurred while trying to update vendor products table'
RETURN -11001
END CATCH
COMMIT TRANSACTION
SELECT *
FROM VendorProducts
WHERE vendor = #vendor
RETURN 0
Too long for a comment so let's start. First, learn to ask smart questions. How long did it take for you to reveal any error message? And did you post the complete message or an abbreviated version?
ALTER PROCEDURE HW5UD1
Start over. This name is useless. Anyone reading your code (or the code that calls this procedure) should have some idea of what your procedure does just by reading that name.
And let's look at how you call this procedure. You first said you are calling this procedure with "#vendor=1". And a bit later you say "vendor means vendor name like DVDemporium". Something seems wrong and inconsistent here. From a schema perspective, you should have a foreign key (FK) from VendorProducts to Vendors. Do you? Do you even have a Vendors table? And is the FK column varchar or numeric? I would expect numeric since you passed a value of 1.
Next, your procedure contains a single DML statement. A single statement (update - in this case) is atomic and will either be successful or not. There is no need for a transaction. If a transaction was started before this procedure was called, you have now nested a transaction for no particular reason. Worse, you catch any error and then hide the TRUE error while throwing one of your own. You just made debugging any actual errors impossible with the hiding. Error handling is complicated in tsql - I suggest you have a read Erland's discussion.
In your CATCH clause, you use print. Nope - don't. Do not attempt to provide "output" from a procedure (or trigger) using print. These are returned to the caller in a manner that most callers don't expect and won't process (either at all or as intended by you the developer). They simply generate extra work. It can be used for debugging purposes but should not be in production code. Because you throw an error, the caller implicitly knows that "Error occurred ..." - your print message adds nothing useful.
Your last statement in the procedure simply selects rows from the table you attempted to update. Why? If the caller has all that information when calling, this only creates extra "work". If the caller does not, then why do you assume the caller wants or needs this information. Generally speaking, your procedure should do one "thing". Just update the row(s).
So perhaps this is all an exercise for learning. Unfortunately people tend to provide working code without much explanation. Sure, you could just copy that and turn it in for a grade. But you will not have learned much (if any) and that is unfortunate. Without understanding and learning you will not develop the skills you need to be an effective developer.
And lastly, there are numerous discussions of best practices for tsql coding. These you should read and start implementing.

Conditional SQL block evaluated even when it won't be executed

I'm working on writing a migration script for a database, and am hoping to make it idempotent, so we can safely run it any number of times without fear of it altering the database (/ migrating data) beyond the first attempt.
Part of this migration involves removing columns from a table, but inserting that data into another table first. To do so, I have something along these lines.
IF EXISTS
(SELECT * FROM sys.columns
WHERE object_id = OBJECT_ID('TableToBeModified')
AND name = 'ColumnToBeDropped')
BEGIN
CREATE TABLE MigrationTable (
Id int,
ColumnToBeDropped varchar
);
INSERT INTO MigrationTable
(Id, ColumnToBeDropped)
SELECT Id, ColumnToBeDropped
FROM TableToBeModified;
END
The first time through, this works fine, since it still exists. However, on subsequent attempts, it fails because the column no longer exists. I understand that the entire script is evaluated, and I could instead put the inner contents into an EXEC statement, but is that really the best solution to this problem, or is there another, still potentially "validity enforced" option?
I understand that the entire script is evaluated, and I could instead put the inner contents into an EXEC statement, but is that really the best solution to this problem
Yes. There are several scenarios in which you would want to push off the parsing validation due to dependencies elsewhere in the script. I will even sometimes put things into an EXEC, even if there are no current problems, to ensure that there won't be as either the rest of the script changes or the environment due to addition changes made after the current rollout script was developed. Minorly, it helps break things up visually.
While there can be permissions issues related to breaking ownership changing due to using Dynamic SQL, that is rarely a concern for a rollout script, and not a problem I have ever run into.
If we are not sure that the script will work or not specially migrating database.
However, For query to updated data related change, i will execute script with BEGIN TRAN and check result is expected then we need to perform COMMIT TRAN otherwise ROLLBACK transaction, so it will discard transaction.

Use transactions for select statements?

I don't use Stored procedures very often and was wondering if it made sense to wrap my select queries in a transaction.
My procedure has three simple select queries, two of which use the returned value of the first.
In a highly concurrent application it could (theoretically) happen that data you've read in the first select is modified before the other selects are executed.
If that is a situation that could occur in your application you should use a transaction to wrap your selects. Make sure you pick the correct isolation level though, not all transaction types guarantee consistent reads.
Update :
You may also find this article on concurrent update/insert solutions (aka upsert) interesting. It puts several common methods of upsert to the test to see what method actually guarantees data is not modified between a select and the next statement. The results are, well, shocking I'd say.
Transactions are usually used when you have CREATE, UPDATE or DELETE statements and you want to have the atomic behavior, that is, Either commit everything or commit nothing.
However, you could use a transaction for READ select statements to:
Make sure nobody else could update the table of interest while the bunch of your select query is executing.
Have a look at this msdn post.
Most databases run every single query in a transaction even if not specified it is implicitly wrapped. This includes select statements.
PostgreSQL actually treats every SQL statement as being executed within a transaction. If you do not issue a BEGIN command, then each individual statement has an implicit BEGIN and (if successful) COMMIT wrapped around it. A group of statements surrounded by BEGIN and COMMIT is sometimes called a transaction block.
https://www.postgresql.org/docs/current/tutorial-transactions.html

SQL - update, delete, insert - Whatif scenerio

I was reading an article the other day the showed how to run SQL Update, Insert, or Deletes as a whatif type scenario. I don't remember the parameter that they talked about and now I can't find the article. Not sure if I was dreaming.
Anyway, does anyone know if there is a parameter in SQL2008 that lets you try an insert, update, or delete without actually committing it? It will actually log or show you what it would have updated. You remove the parameter and run it if it behaves as you would expect.
I don't know of a SQL2008 specific feature with any SQL service that supports transactions you can do this:
Start a transaction ("BEGIN TRANSACTION" in TSQL)
The rest of your INSERT/UPDATE/DELETE/what-ever code
(optional) Some extra SELECT statements and such if needed to output the result of the above actions, if the default output from step 2 (things like "X rows affected") is not enough
Rollback the transaction ("ROLLBACK TRANSACTION" in TSQL)
(optional) Repeat the testing code to show how things are without the code in step 2 having run
For example:
BEGIN TRANSACTION
-- make changes
DELETE people WHERE name LIKE 'X%'
DELETE people WHERE name LIKE 'D%'
EXEC some_proc_that_does_more_work
-- check the DB state after the changes
SELECT COUNT(*) FROM people
-- undo
ROLLBACK TRANSACTION
-- confirm the DB state without the changes
SELECT COUNT(*) FROM people
(you might prefer to do the optional "confirm" step before starting the transaction rather than after rolling it back, but I've always done it this way around as it keeps the two likely-to-be-identical sections of code together for easier editing)
If you use something like this rather then something SQL2008 specific the technique should be transferable to other RDBS too (just update the syntax if needed).
OK, finally figured it out. I've confused this with another project I was working on with PowerShell. PowerShell has a "whatif" parameter that can be used to show you what files would be removed before they are removed.
My apologies to those who have spent time trying to find an answer to this port and my thanks to those of you who have responsed.
I believe you're talking about BEGIN TRANSACTION
BEGIN TRANSACTION starts a local transaction for the connection issuing the statement. Depending on the current transaction isolation level settings, many resources acquired to support the Transact-SQL statements issued by the connection are locked by the transaction until it is completed with either a COMMIT TRANSACTION or ROLLBACK TRANSACTION statement. Transactions left outstanding for long periods of time can prevent other users from accessing these locked resources, and also can prevent log truncation.
Do you perhaps mean SET NOEXEC ON ?
When SET NOEXEC is ON, SQL Server
compiles each batch of Transact-SQL
statements but does not execute them.
When SET NOEXEC is OFF, all batches
are executed after compilation.
Note that this won't warn/indicate things like key violations.
Toad for SQL Server has a "Validate SQL" feature that checks queries against wrong table/column names etc. . Maybe you are talking about some new feature in SSMS 2008 similar to that...
I'm more than seven years late to this particular party but I suspect the feature in question may also have been the OUTPUT clause. Certainly, it can be used to implement whatif functionality similar to Powershell's in a t-sql stored procedure.
https://learn.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql
Use this in each insert/update/delete/merge query to let the SP output a meaningful resultset of the changes it makes e.g. outputting the table name and action performed as the first two columns then all the altered columns.
Then simply rollback the changes if a #whatif parameter is set to 1 or commit them if #whatif is set to 0.

Why is a trailing SET inconsistently throwing an error in SQL?

I'm looking at an error from one of our web applications, and it was calling a stored procedure that was responsible for updating a record in the database.
This stored procedure has worked for weeks with no issues. Then one day it started throwing errors, while debugging we found the cause to be inside the stored procedure.
It basically had a statement like this
Begin
// Do Stuff
Set
End
So the SET never actually set anything. For some reason this runs in perfectly fine on our server, and was running fine on a client server until earlier today when it decided to start complaining. (Incorrect Syntax error)
Is there any type of SQL Server setting that would cause this sudden change in behaviour?
Clarification - The SET has always been in the procedures. And running a SET by itself, or as a sole statement in a stored procedure does in fact work for me. This is the problem, it shouldn't work. So is there anything that would cause it to work when it should be failing?
A procedure with a SET like that would normally fail to compile, even if the SET cannot be reached:
alter procedure dbo.testproc as
begin
return 1;
set
end
Incorrect syntax near the keyword 'SET'.
Since the alter fails, I can't see how the procedure could end up in your database in the first place?
Or maybe you were running in compatibility mode for SQL Server 2000 (which still allowed this.) Changing the compatibility mdoe to SQL Server 2005 or higher would then break the procedure.
Executing "SET", by itself, will generate an error. I was originally going to suggest that you might have branching code (IFs, RETURNs, GOTOs, etc.) that caused the line to never be reached... but I find that I cannot create a stored procedure that contains this as a stand-alone statement.
If you script out the procedure and try to recreate it (with a different name), can it be created?
Might be worth posting that script, or as much of it as your are comfortable making public.

Resources