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.
Related
I am trying to write to a temp table , the source data can come from two different sources but they do have the same schema. the source of data is selected by a conditional statement IF ELSE only one can execute, but sql doesn't seem to like that . it complains the table already exists. here is the piece of code. "Msg 2714, Level 16, State 1, Procedure sp_xxx, Line 37 [Batch Start Line 0]
There is already an object named '#my_temp_table' in the database. "
IF #flag = 0
SELECT * INTO #my_temp_table
FROM source_A
ELSE
BEGIN
--even tho the temp table will not exist i am trying to by pass the error
IF OBJECT_ID('tempdb..#my_temp_table ') IS NOT NULL
DROP TABLE #my_temp_table
SELECT * INTO #my_temp_table
FROM source_B
END
There are a couple of things going on here that are or will be issues for you.
SQL Server's SELECT...INTO logic is how Microsoft chose to implement the more standard CREATE TABLE AS... syntax (which is then followed by a SELECT statement in most dialects). You can only create that table once, and then it's there.
Local temp tables (with a single #) continue to exist until your session disconnects from the instance. So if you try to run the above code twice without disconnecting, it'll fail.
You're using SELECT *, which is going to bite you when somebody changes the schema of one source table.
All that said, you don't need all that logic. You can do what you want with one query.
SELECT
<Column List>
INTO #my_temp_table
FROM
source_A
WHERE #flag = 0
UNION
SELECT
<Column List>
FROM
source_B
WHERE #flag <> 0;
I am trying to execute a procedure with a parameter, and depending on the value of the parameter, three different IF conditions will be evaluated to verify which query it will execute from a linked server.
But when I execute the query, it seems to be checking if the tables inside all the IF exists before starting the query. And I know that only one of the table exists, that is why I am using the parameter, so it shouldn't fail. but I anyhow get the following error:
Msg 7314, Level 16, State 1, Line 25
The OLE DB provider "Microsoft.ACE.OLEDB.16.0" for linked server "LinkedServer" does not contain the table "D100". The table either does not exist or the current user does not have permissions on that table.
So in this code, assume that the parameter is 300. then I get the message above.
Do you know, if there is a way, to limit the query to do not check all the tables, but only the one where the IF condition will be met?
ALTER PROCEDURE[dbo].[Import_data]
#p1 int = 0
AS
BEGIN
SET NOCOUNT ON;
IF(#p1 = 100)
BEGIN
DROP TABLE IF EXISTS Table1
SELECT [Field1], [Field2], [Field3], [Field4], [Field5], [Field6]
INTO Table1
FROM[LinkedServer]...[D100]
END
IF(#p1 = 200)
BEGIN
DROP TABLE IF EXISTS Table2
SELECT[Field1], [Field2], [Field3], [Field4], [Field5], [Field6]
INTO Table2
FROM[LinkedServer]...[D200]
END
IF(#p1 = 300)
BEGIN
DROP TABLE IF EXISTS Table3
SELECT[Field1], [Field2], [Field3], [Field4], [Field5], [Field6]
INTO Table3
FROM[LinkedServer]...[D300]
END
END
I have tried googling it, but I found mostly workarounds as running a sub procedure, but it is not really a clean solution, I think.
Okay, it seems I that I found the answer. Even with an IF statement, the SQL Server validates the entire query before executing it, so the way to overcome it, is to use a Dynamic SQL Query.
"SQL Server Dynamic SQL is a programming technique that allows you to construct SQL statements dynamically at runtime. It allows you to create more general purpose and flexible SQL statement because the full text of the SQL statements may be unknown at compilation."
This is how the query looks now. so instead of multiple IF statements, the query changes dynamically depending on the parameter.
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = N'DROP TABLE IF EXISTS Table1;
SELECT [Field1]
,[Field2]
,[Field3]
,[Field4]
,[Field5]
,[Field6]
INTO Table1
FROM [LinkedServer]...[D' + CONVERT(nvarchar(3),#p1) + N']'
EXEC sp_executesql #SQL
I'm trying to write a stored procedure that truncates the first table on our data warehouse, then copies data from our local database to the DWH server.
Here's the code:
USE [ARGTPAWN-DB-DWH].[DWH].[dbo].[PML];
GO
TRUNCATE TABLE [ARGTPAWN-DB-DWH].[DWH].[dbo].[PML];
GO
SELECT *
INTO [ARGTPAWN-DB-DWH].[DWH].[dbo].[PML]
FROM [14TPAWNDB001].[FLMedicaid].[dbo].[PML]
GO
And the response I am getting is:
Msg 911, Level 16, State 1, Line 1
Database 'ARGTPAWN-DB-DWH' does not exist. Make sure that the name is entered correctly.
Msg 4701, Level 16, State 1, Line 3
Cannot find the object "PML" because it does not exist or you do not have permissions.
Msg 117, Level 15, State 1, Line 7
The object name 'ARGTPAWN-DB-DWH.DWH.dbo.PML' contains more than the maximum number of prefixes. The maximum is 2.
The servers are already linked, so that is not an issue, but I'm very curious as to why this is not working.
Linked Server and distributed query can be tricky in term of performance...
You should consider to write the Stored Procedure on the database that hosts the target tables even if you call it from the database that hosts the source tables.
On target database :
CREATE PROCEDURE [DBO].[TARGET_SIDE_PS]
AS
-- For error handling.
DECLARE #aERROR int
DECLARE #aCOUNT int
-- Start transaction.
BEGIN TRANSACTION
-- Drop target table.
IF OBJECT_ID('dbo.PML', 'U') IS NOT NULL
DROP TABLE dbo.PML;
-- Error catching
SELECT #aERROR = ##ERROR, #aCOUNT = ##ROWCOUNT
IF #aERROR<>0
BEGIN
-- Error : do what is needed.
--
ROLLBACK TRANSACTION
RETURN 1
END
SELECT *
INTO dbo.PML
FROM [SOURCELINKEDSERVER].[SOURCEDATABASE].[dbo].[PML]
-- Error catching
SELECT #aERROR = ##ERROR, #aCOUNT = ##ROWCOUNT
IF #aERROR<>0
BEGIN
-- Error : do what is needed.
--
ROLLBACK TRANSACTION
RETURN 2
END
IF #aCOUNT <= 0
BEGIN
-- No data: do what is needed.
--
PRINT 'NO DATA !!'
END
COMMIT TRANSACTION
RETURN 0
Call the target side PS from the source side (or from the target side) and it's done but source side database have to be linked to target side database.
Transaction can be remove due to DROP/CREATE/INSERT sequence.
You can do the oposite : PS on the source side, with drop and insert on the linked target side server database, but you must know :
- transaction will take a while.
- all the source data will be locked during the whole process.
- INSERT will take a while.
You don't need to execute:
USE [ARGTPAWN-DB-DWH].[DWH].[dbo].[PML];
Simply execute the lines below.
What is ARGTPAWN-DB-DWH? If this is the server name it is not needed.
Your USE statement should refer to the database only.
Your SELECT * INTO... will attempt to create the table PML, if this already exists (which it will if you are performing a truncate) it will fail - Use INSERT INTO... or DROP TABLE... instead of truncate.
Don't use SELECT *.
Why can't you do this and is there are work around?
You get this error.
Msg 2714, Level 16, State 1, Line 13
There is already an object named '#temptable' in the database.
declare #x int
set #x = 1
if (#x = 0)
begin
select 1 as Value into #temptable
end
else
begin
select 2 as Value into #temptable
end
select * from #temptable
drop table #temptable
This is a two-part question and while Kev Fairchild provides a good answer to the second question he totally ignores the first - why is the error produced?
The answer lies in the way the preprocessor works. This
SELECT field-list INTO #symbol ...
is resolved into a parse-tree that is directly equivalent to
DECLARE #symbol_sessionid TABLE(field-list)
INSERT INTO #symbol_sessionid SELECT field-list ...
and this puts #symbol into the local scope's name table. The business with _sessionid is to provide each user session with a private namespace; if you specify two hashes (##symbol) this behaviour is suppressed. Munging and unmunging of the sessionid extension is (ovbiously) transparent.
The upshot of all this is that multiple INTO #symbol clauses produce multiple declarations in the same scope, leading to Msg 2714.
You can't do that because of deferred name resolution, you can do it with a real table, just take out the pound signs
You could also create the temp table first on top and then do a regular insert into table
First step... check if the table already exists... if it does, delete it. Next, explicitly create the table rather than using SELECT INTO...
You'll find it much more reliable that way.
IF OBJECT_ID('tempdb..#temptable', 'U') IS NOT NULL
BEGIN
DROP TABLE #temptable
END
CREATE TABLE #temptable (Value INT)
declare #x int
set #x = 1
if (#x = 0)
begin
INSERT INTO #temptable (Value) select 1
end
else
begin
INSERT INTO #temptable (Value) select 2
end
select * from #temptable
drop table #temptable
Also, hopefully the table and field names are simplified for your example and aren't what you really call them ;)
-- Kevin Fairchild
Deferred name resolution is also the reason you cannot be sure that sp_depends gives back correct results, check out this post I wrote a while back Do you depend on sp_depends (no pun intended)
I am going to guess that the issue is that you haven't created the #temptable.
Sorry I can't be more detailed but since you haven't even tried to explain what you are seeing you get a less than stellar answer.
From the look of the code is seems like you might have been prototyping this in SQL Studio or similiar, right? Can I guess that you've run this a few times and had it get to the point where it's created #temptable but then failed before it got to the end and dropped the table again? Restart the SQL editing tool you're using and try again.
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