How do I solve these two errors when creating a trigger? - sql

I'm creating a simple trigger that will email me when a table is updated or inserted into:
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'sportsNewsUpd' AND type = 'TR')
DROP TRIGGER sportsNewsUpd
GO
CREATE TRIGGER sportsNewsUpd
ON sportsNews
FOR Insert, UPDATE
AS
EXEC master.webdb.xp_sendmail
#recipients = 'name#email.com',
#subject = 'Sports News has been altered.'
GO
I am getting two errors:
Incorrect syntax near 'GO', 'CREATE TRIGGER' must be the first tatement in query batch., Incorrect syntax near 'GO'. (two 'GO', two errors).
My resources:
http://msdn.microsoft.com/en-us/library/aa258254(v=sql.80).aspx (B. under examples)
http://msdn.microsoft.com/en-us/library/ms189505.aspx

semicolon after DROP TRIGGER sportsNewsUpd?
so...
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'sportsNewsUpd' AND type = 'TR')
DROP TRIGGER sportsNewsUpd;
GO

try:
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'sportsNewsUpd' AND type = 'TR')
begin
DROP TRIGGER sportsNewsUpd
end
GO
CREATE TRIGGER sportsNewsUpd ON sportsNews FOR Insert, UPDATE AS
begin
EXEC master.webdb.xp_sendmail
#recipients = 'name#email.com',
#subject = 'Sports News has been altered.'
end
GO

How do you run the query(ies)?
I doubt very much that you would get these errors running the script from an SSMS query window. Also, you wouldn't get the errors if you were running it using sqlcmd or osql.
What I mean is I'm suspecting you are trying to run the script from the application and so you may have probably been unaware that GO is not a part of T-SQL.
Solution in this case would be to split the script in two and run each part in turn.

Related

How to execute commands/functions in order

