Consider following script:
IF OBJECT_ID('tempdb.dbo.#INTERMED', 'U') IS NOT NULL
DROP TABLE #INTERMED;
IF OBJECT_ID('tempdb.dbo.#INTERMED1', 'U') IS NOT NULL
DROP TABLE #INTERMED1;
PRINT 'Inserting INTO #INTERMED'
GO
SELECT 11 AS Col1
INTO #INTERMED
RETURN -- Why does execution continue below this line?
PRINT 'Inserting INTO #INTERMED1' -- This doesn't print anything
GO
SELECT 'Testing testing 123' AS Col2
INTO #INTERMED1
SELECT * FROM #INTERMED1 i
When you run it in SSMS you will notice that RETURN is ignored, PRINT statement after RETURN doesn't do anything and then execution continues.
Can someone explain why? I would expect it to exit immediately after RETURN.
I did find that it is somehow related to GO statements because if I commented out all GO statements it behaves as expected (exits after RETURN) but I still don't have an explanation.
GO is not part of the SQL Language. It's a batch separator used by Management Studio, and adopted as a convention by some other tools as well, but it has no special meaning in the language itself. Try to use it in a stored procedure and see what I mean.
Therefore, what happens is you have one batch the looks like this:
IF OBJECT_ID('tempdb.dbo.#INTERMED', 'U') IS NOT NULL
DROP TABLE #INTERMED;
IF OBJECT_ID('tempdb.dbo.#INTERMED1', 'U') IS NOT NULL
DROP TABLE #INTERMED1;
PRINT 'Inserting INTO #INTERMED'
It does it's thing, and then you have a new batch that looks like this:
SELECT 11 AS Col1
INTO #INTERMED
RETURN -- Why does execution continue below this line?
PRINT 'Inserting INTO #INTERMED1' -- This doesn't print anything
It runs to the RETURN statement, at which point the batch, and only that batch, returns/finishes. However, there is still one more batch to run:
SELECT 'Testing testing 123' AS Col2
INTO #INTERMED1
SELECT * FROM #INTERMED1 i
Again, this is a whole new batch. The previous RETURN statement means nothing. It's like you called three methods in sequence.
I also saw this in the comments:
The reason I had GO in it is to actually have PRINT statements output something while script is still executing.
There's a better way. Look into the RAISERROR statement:
RAISERROR('My Progress Message',0,1) WITH NOWAIT
Related
I'd like to write a stored procedure for MS SQL Server 2008 that removes rows from a table... but if a certain argument is supplied, it would show what would be removed if it were ran normally at that moment, instead of actually removing.
For returning what would be removed, I could of course copy everything to be removed into a temp table and return the table.
But is there a built-in function that would allow that functionality?
I know execution plans can be helpful for debugging, but I don't see anything in the docs that come close to doing this (see esp here and here, also SO question and answers here)
You could follow the T-SQL given below, where I am using a sample table TABLE1 from which records need to be deleted.
The clause OUTPUT Deleted.* is a standard SQL Server feature that will automatically return all rows being deleted.
Automatically show all rows being deleted
--boolean parameter that tells whether to just show rows being deleted
--OR to actually delete the rows
DECLARE #delete BIT = 0;
BEGIN TRAN
DELETE FROM TABLE1
OUTPUT DELETED.*;
IF #delete = 0
BEGIN
ROLLBACK TRAN
END
ELSE
BEGIN
COMMIT TRAN
END
One way is to create three procedures for this. One is the "driver" procedure which decides which procedure to execute. This is important because execution plans can be a real problem when there are multiple execution paths. You can read more in depth about the performance implications here. http://sqlinthewild.co.za/index.php/2009/09/15/multiple-execution-paths/
Something like this should work for you.
create procedure MyProc
(
#DeleteData bit
) as
if #DeleteData = 1
exec MyDeleteProc
else
exec MySelectProc
I have a SQL trigger on a table, which will fire after insert, update and delete.
I insert all the affected records in a separate physical table with codes defining the state of update. Following code snippet is the trigger defined.
CREATE TRIGGER [dbo].[DATA_CACHE]
ON [dbo].[DATA_USAGE]
for Insert,Update,Delete
AS
BEGIN
if(select COUNT(*) from inserted)>0
begin
if (select COUNT(*) from deleted)>0
BEGIN
--update
INSERT INTO CACHE_UPDATE_TABLE (CODE, ID, DATE, COUNT)
SELECT 2, ins.ID, ins.DATE, ins.COUNT
from inserted ins
END
else
begin
-- insert
INSERT INTO CACHE_UPDATE_TABLE (CODE, ID, DATE, COUNT)
SELECT 1, ins.ID, ins.DATE, ins.COUNT
from inserted ins
end
END
else
BEGIN
-- delete
INSERT INTO CACHE_UPDATE_TABLE (CODE, ID, DATE, COUNT)
SELECT 3, del.ID, del.DATE, del.COUNT
from deleted del
end
END
SELECT * FROM CACHE_UPDATE_TABLE
As you can see in the above trigger i had added an additional statement after the trigger by MISTAKE, selecting all values from the target table. This statement was after the defined trigger, however when i tried to alter the trigger, by right clicking on trigger and selecting modify, it also showed me the select statement after the end block of trigger.
Does this mean, every time the trigger is fired this select statement executes ? this is my first question (Question A) - May be a silly one, but i am a little confused about this.
My second question is (Question B) I encounter locking issue on the CACHE_UPDATE_TABLE, could this be the reason for locking? Also there is a SQL job which runs every one minute to check the CACHE_UPDATE_TABLE table, and then i perform some operation(linked server related) and delete these records from CACHE_UPDATE_TABLE after i am done. Locking Issue could be because of this?? and if so, how do i counter it?
My third question is (Question C) Is this the best way to do this operation using triggers or can i do it some other way? Is the trigger defined proper?
-Any help will be appreciated... Thanks.
You've got a lot of different questions in there which is probably why you've not received any answers, but I'll cover what I can.
A) That's quite an interesting question actually. I would have assumed that it would do nothing - It'd be executed when you create the trigger but then wouldn't be part of the trigger - however I've noticed odd behaviour with this before so I tested with a simple stored procedure:
CREATE PROCEDURE dbo.test ( #i INT ) AS
BEGIN
SELECT #i
END;
SELECT 'hi'
GO
Executing the stored procedure causes the SELECT 'hi' to fire as well as the SELECT #i. I still don't have an answer for your question, but I would definitely make sure not to have any stray SQL outside the trigger when you create it for this reason alone.
I've just investigated this a little more and apparently the end of the stored procedure is wherever the first GO is after the procedure (which SQL Server automatically adds to the end if you don't use one). So you could define your whole procedure after the END - you can still use the parameters too.
This seems to be because the BEGIN and END aren't a required part of the stored procedure definition - they're not actually indicating the begin and end of the stored procedure, they're just an unrelated BEGIN...END block like you might put after and IF statement. You can have as many BEGIN...END blocks as you like in the procedure definition, or none at all.
C) I would definitely change your trigger. You've massively complicated it by combining the 3 triggers without reusing any code. The only reason to combine INSERT,UPDATE and DELETE triggers is so that you don't have to duplicate code. You should either:
Have 3 separate triggers, each containing only the relevant INSERT - that way you remove all of the conditional logic.
Keep them together but work out only the CODE using some conditional logic and have only 1 INSERT statement.
I'd be tempted to go with the 3 separate triggers, or at least an separate out the delete trigger, and then use CASE del.ID IS NULL THEN 1 ELSE 2 END for the CODE on the INSERT/UPDATE trigger. But you could combine them with (untested):
INSERT INTO CACHE_UPDATE_TABLE (CODE, ID, DATE, COUNT)
SELECT CASE WHEN del.ID IS NULL THEN 1
WHEN ins.ID IS NULL THEN 3
ELSE 2 END
,ISNULL(ins.ID, del.ID)
,ISNULL(ins.DATE, del.DATE)
,ISNULL(ins.COUNT, del.COUNT)
FROM deleted del
FULL OUTER JOIN inserted ins ON del.ID = ins.ID
Just remove that
SELECT * FROM CACHE_UPDATE_TABLE
Whats wrong with the following query? However it works fine if I remove first "Go".
Go
if exists(select 1 from sys.objects where name='SP_xyz' and type='P')
drop procedure SP_xyz
Go
There are no need to first GO,
because GO is a command which execute the batch process.
You have no any statement before first GO to execute.
Please refer below example with the comment.
The following example creates two batches. The first batch contains only a USEAdventureWorks2012 statement to set the database context. The remaining statements use a local variable. Therefore, all local variable declarations must be grouped in a single batch. This is done by not having a GO command until after the last statement that references the variable.
USE AdventureWorks2012;
GO
DECLARE #NmbrPeople int
SELECT #NmbrPeople = COUNT(*)
FROM Person.Person;
PRINT 'The number of people as of ' +
CAST(GETDATE() AS char(20)) + ' is ' +
CAST(#NmbrPeople AS char (10));
GO
I have searched and found nothing about it (I believe it is impossible to do it). My problem is that I have to check if a temporary table exists and also if there is some specific data on that temporary table.
Did anyone faced this before? How did you managed to solve it? I would like to avoid creating milions of IF..ELSE blocks.
EDIT:
IF (OBJECT_ID('tempdb..#tempTable') IS NOT NULL
AND EXISTS (SELECT * FROM #tempTable WHERE THIS_COLUMN = 'value'))
BEGIN
PRINT 'Temp table and data exists'
END
ELSE
BEGIN
PRINT 'Temp table or data does not exist'
END
This is what I want to do. The problem comes when the tempTable doesn't exist (that could happen). It throws an error because, although the first stamement returns false, it continues to execute the second statement. And the SELECT statement is not able to find the table and therefore throws the error. The solution I found was to do this:
IF OBJECT_ID('#tempTable') IS NOT NULL
BEGIN
IF EXISTS (SELECT * FROM #tempTable WHERE THIS_COLUMN = 'value'
BEGIN
PRINT 'Temp table and data exists'
END
ELSE
BEGIN
PRINT 'Temp table exists but data does not exist'
END
END
ELSE
BEGIN
PRINT 'Temp table does not exist'
END
My question would be, is there a way of having 2 conditions and if the first condition returns false not check the second one? Kind of using && in a programming language.
What you are trying to do is not possible as this is a compile time failure and the whole statement needs to be compiled together.
It won't evaluate the first part of the statement then compile the second part only if that is true. You need to split the test for existence and the query referencing the table into two separate statements so they are compiled separately.
See here
There is no such thing as XAND logical gate (exclusive AND). In theory XAND would mean, that both operands are true or both are false. So this means XAND is the same as Equals (=), at least for bitwise logical operations.
Please show some sample code to illustrade what you are trying to do.
Regards
I have searched this sometime ago and if I remember correctly Sql Server does indeed short circuit logical conditions but it is it who decides which one it will check first regardless of the order in which they appear in the if clause.
This is probably a sketchy solution, but I sometimes use COALESCE statements to control the sorts of if, else, then structure you are trying to get at. In this case, it is a little more dirty looking because we are looking for the inverse of a coalesce statement.
DECLARE #temp_message AS varchar(100)
SELECT #temp_message = COALESCE(CASE
WHEN OBJECT_ID('tempdb..#tempTable') IS NOT NULL THEN NULL
ELSE 'Temp table does not exist'
END,
CASE
WHEN EXISTS (SELECT * FROM #tempTable WHERE THIS_COLUMN = 'value') THEN NULL
ELSE 'Specified value does not exist in temp table'
END,
'Temp table and data exists')
PRINT #temp_message
The COALESCE runs one statement after another until one does not yield a NULL value. That means you can do cool things like run a series of small queries to check some values before running a large costly query. Let me know if that is really illegitimate! It worked on my machine :)
I see two ways to get close to this in MSSQL:
First. If you use sp_executesql (dynamic sql) your stored procedure will be compiled without errors. Also if #tempTable doesn't exists server will output error but continue batch execution:
exec sp_executesql N'SELECT count(*) FROM #tempTable WHERE THIS_COLUMN = ''value'''
if ##rowcount > 0
print 'ok'
else
print 'error'
end;
Second. Just create User defined function with nested IF and EXISTS (as you do it now) which output 0 and 1. And use dynamic sql to input table name and possible filter value(s) to this UDF. In this case you can use this UDF in IF.
lets say I have a script like that:
if(some condition)
begin
select somecolumn from sometable
end
Lets say, that "somecolumn" does not exist and the condition is not true, which means the select is NOT executed. Even though the select would not be executed, the script is not valid, Management Studio complains about the missing column "somecolumn".
Question: Can I somehow disable this sort of check so that the script is executed and since the if is not true, it will never notice that the column is missing?
Thanks :-)
Use dynamic SQL
if(some condition)
begin
exec ('select somecolumn from sometable') --or sp_executesql
end
It actually makes no sense to run this because of what SQL is. It's not executed line by line: the whole batch is parsed etc in one go and the error is generated here, before anything actually runs in the sense you mean. This is by design...
You can create a procedure that references a table that does not exist however that is the only exception to the rule. From the BOL:
Deferred name resolution can only be
used when you reference nonexistent
table objects. All other objects must
exist at the time the stored procedure
is created. For example, when you
reference an existing table in a
stored procedure you cannot list
nonexistent columns for that table.
Beyond using dynamic SQL, there is no means to reference non-existent columns in a stored procedure.