| 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) > 0BEGIN 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 sqlJack Vamvas--------------------Search IT jobs from multiple sources- http://www.ITjobfeed.com |
 |
|
|
jezemine
Master Smack Fu Yak Hacker
2886 Posts |
|
|
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. |
 |
|
|
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) > 0BEGIN 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)ENDBe One with the OptimizerTG |
 |
|
|
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. |
 |
|
|
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 KizerMicrosoft MVP for Windows Server System - SQL Serverhttp://weblogs.sqlteam.com/tarad/ |
 |
|
|
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. |
 |
|
|
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 |
 |
|
|
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." |
 |
|
|
jezemine
Master Smack Fu Yak Hacker
2886 Posts |
Posted - 2008-02-10 : 03:11:45
|
then do what Tara suggested. elsasoft.org |
 |
|
|
madhivanan
Premature Yak Congratulator
22864 Posts |
|
|
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 |
 |
|
|
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 thatBut there is no way to extract and you need to type manuallyMadhivananFailing to plan is Planning to fail |
 |
|
|
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. |
 |
|
|
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 OptimizerTG |
 |
|
|
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. |
 |
|
|
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 |
 |
|
|
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) > 0BEGIN 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 insertedend') SET @DbNames = SUBSTRING(@DbNames, 0, LEN(@DbNames) - 5)ENDBe One with the OptimizerTG |
 |
|
|
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! |
 |
|
|
|