Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2005 Forums
 Transact-SQL (2005)
 Using USE (what am I doing wrong here)

Author  Topic 

PiecesOfEight
Posting Yak Master

200 Posts

Posted - 2008-02-08 : 23:48:47
I need to loop through some database names and run tsql to create objects in each db (basically the same objects in each). In the test script below, the script always tries to create the table in Test3, so the second time through the loop it fails with an object already exists error.

---------------------

DECLARE @DbNames nvarchar(60)
SET @DbNames = 'Test3_Test2_Test1_'

DECLARE @CurrentDbName nvarchar(20)

WHILE LEN(@DbNames) > 0
BEGIN

PRINT '>' + @DbNames + '<'

SET @CurrentDbName = SUBSTRING(@DbNames, LEN(@DbNames) - 5 , 5)

IF @CurrentDbName = 'Test1'
BEGIN
USE Test1
PRINT 'Creating objects in database ' + @CurrentDbName
END
ELSE IF @CurrentDbName = 'Test2'
BEGIN
USE Test2
PRINT 'Creating objects in database ' + @CurrentDbName
END
ELSE IF @CurrentDbName = 'Test3'
BEGIN
USE Test3
PRINT 'Creating objects in database ' + @CurrentDbName
END

CREATE TABLE TestTab (TestCol int)

SET @DbNames = SUBSTRING(@DbNames, 0, LEN(@DbNames) - 5)

END


When I look at Management Studio, it shows Test2 as being the current database. I'm about 99% sure there's some rookie mistake in here that I'm missing, but I just don't see it. BTW -- I am using the IFs because if I use USE inside an EXEC('USE ' + @CurrentDbName), the database context doesn't persist outside the EXEC. Any help is appreciated.

jackv
Master Smack Fu Yak Hacker

2179 Posts

Posted - 2008-02-09 : 06:57:43
You might have to look at using dynamic sql

Jack Vamvas
--------------------
Search IT jobs from multiple sources- http://www.ITjobfeed.com
Go to Top of Page

jezemine
Master Smack Fu Yak Hacker

2886 Posts

Posted - 2008-02-09 : 09:49:04
see my post of 12/12/2007 11:42:51 in this thread:

http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=94068

(note to regulars: how do you link to a particular post, and not the topic as a whole?)


elsasoft.org
Go to Top of Page

PiecesOfEight
Posting Yak Master

200 Posts

Posted - 2008-02-09 : 11:41:21
Thanks for the reply Jezemine. I read through that thread. For this project, I can't call a separate file from sqlcmd; this has to be all one script. I also can't wrap the object creation in a single EXEC b/c I am creating objects like procs and triggers that each have to be in their own batches.

Is their a fundamental flaw in the script I posted? As I said, in Management Studio, it appears that the database context is correct, yet it creates the table in Test3 during the first pass through the WHILE, rather than Test1.
Go to Top of Page

TG
Master Smack Fu Yak Hacker

6065 Posts

Posted - 2008-02-09 : 14:02:32
>>Is their a fundamental flaw in the script I posted?

No. No, because logically it should work. Sql server messes this up for the same reason that when you, within a batch, create a table and then SELECT from it, sql will return the error that the table does not exist. The internal process that checks the batch for sytax errors as well as prepares execution plans does not take the <conditional> IF blocks into account.

I set up your little script here on my 2000 box after creating the three Testn DBs. I put a BREAK statement after the first iteration. Sql created the table in Test3 even though the logic flows to USE Test1. When the code is allowed to complete it goes through Test2 and blows up when it tries to create the table (again) in Test3. I am assuming it is because the original plan read the USE Test3 statement directly infront of the CREATE Table even though it the <condition> does not allow that code to execute in the first iteration. The prepared plan executed "Use Test3...CREATE TABLE..." on the first iteration!!

I think you'll have to go with jackv's suggestion of dynamic sql. This seems to work:

