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
Related
I'm writing the following code into SQL Server Management Studio
DECLARE #tab AS table(ProductID integer, StockCount integer)
INSERT INTO #tab
SELECT ProductID, InStock + OnOrder
FROM Inventory.Product;
GO
SELECT * FROM #tab;
When I execute the code, an error occurs. Which of the two following actions could I take to prevent the error from happening?
1
Modify the INSERT statement to:
INSERT INTO #tab
SELECT ProductID, InStock
FROM Inventory.Product;
2
--Remove the GO command
3
--Use a temporary table named #tab instead of the #tab variable
4
--Add a second GO command after the final SELECT statement
Personally, I think 1 and 2 are correct, but with slight disability issues I'm not confident enough in my answer being 100% correct, if anyone could give any pointers that would certainly help or explain why I may be wrong.
EDIT: THE ERROR I'M GIVEN WHEN I RUN A QUERY IS:
Msg 208, Level 16, State 1, Line 2
Invalid object name 'Inventory.Product'.
Msg 1087, Level 15, State 2, Line 6
Must declare the table variable "#tab".
The reason you are getting the error is because a Variable's scope is the transaction in which it is declared.
When you declare a variable as soon as you put the GO key word which is a batch separator the variable is not visible after that GO keyword.
Anyway these are not the factors which should decide whether to use a Table Variable or a Temp table.
Have a look at this question What's the difference between a temp table and table variable in SQL Server to learn about the differences between the Temp tables and table variables and then decide what is best for you.
I would say 2 & 3 are correct. The Go will not allow the variable to carry over to the second query, but you could get around this by using a temporary table instead of a variable.
2 & 3 are correct indeed.
Remove the go makes it as a single batch, so #tab var would be
accessible for second SELECT stmnt
If you need though to split it into two batches, then as advised before - make it a table.
While working on a database documentation topic, I encountered a situation and got stuck. Thanks in advance for potential help. Here are the facts:
I am trying to obtain only the body of certain stored procedures in my database.
Anything else, such as SP parameters or options - I don't need.
Googled around and all I've found is ways to obtain the entire SP text - most of them already known.
I've put together a solution as you can see below but it's not covering all the cases and it's not pretty.
Having defined this test SP:
CREATE PROCEDURE dbo.returnDay
#addTheseDays SMALLINT = 0
AS
-- This is just a test SP that retrieves
-- the current date if #addTheseDays isn't defined,
-- otherwise the current day + #addTheseDays
SELECT GETDATE() + #addTheseDays;
GO
What didn't help:
-- This doesn't help since it retrieves all SP text (including parameters and options part)
EXEC sp_helptext 'dbo.returnDay';
-- The ROUTINE_DEFINITION column also holds the entire SP text.
SELECT *
FROM INFORMATION_SCHEMA.ROUTINES;
Workaround I've done and works, with exceptions:
DECLARE #spText VARCHAR(MAX)
SELECT #spText = object_definition(object_id('dbo.returnDay'))
SELECT SUBSTRING(#spText, CHARINDEX('AS', #spText, 0) + 2 , LEN(#spText)) AS spBody
This "ugly" string manipulation workaround works but only when the SP does not have "WITH EXECUTE AS CALLER" option or the parameters don't have "AS" as part of their name. In these cases then I get extra, unneeded info regarding the SP (again, only need SP body - only what is between the AS and batch terminator).
Also tried to use the first BEGIN and last END in the SP body (and get what's between) but since these are not mandatory in SQL Server and some SPs don't have them then I can't rely on them.
Any ideas and/or suggestions on how can I get only the SP body (code and comments) in a better way?
As a quick and dirty one-off you can look for AS as a single word, taking into account the single EXECUTE AS exception.
This will fail for any comments containing -AS- of course.
SELECT SUBSTRING(
#spText,
PATINDEX('%[ ' + CHAR(13) + CHAR(10) + CHAR(9) + ']AS[ ' + CHAR(13) + CHAR(10) + CHAR(9) + ']%', REPLACE(#spText, 'WITH EXECUTE AS CALLER', 'WITH EXECUTE ?? CALLER')) + 3,
LEN(#spText))
You will need parse the T-SQL sproc to obtain just the body. Have a look at RegEx to parse stored procedure and object names from DDL in a .sql file C#. There are several CLR assemblies that give you regex access within SQL, or perform in your application language if not purely SQL for your application.
I am writing a stored procedure for SQL Server 2008 in which I need to extract information from a set of tables. I do not know ahead of time the structure of those tables. There is another table in the same database that tells me the names and types of the fields in this table.
I am doing this:
declare #sql nvarchar(max)
set #sql = 'select ... into #new_temporary_table ...'
exec sp_executesql #sql
Then I iterate doing:
set #sql = 'insert into #another_temporary_table ... select ... from #new_temporary_table'
exec sp_executesql #sql
After that I drop the temporary table. This happens in a loop, so the table with be created, populated and dropped many times, each time with different columns.
This fails with the error:
Invalid object name: #new_temporary_table.
After some googling I have found that:
The table #new_temporary_table is being created in the scope of the call to exec sp_executesql which is different from the one of my stored proc. This is the reason the next exec sp_executesql cannot find the table. This post explains it:
http://social.msdn.microsoft.com/forums/en-US/transactsql/thread/1dd6a408-4ac5-4193-9284-4fee8880d18a
I could use global temporary tables, which are prepended with ##. I can't do this because multiple stored procs could run at the same time and they would be affecting each other's state
In this article it says that if I find myself in this situation I should change the structure of the database. This is not an option for me:
http://www.sommarskog.se/dynamic_sql.html
One workaround I have found was combining all the select into #new_temporary_table.. and all the insert into ... scripts into one gigantic statement. This works fine but it has some downsides.
If I do print #sql to troubleshoot, the text gets truncated, for example.
Do I have any other option? All ideas are welcome.
You could use global temp tables, but use a context id (such as newid()) as part of the global temp table name.
declare #sql varchar(2000)
declare #contextid varchar(50) = convert(varchar(20), convert(bigint, substring(convert(binary(16), newid()), 1, 4)))
set #sql = 'select getdate() as stuff into ##new_temporary_table_' + #contextid
exec (#sql)
I think it's best to use one single script.
You can change how many characters will print in Tools > Options > Query Results > SQL Server > Results to Text - change "Maximum number of characters..." from 256 to the max (8192).
If it's bigger than 8192, then yes, printing is difficult. But you could try a different option in this case. Instead of PRINT #sql; instead use the following (with Results to Grid):
SELECT sql FROM (SELECT #sql) AS x(sql) FOR XML PATH;
Now you can click on the result, and it opens in a new query window. Well, it's an XML file window, and you can't execute it or see color-coding, and you have to ignore that it changes e.g. > to > to make it valid as XML data, but from here it's easy to eyeball if you're just trying to eyeball it. You can copy and paste it to a real query editor window and do a search and replace for the entitized characters if you like. FWIW I asked for them to make such XML windows real query windows, but this was denied:
http://connect.microsoft.com/SQLServer/feedback/details/425990/ssms-allow-same-semantics-for-xml-docs-as-query-windows
#temp tables (not global) are available in the scope they were created and below.
So you could do something like...
while (your_condition = 1) begin
set #sql = 'select ... into #temp1 ...from blah
exec sp_do_the_inserts'
exec(#sql)
end
The sp_do_the_inserts might look like...
select * into #temp2 from #temp1
....your special logic here....
This assumes you create sp_do_the_inserts beforehand, of course.
Don't know if that serves your need.
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