I have the following script in SQL Server 2014 for creating a scalar function named GetFiscalPeriod. The script must check for the existence of the function by its name before creating it.
USE [DatabaseName]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF EXISTS (select * from dbo.sysobjects where
id = object_id(N'[dbo].[GetFiscalPeriod]')
and OBJECTPROPERTY(id, N'IsScalarFunction') = 1)
--want to terminate the whole batch
RETURN --doesn't work the way I want
--seems to terminate IF batch only
GO
CREATE FUNCTION [dbo].[GetFiscalPeriod] ()
Returns INT
AS
BEGIN
RETURN (select MAX(Id) from dbo.__FiscalPeriod__);
END
I want it to terminate the whole thing as it reaches inside IF body. (RETURN)
The problem is no matter how I change the code, either it jumps to CREATE FUNCTION giving this error:
There is already an object named 'GetFiscalPeriod' in the database.
Or giving this syntax error (when I try to put CREATE FUNCTION in the IF clause):
'CREATE FUNCTION' must be the first statement in a query batch.
Question is:
Is there anyway to tell SQL to ignore the rest of the script when the object name exists?
Note:
I used to drop the function beforehand, and it works. But I don't want to drop and recreate everything every time.
...
IF EXISTS (select * from dbo.sysobjects where
id = object_id(N'[dbo].[GetFiscalPeriod]')
and OBJECTPROPERTY(id, N'IsScalarFunction') = 1)
DROP FUNCTION [dbo].[GetFiscalPeriod] --works
GO
CREATE FUNCTION [dbo].[GetFiscalPeriod] ()
...
You can check for existence, if existed ALTER else CREATE:
you must exec query as string.
IF not EXISTS (select * from dbo.sysobjects where id = object_id(N'[dbo].[GetFiscalPeriod]') and OBJECTPROPERTY(id, N'IsScalarFunction') = 1)
execute sp_executesql N'
CREATE FUNCTION [dbo].[GetFiscalPeriod] ()
Returns INT
AS
BEGIN
RETURN (select MAX(Id) from dbo.__FiscalPeriod__);
END'
else
N'
ALTER FUNCTION [dbo].[GetFiscalPeriod] ()
Returns INT
AS
BEGIN
RETURN (select MAX(Id) from dbo.__FiscalPeriod__);
END'
Without dynamic SQL, you'll need to raise an error to stop the calling application from running the remainder of the script. This can be done with the -b command-line argument in the case of SQLCMD:
SQLCMD -S YourServer -d YourDatabase -E -iYourScript.sql -I -b
The T-SQL within the executed script to raise the error and terminate execution:
IF OBJECT_ID(N'dbo.GetFiscalPeriod', 'FN') IS NOT NULL
RAISERROR('Function GetFiscalPeriod already exists', 16, 1);
If you are using SQL Server 2016 SP1 or later, you can use CREATE OR ALTER to avoid dropping the object beforehand. It seems to me you would want to recreate the function even if it exists since it may be different.
You can do that check in the following way using dynamic sql.
declare #var nvarchar(max)
='CREATE FUNCTION [dbo].[GetFiscalPeriod] ()
Returns INT
AS
BEGIN
RETURN (select MAX(Id) from dbo.__FiscalPeriod__);
END'
IF Not EXISTS (funname) --function name for which the existence should be
begin --checked
exec sp_executesql #var
end
GO
CREATE FUNCTION IF NOT EXISTS
Create the function first with this key phrase
Related
I have a procedure which takes as argument the function name and then finds out the full body of that function and stores it in a local variable V_FullString.
I need this output not to show in screen rather to put it in text file.
Is it possible ?
ALTER PROCEDURE [dbo].[SP_DictionaryFunction]
#P_FunctionName VARCHAR(100)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #V_ObjectName varchar(100) = #P_FunctionName;
DECLARE #V_FULLSTRING VARCHAR(5000) = ''
IF EXISTS (SELECT object_id FROM SYS.objects
WHERE object_id = OBJECT_ID(#V_ObjectName))
BEGIN
DECLARE #V_TABLE TABLE (Line VARCHAR(500))
INSERT INTO #V_TABLE
EXECUTE SP_HELPTEXT #V_ObjectName
SELECT #V_FULLSTRING = #V_FULLSTRING + Line
FROM #V_TABLE
PRINT #V_FULLSTRING
END
END
Writing a file from the server would be a very bad idea. Ideally you'd be writing it at the client. So... how are you executing the SQL? If this is SSMS, just press ctrl+shift+f. If you are using sqlcmd, then the -o {filename} switch should work. If you're executing it via ADO.NET, then File.WriteAllText. Otherwise, you'll have to tell us what client tool you are using.
Note: I expect that the main problem you're seeing is that print is often ignored. You might prefer select, which all tools will respect.
I am trying to firstly check if the function exists then create it, if it doesn't exist.
I'm getting this error from the function:
IF NOT EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[GetRelativeExpiry]') AND type in (N'U'))
BEGIN
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
CREATE FUNCTION [dbo].[GetRelativeExpiry]
(
#Date DATE,
#N INT
)
RETURNS DATE
AS
BEGIN
-- Declare the return variable here
DECLARE #Expiry as DATE;
IF #N > 0
BEGIN
SELECT #Expiry = MAX(E2.Expiry)
FROM (SELECT TOP(#N) Expiry
FROM ExpiryDates E1
WHERE E1.Expiry >= #date
ORDER BY E1.Expiry) AS E2
END
ELSE
BEGIN
SELECT #Expiry = MIN(E2.Expiry)
FROM (SELECT TOP(-#N) Expiry
FROM ExpiryDates E1
WHERE E1.Expiry <= #date
ORDER BY E1.Expiry DESC) AS E2
END
RETURN #Expiry
END
END
I am not sure why I am getting this error, could someone please help?
I am using Microsoft SQL Server Management Studio 2014
CREATE statements, whether they are for TYPE, PROCEDURE, FUNCTION, ... should always be the first statement in a batch.
To work around this in batches like yours execute the CREATE statement using sp_executesql like this:
EXEC sp_executesql N'
-- your CREATE statement here
';
If you are trying to do this in regular window within MS SQL Server Management Studio 2014 (or any prior version), where you would normally write all other queries, then your definition of function must be very first statement that is not commented.
See image below. If statement USE PTCRMstaging_test; was not commented, SQL Server Management Studio would give that same error.
I am generating dynamic script to move data into another database if table is not present then i want to create. This script runs perfectly if it execute directly. But gives error if that script string execute by execute statement. i have tried exec also.
declare #temp as varchar(max)
set #temp='select * into Allocation_Archive.dbo.Users from Users'
execute #temp
Error
Database 'select * into Allocation_Archive' does not exist. Make sure that the name is entered correctly.
Option 1
Add a USE database statement to your variable or specify the full object name including database and schema.
Wrap the #temp variable in parentheses
For example:
declare #temp as varchar(max)
set #temp='select * into Allocation_Archive.dbo.Users from ThisDatabase.dbo.Users'
execute #temp
Option 2
Don't use:
EXECUTE #temp
Instead use
EXEC sp_executesql #temp
You will also need to change your #temp variable to be nvarchar(max)
I am performing the below operation. I am getting the error and unable to find what the error is.Could any one help me finding it.
a) Check for the availability of DESTINATION data base. If it is not exist, create the data base and move the tables to the data base.
b) If the table exists in the DESTINATION data base then no process required for the table.
if db_id('Destination')is null
begin
Create database Destination
select * into TabDestination from [Source].[dbo].[TabSource]
end
else
begin
use Destination
go
if('TabDestination' in (select name from sys.objects where type = 'u'))
insert into TabDestination select * from [Source].[dbo].[TabSource]
end
I am getting fallowing error
Msg 911, Level 16, State 1, Line 8
Database 'Destination' does not exist. Make sure that the name is entered correctly.
Msg 102, Level 15, State 1, Line 3
Incorrect syntax near 'end'.
Your problem is with the USE, from the documentation:
USE is executed at both compile and execution time...
If the database specified doesn't exist at compile time then the query will fail. You can see this by trying to run the following query:
IF 1 = 2
BEGIN
USE NonexistantDatabase
END
This query fails despite the fact that the USE statement is never executed.
You should instead change your query to use database qualified names, for example:
INSERT INTO Destination.dbo.Table SELECT * FROM Source.dbo.Table
Few problems here:
After Create database Destination you need to use that database before you do the select * into TabDestination... as you will create TabDestination in some other DB.
The Go in the middle of the begin...end block won't work.
To specify your database for the inserts to TabDesitination you'd be better to use the fully qualified name of the table than calling Use, eg Insert Destiation.dbo.TabDestination...
You need to use If Exists (select... for the second if statement.
Because your Database may not exists when the script compiles, a lot of the sql needs to be exec'd dynamically.
So your script could be re-written as:
if db_id('Destination')is null
begin
Create database Destination
exec ('select * into Destination.dbo.TabDestination from [Source].[dbo].[TabSource]')
end
else
begin
if exists (select name from Destination.sys.objects where name = 'TabDestination' and type = 'u')
insert into Destination.dbo.TabDestination select * from [Source].[dbo].[TabSource]
end
A variation on #Jon Egerton's answer, however there is one case you've neglected to cover: the database exists but the table does not.
DECLARE #sql NVARCHAR(MAX) = N'SELECT *
INTO Destination.dbo.TabDestination
FROM Source.dbo.TabSource;';
IF DB_ID('Destination') IS NULL
BEGIN
PRINT 'Creating database...';
CREATE DATABASE Destination;
PRINT 'Selecting into new table...';
EXEC sp_executeSQL #sql;
END
ELSE
BEGIN
IF EXISTS (SELECT 1 FROM Destination.sys.tables WHERE schema_id = 1
AND name = N'TabDestination')
BEGIN
PRINT 'Inserting into existing table...';
INSERT Destination.dbo.TabDestination SELECT * FROM Source.dbo.TabSource;
END
ELSE
BEGIN
PRINT 'Selecting into new table...';
EXEC sp_executeSQL #sql;
END
END
EDIT
Added PRINT statements for debugging purposes, as I suggested in the follow-up to #Jon's answer.
You just need to get rid of the GO command, its a batch separator so it breaks your begin/end. Oh and you can't use USE like that either.
As part of my integration strategy, I have a few SQL scripts that run in order to update the database. The first thing all of these scripts do is check to see if they need to run, e.g.:
if #version <> #expects
begin
declare #error varchar(100);
set #error = 'Invalid version. Your version is ' + convert(varchar, #version) + '. This script expects version ' + convert(varchar, #expects) + '.';
raiserror(#error, 10, 1);
end
else
begin
...sql statements here...
end
Works great! Except if I need to add a stored procedure. The "create proc" command must be the only command in a batch of sql commands. Putting a "create proc" in my IF statement causes this error:
'CREATE/ALTER PROCEDURE' must be the first statement in a query batch.
Ouch! How do I put the CREATE PROC command in my script, and have it only execute if it needs to?
Here's what I came up with:
Wrap it in an EXEC(), like so:
if #version <> #expects
begin
...snip...
end
else
begin
exec('CREATE PROC MyProc AS SELECT ''Victory!''');
end
Works like a charm!
SET NOEXEC ON is good way to switch off some part of code
IF NOT EXISTS (SELECT * FROM sys.assemblies WHERE name = 'SQL_CLR_Functions')
SET NOEXEC ON
GO
CREATE FUNCTION dbo.CLR_CharList_Split(#list nvarchar(MAX), #delim nchar(1) = N',')
RETURNS TABLE (str nvarchar(4000)) AS EXTERNAL NAME SQL_CLR_Functions.[Granite.SQL.CLR.Functions].CLR_CharList_Split
GO
SET NOEXEC OFF
Found here:
https://codereview.stackexchange.com/questions/10490/conditional-create-must-be-the-only-statement-in-the-batch
P.S. Another way is SET PARSEONLY { ON | OFF }.
But watch out for single quotes within your Stored Procedure - they need to be "escaped" by adding a second one. The first answer has done this, but just in case you missed it. A trap for young players.
Versioning your database is the way to go, but... Why conditionally create stored procedures. For Views, stored procedures, functions, just conditionally drop them and re-create them every time. If you conditionally create, then you will not clean-up databases that have a problem or a hack that got put in 2 years ago by another developer (you or I would never do this) who was sure he would remember to remove the one time emergency update.
Problem with dropping and creating is you lose any security grants that had previously been applied to the object being dropped.
This is an old thread, but Jobo is incorrect: Create Procedure must be the first statement in a batch. Therefore, you can't use Exists to test for existence and then use either Create or Alter. Pity.
It is much better to alter an existing stored proc because of the potential for properties and permissions that have been added AND which will be lost if the stored proc is dropped.
So, test to see if it NOT EXISTS, if it does not then create a dummy proc. Then after that use an alter statement.
IF NOT EXISTS(SELECT * FROM sysobjects WHERE Name = 'YOUR_STORED_PROC_NAME' AND xtype='P')
EXECUTE('CREATE PROC [dbo].[YOUR_STORED_PROC_NAME] as BEGIN select 0 END')
GO
ALTER PROC [dbo].[YOUR_STORED_PROC_NAME]
....
I must admit, I would normally agree with #Peter - I conditionally drop and then unconditionally recreate every time. I've been caught out too many times in the past when trying to second-guess the schema differences between databases, with or without any form of version control.
Having said that, your own suggestion #Josh is pretty cool. Certainly interesting. :-)
My solution is to check if the proc exists, if so then drop it, and then create the proc (same answer as #robsoft but with an example...)
IF EXISTS(SELECT * FROM sysobjects WHERE Name = 'PROC_NAME' AND xtype='P')
BEGIN
DROP PROCEDURE PROC_NAME
END
GO
CREATE PROCEDURE PROC_NAME
#value int
AS
BEGIN
UPDATE SomeTable
SET SomeColumn = 1
WHERE Value = #value
END
GO
use the 'Exists' command in T-SQL to see if the stored proc exists. If it does, use 'Alter', else use 'Create'
IF NOT EXISTS(SELECT * FROM sys.procedures WHERE name = 'pr_MyStoredProc')
BEGIN
CREATE PROCEDURE pr_MyStoredProc AS .....
SET NOCOUNT ON
END
ALTER PROC pr_MyStoredProc
AS
SELECT * FROM tb_MyTable