DECLARE @DbNames nvarchar(60)
SET @DbNames = 'Test3_Test2_Test1_'

DECLARE @CurrentDbName nvarchar(20)

WHILE LEN(@DbNames) > 0
BEGIN

PRINT '>' + @DbNames + '<'

SET @CurrentDbName = SUBSTRING(@DbNames, LEN(@DbNames) - 5 , 5)

if db_id(@CurrentDbName) > 0
begin
exec ('
use ' + @CurrentDbName + '

CREATE TABLE TestTab (TestCol int)
')
end

SET @DbNames = SUBSTRING(@DbNames, 0, LEN(@DbNames) - 5)

END


Be One with the Optimizer
TG
Go to Top of Page

PiecesOfEight
Posting Yak Master

200 Posts

Posted - 2008-02-09 : 14:57:31
Thanks. At least now I know I'm not crazy . The problem I have with the EXEC approach is that I have objects I need to create that have to be in their own batch. So the following works:

		exec ('
use ' + @CurrentDbName + '

CREATE TABLE TestTab (TestCol int)
')


But the following does not. Any ideas about working around that issue?


		exec ('
use ' + @CurrentDbName + '

CREATE TABLE TestTab (TestCol int)
CREATE TRIGGER ...

')


Even just a USE and the trigger creation in the same exec fails b/c the trigger creation has to be in its own batch.
Go to Top of Page

tkizer
Almighty SQL Goddess

38200 Posts

Posted - 2008-02-09 : 15:36:13
Use the three part naming convention for the table that you want to create instead of using USE:
EXEC ('CREATE TABLE ' + @CurrentDbName + '.dbo.TestTab (TestCol int)')

Then put the trigger in another batch by doing another EXEC, making sure to use the three part naming convention again.


Tara Kizer
Microsoft MVP for Windows Server System - SQL Server
http://weblogs.sqlteam.com/tarad/
Go to Top of Page

PiecesOfEight
Posting Yak Master

200 Posts

Posted - 2008-02-09 : 16:17:48
Thanks. I was hoping to avoid cluttering the script, b/c there is a lot of code, including DML, and it is being used in a set of documentation. However, if there is no way around SQL Server on this one, then that is probably the best solution.
Go to Top of Page

jezemine
Master Smack Fu Yak Hacker

2886 Posts

Posted - 2008-02-09 : 17:13:48
quote:
Originally posted by PiecesOfEight

Thanks. At least now I know I'm not crazy . The problem I have with the EXEC approach is that I have objects I need to create that have to be in their own batch. So the following works:

		exec ('
use ' + @CurrentDbName + '

CREATE TABLE TestTab (TestCol int)
')


But the following does not. Any ideas about working around that issue?


		exec ('
use ' + @CurrentDbName + '

CREATE TABLE TestTab (TestCol int)
CREATE TRIGGER ...

')


Even just a USE and the trigger creation in the same exec fails b/c the trigger creation has to be in its own batch.




I guess this will work...

		exec ('
use ' + @CurrentDbName + '
CREATE TABLE TestTab (TestCol int)')

exec ('
use ' + @CurrentDbName + '
CREATE TRIGGER ...')

[/code]


elsasoft.org
Go to Top of Page

PiecesOfEight
Posting Yak Master

200 Posts

Posted - 2008-02-09 : 23:55:41
Actually that doesn't work in the case of create trigger, create proc, and some other commands. You will get an error: "<Command> must be the first statement in a query batch."
Go to Top of Page

jezemine
Master Smack Fu Yak Hacker

2886 Posts

Posted - 2008-02-10 : 03:11:45
then do what Tara suggested.


elsasoft.org
Go to Top of Page

madhivanan
Premature Yak Congratulator

22864 Posts

Posted - 2008-02-11 : 04:50:58
quote:
Originally posted by jezemine

see my post of 12/12/2007 11:42:51 in this thread:

http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=94068

(note to regulars: how do you link to a particular post, and not the topic as a whole?)


elsasoft.org


Append reply id
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=94068#356331

Madhivanan

Failing to plan is Planning to fail
Go to Top of Page

jezemine
Master Smack Fu Yak Hacker

2886 Posts

Posted - 2008-02-11 : 08:51:56
and how do you discover the replyid? do you have to view source on the page?


elsasoft.org
Go to Top of Page

madhivanan
Premature Yak Congratulator

22864 Posts

Posted - 2008-02-11 : 09:21:50
quote:
Originally posted by jezemine

and how do you discover the replyid? do you have to view source on the page?


elsasoft.org


Move the mouse pointer to "Reply with Quote"
In the status bar you can find that
But there is no way to extract and you need to type manually

Madhivanan

Failing to plan is Planning to fail
Go to Top of Page

PiecesOfEight
Posting Yak Master

200 Posts

Posted - 2008-02-11 : 16:45:06
quote:
then do what Tara suggested.




You can't use 3 part naming for triggers. This little problem has been like a tour of tsql idiosyncracies.
Go to Top of Page

TG
Master Smack Fu Yak Hacker

6065 Posts

Posted - 2008-02-11 : 17:13:47
There may be better ways to accomplish this but the simplest thing is:

I suggest you try inverting the order of your IF blocks so that the last "USE" statement is the first database in the iteration sequence.

Be One with the Optimizer
TG
Go to Top of Page

PiecesOfEight
Posting Yak Master

200 Posts

Posted - 2008-02-11 : 18:23:20
That makes sense, but the last USE is always the one that is used. So if I set it up so that Test1 is last, it works for the first iteration, but then it tries to create the table in Test1 again on the next iteration instead of Test2. I've tried to confuse it with gotos and other methods of indirection, but it appears to be on to me.
Go to Top of Page

jezemine
Master Smack Fu Yak Hacker

2886 Posts

Posted - 2008-02-11 : 19:10:03
sqlcmd is the way to go. stop resisting it.


elsasoft.org
Go to Top of Page

TG
Master Smack Fu Yak Hacker

6065 Posts

Posted - 2008-02-11 : 19:19:49
yeah yeah ok ok...I'm sure that is the right way to go but...

Keep the USE statements in there. But where you are: CREATE TABLE, EXEC that call and then EXEC the CREATE TRIGGER call after that. This worked in that little test environment I set up:

DECLARE @DbNames nvarchar(60)
SET @DbNames = 'Test1_Test2_Test3_'

DECLARE @CurrentDbName nvarchar(20)

WHILE LEN(@DbNames) > 0
BEGIN

PRINT '>' + @DbNames + '<'

SET @CurrentDbName = SUBSTRING(@DbNames, LEN(@DbNames) - 5 , 5)

--select @CurrentDbName [@CurrentDbName]

IF @CurrentDbName = 'Test1'
BEGIN
USE Test1
PRINT 'Creating objects in database ' + @CurrentDbName
END
ELSE IF @CurrentDbName = 'Test2'
BEGIN
USE Test2
PRINT 'Creating objects in database ' + @CurrentDbName
END
ELSE IF @CurrentDbName = 'Test3'
BEGIN
USE Test3
PRINT 'Creating objects in database ' + @CurrentDbName
END


-- select db_name() [db_name()]

exec('CREATE TABLE TestTab (TestCol int)')
exec ('
create trigger trTestTab on TestTab after insert
as
begin
select * from inserted
end
')

SET @DbNames = SUBSTRING(@DbNames, 0, LEN(@DbNames) - 5)

END


Be One with the Optimizer
TG
Go to Top of Page

PiecesOfEight
Posting Yak Master

200 Posts

Posted - 2008-02-11 : 22:14:11
Thanks for sticking with me on this one. That EXEC for the create table did the trick. I didn't put that in originally, b/c the table creates didn't require their own batch. Hallelujah!
Go to Top of Page
   

- Advertisement -