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.
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 created the stored procedure for inserting records in 'order' table. I am trying to fetching the max of id of Order table from another database in variable #Id.
The schema is as follows:
CREATE PROCEDURE order_add
#orderstatus nvarchar(1000),
#paymentstatus nvarchar(1000),
#Customer nvarchar(1000),
#createdon nvarchar(1000),
#ordertotal nvarchar(1000)
AS
DECLARE #Id int;
BEGIN
SELECT MAX(Id)
INTO #Id
FROM [Database2].dbo."Order";
INSERT INTO Customer
VALUES(#Id, #orderstatus, #paymentstatus, #Customer, #createdon, #ordertotal);
END;
When I am executing this stored procedure I get this error:
Msg 102, Level 15, State 1, Procedure order_add, Line 13 [Batch Start Line 0]
Incorrect syntax near '#Id'.
What should I do to solve this?
Your procedure has vogue syntax of SELECT statement :
select #Id = max(Id)
from [Database2].dbo.Order
insert into Customer (id, orderstatus, paymentstatus, Customer, createdon, ordertotal)
values(#Id, #orderstatus, #paymentstatus, #Customer, #createdon, #ordertotal
);
Note :
Always qualify all column name explicitly while using insert into statement.
You have an answer that will "solve" this problem. But stop and think about your code. Think about the hierarchy of objects within a database server. You create a procedure within a particular database. It is dangerous to qualify table names with a database name - even if that is the same name in which your procedure is created. Why? Because someone will eventually want to create a copy of this database. Perhaps for testing, for disaster recovery, etc. Now your procedure will either fail or will not produce the correct results.
In addition, you have a need for guidance in your code. Presumably you work within a group that has multiple members and at least one of those members has significant experience. Your group should be doing code reviews to improve the quality of code writing generally, to raise the level of business knowledge within the group, and to identify coding errors. This process should catch many issues, some of which I'll mention.
You should assign scalar variables using SET, not SELECT. Why? Because there is a subtle and significant difference when the query finds no rows. In such a case, SET will assign a value of NULL to the variable while SELECT will leave it unchanged. More here.
You also need to think about datatypes. Use the correct ones, don't just define everything as a string - especially very large strings. I will guess that most of your parameters should not be nvarchar - and certainly not containing 1000 characters.
Why do you use double quotes in your first select statement? If you follow the rules for object names (and you should), you don't need to delimit them (which is what the double quotes do). Be consistent and follow the rules. And again - don't qualify table names with the database name unless you absolutely intend to do this for good reasons. And if you do intend this, then use a synonym. That will make someone's life much easier and give you good karma.
Lastly, always specify the list of columns you populate in an insert statement. Do not - repeat NOT - develop the lazy habit of omitting that. Why? The order of columns might change - as unlikely as that might seem. An additional column with a default might be added. Assuming the default value should be used in this situation, you now must change your statement. By specifying the column list, your code will continue to work correctly after the column is added.
I have a proc designed to move data from a linked sql2008r2 server with several identical dbs to my new sql2014 server. It creates all new databases and objects and transforms/treats the data on import. You run it on a source database name and it does the rest. I've used it for a couple of months without problems, including as recently as last night.
Part of the code selects before and after record counts for each table for a report at the end.
Today, the before/after counts are preventing me from running the stored procedure or altering it. (multiple errors, since it does this after each table.)
Here are the lines it freaks out about. Maybe there's a better way to do this than using a temp table?
exec('select count(*) as beforerecs into ##t from [sourceserver\localities].' + #dbname + '.dbo.' + #Tabname + ';')
select #sCount=beforerecs from ##t --line 96
drop table ##t
exec('select count(*) as afterrecs into ##t from ' + #destdbname + '.dbo.' + #Tabname + ';')
select #dcount=afterrecs from ##t --line 99
drop table ##t
The errors I'm getting are complaining because the table and column don't technically exist until the previous line of code is executed, but it didn't care about that until today:
Msg 207, Level 16, State 1, Procedure MigrateLocalityDB, Line 96
Invalid column name 'beforerecs'.
Msg 207, Level 16, State 1, Procedure MigrateLocalityDB, Line 99
Invalid column name 'afterrecs'.
This was the only way I could think of at the time to get the record counts using my database and table name variables. Suggestions for a better way are most welcome! Thanks in advance.
Balde actually answered this, but I'm not sure how to close the question without an answer post.
The problem turned out to be related to using global temp tables with a name that is sometimes used in other stored procedures (##t) --something that hadn't been a problem before, but was lazy on my part. Changing them to local temporary tables (#t) allowed me to compile (alter) the proc, but the proc failed to run as the results weren't available to the subsequent query. The solution was to continue using global temp tables but simply make the name more unique to prevent conflicts (##tlocmig). Live and learn.
Thanks to Balde for getting me on the right track!
The following tSQL query is puzzling me:
select 1 as FIELD into #TEMP
drop table #TEMP
select 1 as FIELD into #TEMP
When I run it from SQL Server Management Studio session window (pressing F5 to the whole query, as a group), I get the following error:
Msg 2714, Level 16, State 1, Line 3
There is already an object named '#TEMP' in the database.
Note that table #TEMP doesn't exist before the query is executed.
I thought that the code shouldn't produce any errors as line 2 is dropping the temporary table. But it is as if the drop isn't taking effect when line 3 is executed.
My questions:
Why does the error happen?
How do I fix the query so it executes as intended?
PS. The query above is a simplification of a real world query of mine that is showing the same symptoms.
PS2. Regardless of whether this is a sound programming practice or not (as Sean hinted in his comments), this unexpected behavior prompted me to look for information on how these queries are parsed in the hopes that the knowledge will be helpful to me in the future.
As I found the seek of existing tables are different:
select 1 as FIELD into #TEMP
drop table #TEMP
When you use into statement after those commands:
select 1 as FIELD into #TEMP
Error is:
There is already an object named '#TEMP' in the database.
And When you use a select on #TEMP after those commands:
select * from #TEMP
Error is:
Invalid object name '#TEMP'.
So, In first case THERE IS an object with #TEMP name and in the other case THERE IS NOT an object with #TEMP name !.
An important note from technet.microsoft is:
DROP TABLE and CREATE TABLE should not be executed on the same table in the same batch. Otherwise an unexpected error may occur.
In notes of dropping tables by SQL Server Database Engine:
The SQL Server Database Engine defers the actual page deallocations, and their associated locks, until after a transaction commits.
So the second error on using select statement may related to the actual page deallocations and the first error on using into statement may related to duration between lock associated until the transaction commits.
Here try this:
select 1 as FIELD into #TEMP
drop table #TEMP
GO
select 1 as FIELD into #TEMP
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.