Transaction within IF THEN ELSE doesn't commit - sql

In my TSQL script I have an IF THEN ELSE structure that checks if a column already exists.
If not it creates the column and updates it.
IF NOT EXISTS(
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'tableName' AND COLUMN_NAME = 'columnName'))
BEGIN
BEGIN TRANSACTION
ALTER TABLE tableName
ADD columnName int NULL
COMMIT
BEGIN TRANSACTION
update tableName
set columnName = [something]
from
[subquery]
COMMIT
END
This doesn't work because the column doesn't exist after the commit.
Why doesn't the COMMIT commit?

I'm guessing you are getting an error at parse stage, rather than at execute stage. The COMMIT will indeed commit, but the query parser isn't as clever as the query execution engine, and all the parser knows is that it can see you referring to tableName.columnName, which at parse time doesn't exist.
Wrap the whole update statement in an EXEC:
EXEC ('
update tableName
set columnName = [something]
from
[subquery]
')
and you should be OK. Bear in mind that you will need to double up 's within the 's of the EXEC.

Related

Alter trigger if exists with use of fluent migrator

I have one Trigger called dbo.SendMail and multiple database,
not all database have the trigger dbo.SendMail.
I am using FluentMigrator to manage database versions and i want to do something like below
IF EXISTS (SELECT * FROM sys.triggers WHERE object_id = OBJECT_ID(N'[dbo].[SendMail]'))
BEGIN
ALTER TRIGGER [dbo].[SendMail]
ON [dbo].[Notification]
FOR INSERT
AS
BEGIN
some sql code
END
END
it is giving me error Incorrect syntax near begin, Expecting EXTERNAL.
is there any way to achieve this ?
Thanks in advance.
Try this:
IF OBJECT_ID(N'[dbo].[SendMail]', N'TR') IS NOT NULL
-- Do whatever
Else
-- Do something else
Here is your trigger code with some dynamic sql. You have to roll with dynamic sql here because creating or altering objects must be the only statement in a batch. You can't wrap the create/alter logic inside an IF statement.
IF OBJECT_ID('SendMail') is not null
begin
declare #SQL nvarchar(max)
set #SQL = 'ALTER TRIGGER [dbo].[SendMail]
ON [dbo].[Notification]
FOR INSERT
AS
BEGIN
some sql code
END'
exec sp_executesql #SQL
end

invalid object name

I have a table that gets deleted and re created in milliseconds(cant just insert and delete). Of course this occurs sometimes when another stored procedure is running and trying to call that table. How would I avoid this? I have tried 'waitfor' xx seconds and different types of loops to wait until the table is back but I still get the error saying the table does not exist or (invalid object name 'xxxx') Thanks for any help.
Delete and recreate the table within a transaction.
When ever you read / write from / to it make sure you transaction isolation level is READ COMMITTED.
That way, the table should always be there as your read / writes won't happen until the transaction for deleting and creating the table is commited.
I think that's right, so I hope that helps.
enter link description here
You need to check if the table exists before you try to access it.
You can do something like:
IF (EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'Schema'
AND TABLE_NAME = 'Table'))
BEGIN
-- Do stuff with the table
END
Another option is to handle the schema errors by using TRY/CATCH together with dynamic SQL like this:
BEGIN TRY
DECLARE #sql nvarchar(100)
SET #sql = 'SELECT * FROM NonExistentTable'
EXEC sp_executesql #sql
END TRY
BEGIN CATCH
SELECT'Do stuff here'
END CATCH

Alter statement in a Transaction