I'm writing a long SQL query that I will be using to automate the process of ingesting large-ish flat files (using python to flatten heavily nested JSON files) and normalizing them for scalability and ease of use with PowerBI reports and dashboards.
Currently, I've got a long process that slices the table into multiple tables, generates mapping tables between them and the primary table, remaps a PK/FK link back to the primary table and drops the old unneeded columns from the primary table.
I'm still building and debugging the script, and I'm getting really frustrated with something that I think I'm doing wrong as I'm not very proficient in SQL.
Currently, if I try to run all of my code at once it will fail saying I'm using invalid column names. The column names are invalid with the tables in their current state, but if it would simply execute from top to bottom, they would be valid by the time it got to them. I've got to highlight and execute my drop tables statement by itself every time I want to rerun the entire script even though I've got the same drop tables statement at the top.
Any advice on how to make the script simply execute from top to bottom or how to make it step through and ignore the "current" state of the tables (prior to execution) would be greatly helpful.
Some example pseudo of what I've got:
CREATE OR ALTER PROCEDURE DropTables
AS
BEGIN
DROP TABLE IF EXISTS
t1,
t2,
t3
END
GO
CREATE OR ALTER PROCEDURE GenerateTable1
AS
BEGIN
~make table~
END
GO
CREATE OR ALTER PROCEDURE GenerateTable2
BEGIN
~make table~
END
GO
CREATE OR ALTER PROCEDURE GenerateTable3
BEGIN
~make table~
ALTER TABLE t1 ADD ~fk from t3~
UPDATE t1
SET ~keys to match~
FROM t3 WHERE t1.old_col = t3.new_col
ALTER TABLE t1
DROP COLUMN old_col
END
GO
EXEC DropTables
GO
EXEC GenerateTable1
GO
EXEC GenerateTable2
GO
EXEC GenerateTable3
Upon executing this I get "Invalid column name old_col" because old_col currently doesnt exist, however, if it would just execute from top to bottom, old_col would exist when it got to it.
Current workaround is highlighting droptables and executing it by itself first, then I can execute everything at once
GO breaks the script into batches. You just need to scope the batches so each one compiles. Or use dynamic SQL which is just a different way to issue separate batches.
It may not be the most elegant solution, but simply wrapping everything in an exec block with single quotes seems to work. "EXEC(' stuff ');" SQL doesnt try to get ahead of itself and is forced to execute in order from top to bottom, example:
CREATE OR ALTER PROCEDURE DropTables
AS
BEGIN
EXEC('
DROP TABLE IF EXISTS
t1,
t2,
t3
')
;
END
GO
CREATE OR ALTER PROCEDURE GenerateTable1
AS
BEGIN
EXEC('
~make table~
')
;
END
GO
CREATE OR ALTER PROCEDURE GenerateTable2
BEGIN
EXEC('
~make table~
')
;
END
GO
CREATE OR ALTER PROCEDURE GenerateTable3
BEGIN
EXEC('
~make table~
ALTER TABLE t1 ADD ~fk from t3~
UPDATE t1
SET ~keys to match~
FROM t3 WHERE t1.old_col = t3.new_col
ALTER TABLE t1
DROP COLUMN old_col
')
;
END
GO
EXEC DropTables
GO
EXEC GenerateTable1
GO
EXEC GenerateTable2
GO
EXEC GenerateTable3

SQL Server - Check column if exists >> rename and change type

SQL Server:
Check column if exists when
If True : (Change/Modify) column_name and dataType
If False : Create
Schema name : Setup
Code:
IF EXISTS (SELECT 1 FROM sys.columns
WHERE Name = N'bitIntialBalance'
AND Object_ID = Object_ID(N'Setup.LeaveVacationsSubType'))
BEGIN
ALTER TABLE [Setup].[LeaveVacationsSubType]
ALTER COLUMN intIntialBalance INT NULL;
EXEC sp_RENAME 'Setup.LeaveVacationsSubType.bitIntialBalance', 'intIntialBalance', 'COLUMN';
--ALTER TABLE [Setup].[LeaveVacationsSubType] MODIFY [intIntialBalance] INT; not working
END
GO
IF NOT EXISTS(SELECT 1 FROM sys.columns
WHERE Name = N'intIntialBalance'
AND Object_ID = Object_ID(N'Setup.LeaveVacationsSubType'))
BEGIN
ALTER TABLE [Setup].[LeaveVacationsSubType]
ADD intIntialBalance INT NULL;
END
GO
If I guess correctly, the problem is that query plan is made for the whole script, and SQL Server also checks that it can actually perform all the operations, even if it is inside an if statement. That's why you'll get an error, even if in the reality that statement would never be executed.
One way to get around this issue is to make all those statements dynamic, something like this:
execute ('ALTER TABLE [Setup].[LeaveVacationsSubType] MODIFY [intIntialBalance] INT')

SQL Updating column after adding it giving "Invalid column name" error

I have the following SQL in SQL Server 2005 but I get an error stating "Invalid column name 'ExpIsLocalTime' (ln 7) when I run it:
IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'ExpIsLocalTime' AND Object_ID = Object_ID(N'[dbo].[tbl_SessionsAvailable]'))
BEGIN
ALTER TABLE dbo.tbl_SessionsAvailable ADD
ExpIsLocalTime bit NOT NULL CONSTRAINT DF_tbl_SessionsAvailable_ExpIsLocalTime DEFAULT (0)
UPDATE dbo.tbl_SessionsAvailable
SET ExpIsLocalTime = 1
END
GO
This will be in a script file that may be run more than once so I'm trying to make sure the UPDATE only runs once. Is there something about BEGIN/END that delays the execution of the DDL statement?
Your SQL query to do the UPDATE refers to a column that has not yet been created. At compile time, SQL Server detects that the column does not exist, so it gives you the error "Invalid column name 'ExpIsLocalTime'".
In order to include the UPDATE in this query, you will need to encapsulate it in a dynamic SQL query. In other words, something like this:
IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'ExpIsLocalTime' AND Object_ID = Object_ID(N'[dbo].[tbl_SessionsAvailable]'))
BEGIN
ALTER TABLE dbo.tbl_SessionsAvailable ADD
ExpIsLocalTime bit NOT NULL CONSTRAINT DF_tbl_SessionsAvailable_ExpIsLocalTime DEFAULT (0)
DECLARE #SQL NVARCHAR(1000)
SELECT #SQL = N'UPDATE dbo.tbl_SessionsAvailable SET ExpIsLocalTime = 1'
EXEC sp_executesql #SQL
END
GO
We have the same issue in our SQL scripts that maintain tables. After a table is created, if we add a column to it later, we have to use dynamic SQL to avoid these compilation errors.
Another possibly simpler solution is using the GO statement after the Alter statement. This would send the DDL to the server. Then run the rest of your SQL. This should work if you are using sqlcmd osql or SSMS.

Only ALTER a TRIGGER if is exists

I am implementing an auditing system on my database. It uses triggers on each table to log changes.
I need to make modifications to these triggers and so am producing ALTER scripts for each one.
What I'd like to do is only have these triggers be altered if they exist, ideally like so:
IF EXISTS (SELECT * FROM sysobjects WHERE type = 'TR' AND name = 'MyTable_Audit_Update')
BEGIN
ALTER TRIGGER [dbo].[MyTable_Audit_Update] ON [dbo].[MyTable]
AFTER Update
...
END
However when I do this I get an error saying "Invalid syntax near keyword TRIGGER"
The reason that these triggers may not exist is that auditing can be enabled/disabled on tables which the end user can specify. This involves either creating or dropping the triggers. I am unable to make the changes to the triggers upon creation as they are dynamically created and so I must still provide a way altering the triggers should they exist.
The alter statement has to be the first in the batch. So for sql server it would be:
IF EXISTS (SELECT * FROM sysobjects WHERE type = 'TR' AND name = 'MyTable_Audit_Update')
BEGIN
EXEC('ALTER TRIGGER [dbo].[MyTable_Audit_Update] ON [dbo].[MyTable]
AFTER Update
...')
END
This might be a similar issue to one that I found with Sybase years ago, where I found that when trying to execute a create table statement conditionally, the DDL is executed prior to assessing the conditional statement. The only workaround was to use execute immediate.
Unlike CREATE TRIGGER, I failed to find a reference that explicitly states that
CREATE TRIGGER must be the first statement in the batch
but it seems that this restriction applies to ALTER TABLE too.
The simple way to do this would be to DROP the TRIGGER and re-create it:
IF EXISTS (SELECT * FROM sysobjects WHERE type = 'TR' AND name = 'MyTable_Audit_Update')
DROP TRIGGER MyTable_Audit_Update
GO
CREATE TRIGGER [dbo].[MyTable_Audit_Update] ON [dbo].[MyTable]
AFTER Update
...
END

checking for existence of a stored procedure

Does any body know why the following SQL returns a syntax error (Incorrect syntax near 'IF') in SQL Server 2012?
DROP PROCEDURE IF EXISTS MyStoredProcedure;
I know I can use the following instead:
IF EXISTS(SELECT 1 FROM sys.procedures
WHERE Name = 'MyStoredProcedure')
But I wonder why the first one produces the error.
Thanks!
This works:
IF EXISTS(SELECT 1 FROM sys.procedures
WHERE Name = 'MyStoredProcedure')
print 'yes'
-- drop procedure mystoredprocedure
You must have something else in your query window that's throwing the error, or you're running things in the wrong order.
For what it's worth, this is my "go to" for dropping/building procedures before I CREATE PROCEDURE... all my saved proc scripts have this.
IF OBJECT_ID('dbo.uspSomeProcName') IS NOT NULL
BEGIN
DROP PROCEDURE dbo.uspSomeProcName
IF OBJECT_ID('dbo.uspSomeProcName') IS NOT NULL
PRINT '<<< FAILED DROPPING PROCEDURE dbo.uspSomeProcName>>>'
ELSE
PRINT '<<< DROPPED PROCEDURE dbo.uspSomeProcName>>>'
END