I execute the code below:
use AdventureWorks2008R2
begin transaction
BEGIN
alter table HumanResources.Department add newcolumn int
update HumanResources.Department set newcolumn=1 where departmentid=1
END
commit
The error I get is:
Invalid column name 'newcolumn'.
Can ALTER statements be included in Transactions like this? If so, how can I prevent this error?
I have researched this online e.g. here. I have not found an answer to my specific question.
Yes, you can include an ALTER in a transaction. The problem is that the parser validates the syntax for your UPDATE statement, and can't "see" that you are also performing an ALTER. One workaround is to use dynamic SQL, so that the parser doesn't inspect your syntax (and validate column names) until runtime, where the ALTER will have already happened:
BEGIN TRANSACTION;
ALTER TABLE HumanResources.Department ADD newcolumn INT;
EXEC sp_executesql N'UPDATE HumanResources.Department
SET newcolumn = 1 WHERE DepartmentID = 1;';
COMMIT TRANSACTION;
Note that indentation makes code blocks much more easily identifiable (and your BEGIN/END was superfluous).
If you check the existence of column, then it should work.
BEGIN TRANSACTION;
IF COL_LENGTH('table_name', 'newcolumn') IS NULL
BEGIN
ALTER TABLE table_name ADD newcolumn INT;
END
EXEC sp_executesql N'UPDATE table_name
SET newcolumn = 1 WHERE DepartmentID = 1;';
COMMIT TRANSACTION;
Aaron has explained everything already. Another alternative that works for ad-hoc scripts in SSMS is to insert the batch separator GO so that the script is sent as two parts to the server. This only works if it is valid to split the script in the first place (you can't split an IF body for example).

SET statement always runs regardless of IF ELSE condition

I'm losing my sanity over this SQL code. The below IF ELSE statement functions correctly when testing with simple PRINT statement, i.e. the table does not exist so it prints 'FALSE'. But when I uncomment the SET statement and execute, it tries to run the SET statement, and naturally gives and error since the table does not exist.
DECLARE #zeus_calls310_counter int;
IF EXISTS (SELECT * FROM [zeus].tpza.INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'calls_310')
BEGIN
--SET #zeus_calls310_counter = (SELECT COUNT(*) FROM [zeus].[tpza].[dbo].[calls_310]);
PRINT 'TRUE'
END
ELSE
BEGIN
PRINT 'FALSE';
END
Your title indicates where your understanding goes wrong. In the case where the table does not exist, it is not that the SET statement runs and gives an error; it is that when the statement is parsed there is an error.
Whenever you run some SQL, the server first parses the statement, and then, if parsing succeeds, runs it. So you cannot have bare SQL that depends on the existence of tables! Your SET statement will be parsed whether or not the table exists - so when it doesn't, parsing fails.
One solution to this is to wrap those statements that depend on objects that may or may not exist within EXEC. However, in this case you want to populate a variable with the result of a query on that table, and inside an EXEC that variable will not be in scope. So we'd need more detail on what you're doing to do with #zeus_calls310_counter - if it's going to be used soon after, perhaps you could wrap the whole thing, including its declaration, in an EXEC.
#AAkashM is on the right track.
If you use EXEC sp_executesql you can also specify OUTPUT parameters, which in your case would contain the result of the SELECT COUNT statement.
Try this :
DECLARE #zeus_calls310_counter int;
IF EXISTS (SELECT * FROM [zeus].tpza.INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'calls_310')
BEGIN
SET #zeus_calls310_counter = (SELECT COUNT(*) FROM [zeus].[tpza].[calls_310]);
PRINT 'TRUE'
END
ELSE
BEGIN
PRINT 'FALSE';
END
Use this for checking if table exists
IF OBJECT_ID(N'[zeus].[table_name]', N'U') IS NULL
BEGIN
--
END
ELSE
BEGIN
--
END

stored procedure running continuosly in background

I am using one class file for updating my tables. In that I am either inserting or updating tables and after each update or insert, I am calling one stored procedure to save the last updated ID of the table. But once this stored procedure runs it never releases the resource. It is executing always in background. Why is this happening and how can I stop it?
Here is the stored procedure:-
Create procedure [dbo].[Updlastusedkey]
(
#tablename varchar(50)
)
as
Begin
DECLARE #sql varchar(300)
SET #SQL='UPDATE primarykeyTab SET lastKeyUsed = ISNULL(( SELECT Max(ID) from '+#tablename +'),1) WHERE Tablename='''+#tablename +''''
print #SQL
EXEC(#SQL)
END
Do you have Auto-Commit turned on? I think implicit_transactions = OFF means Auto Commit = ON in SQL Server. If not your Update operation may not be executing a COMMIT for the transaction it opened so leaving a write lock on the table. Alternatively just explicitly COMMIT your update perhaps.
Why don't you just create a view?
CREATE VIEW dbo.vPrimaryKeyTab
AS
SELECT tablename = 'table1', MAX(id_column) FROM table1
UNION
SELECT tablename = 'table2', MAX(id_column) FROM table2
/* ... */
;
Now you don't need to update anything or run anything in the background, and the view is always going to be up to date (it won't be the fastest query in the world, but at least you only pay that cost when you need that information, rather than constantly keeping it up to date).
Try this -
UPDATE primarykeyTab SET lastKeyUsed = ISNULL(( SELECT Max(ID) from '+#tablename
+' WITH (NOLOCK)),1) WHERE Tablename='''+#tablename +'''' WITH (NOLOCK)