SQL Server 2005 error - sql-server

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.

Related

Incorrect syntax near the keyword “AS” when alter trigger

i created trigger called trgInsteadofdeleteEmp
and i just want to alter it, i wrote the following SQL code
alter trigger trgInsteadofdeleteEmp on Emp
instead of delete
as
begin
declare #id int , #name nvarchar(100)
select #id =id from deleted
select #name = name from deleted
insert into EmployeeAudit values (#id ,#name + 'tried to delete at' + GETDATE() as varchar(50))
end
and have the following output:
Msg 156, Level 15, State 1, Procedure trgInsteadofdeleteEmp, Line 8 Incorrect syntax near the keyword 'as'.
can someone point me in the direction of how to find the error
Thanks.
No, no, no, no, no.
Don't make the mistake of assuming that inserted and deleted have only one row. You are just putting errors in your code that are going to pop up at an unexpected time. I actually wish that SQL Server flagged this usage when creating the trigger.
Instead:
alter trigger trgInsteadofdeleteEmp on Emp
instead of delete
as
begin
insert into EmployeeAudit(id, name)
select id,
name + ' tried to delete at ' + convert(varchar(255), GETDATE(), 121) )
from deleted d;
end;
Your error is caused by the as. There seems to be a missing cast() function. But that is not the right fix. With date/times, use convert() or format() along with the desired format.
Other suggestions:
Always include the column names when doing an insert. In fact, an audit table really should have an identity id column, createdBy, and createdAt columns, all with default values.
Look at the strings that will be produced and be sure they are readable.
Use semicolons to end statements.
Don't rely on default formatting for date/time values.

temp tables, go commands,

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.

Stored Procedure Does Not Fire Last Command

On our SQL Server (Version 10.0.1600), I have a stored procedure that I wrote.
It is not throwing any errors, and it is returning the correct values after making the insert in the database.
However, the last command spSendEventNotificationEmail (which sends out email notifications) is not being run.
I can run the spSendEventNotificationEmail script manually using the same data, and the notifications show up, so I know it works.
Is there something wrong with how I call it in my stored procedure?
[dbo].[spUpdateRequest](#packetID int, #statusID int output, #empID int, #mtf nVarChar(50)) AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #id int
SET #id=-1
-- Insert statements for procedure here
SELECT A.ID, PacketID, StatusID
INTO #act FROM Action A JOIN Request R ON (R.ID=A.RequestID)
WHERE (PacketID=#packetID) AND (StatusID=#statusID)
IF ((SELECT COUNT(ID) FROM #act)=0) BEGIN -- this statusID has not been entered. Continue
SELECT ID, MTF
INTO #req FROM Request
WHERE PacketID=#packetID
WHILE (0 < (SELECT COUNT(ID) FROM #req)) BEGIN
SELECT TOP 1 #id=ID FROM #req
INSERT INTO Action (RequestID, StatusID, EmpID, DateStamp)
VALUES (#id, #statusID, #empID, GETDATE())
IF ((#mtf IS NOT NULL) AND (0 < LEN(RTRIM(#mtf)))) BEGIN
UPDATE Request SET MTF=#mtf WHERE ID=#id
END
DELETE #req WHERE ID=#id
END
DROP TABLE #req
SELECT #id=##IDENTITY, #statusID=StatusID FROM Action
SELECT TOP 1 #statusID=ID FROM Status
WHERE (#statusID<ID) AND (-1 < Sequence)
EXEC spSendEventNotificationEmail #packetID, #statusID, 'http:\\cpweb:8100\NextStep.aspx'
END ELSE BEGIN
SET #statusID = -1
END
DROP TABLE #act
END
Idea of how the data tables are connected:
From your comments I get you do mainly C# development. A basic test is to make sure the sproc is called with the exact same arguments you expect
PRINT '#packetID: ' + #packetID
PRINT '#statusID: ' + #statusID
EXEC spSendEventNotificationEmail #packetID, #statusID, 'http:\\cpweb:8100\NextStep.aspx'
This way you 1. know that the exec statement is reached 2. the exact values
If this all works than I very good candidate is that you have permission to run the sproc and your (C#?) code that calls it doesn't. I would expect that an error is thrown tough.
A quick test to see if the EXEC is executed fine is to do an insert in a dummy table after it.
Update 1
I suggested to add PRINT statements but indeed as you say you cannot (easily) catch them from C#. What you could do is insert the 2 variables in a log table that you newly create. This way you know the exact values that flow from the C# execution.
As to the why it now works if you add permissions I can't give you a ready answer. SQL security is not transparent to me either. But its good to research yourself a but further. Do you have to add both guest and public?
It would also help to see what's going inside spSendEventNotificationEmail. Chances are good that sproc is using a resource where it didn't have permission before. This could be an object like a table or maybe another sproc. Security is heavily dependent on context/settings and not an easy problem to tackle with a Q/A site like SO.

Change type of a column with numbers from varchar to int

We have two columns in a database which is currently of type varchar(16). Thing is, it contains numbers and always will contain numbers. We therefore want to change its type to integer. But the problem is that it of course already contains data.
Is there any way we can change the type of that column from varchar to int, and not lose all those numbers that are already in there? Hopefully some sort of sql we can just run, without having to create temporary columns and create a C# program or something to do the conversion and so forth... I imagine it could be pretty easy if SQL Server have some function for converting strings to numbers, but I am very unstable on SQL. Pretty much only work with C# and access the database through LINQ to SQL.
Note: Yes, making the column a varchar in the first place was not a very good idea, but that is unfortunately the way they did it.
The only reliable way to do this will be using a temporary table, but it will not be much SQL:
select * into #tmp from bad_table
truncate table bad_table
alter bad_table alter column silly_column int
insert bad_table
select cast(silly_column as int), other_columns
from #tmp
drop table #tmp
The easiest way to do this is:
alter table myTable alter column vColumn int;
This will work as long as
all of the data will fit inside an int
all of the data can be converted to int (i.e. a value of "car" will fail)
there are no indexes that include vColumn. If there are indexes, you will need to include a drop and create for them to get back to where you were.
Just change the datatype in SQL Server Management Studio.
(You may need to go to menu Tools → Options → Designers, and disable the option that prevents saving changes that re-create the table.)
I totally appreciate the previous answers, but also thought a more complete answer would be helpful to other searchers...
There are a couple caveats that would be helpful if you making the changes on a production type table.
If you have an identity column defined on the table you will have to set IDENTITY_INSERT on and off around the re-insert of data. You will also have to use an explicit column list.
If you want to be sure of not killing data in the database, use TRANSACTIONS around the truncate/alter/reinsert process
If you have a lot of data, then trying to just make the change in SQ Server Management Studio could fail with a timeout and you could lose data.
To expand the answer that #cjk gave, look at the following:
Note: 'tuc' is just a placeholder in this script for the real tablename
begin try
begin transaction
print 'Selecting Data...'
select * into #tmp_tuc from tuc
print 'Truncating Table...'
truncate table tuc
alter table tuc alter column {someColumnName} {someDataType} [not null]
... Repeat above until done
print 'Reinserting data...'
set identity_insert tuc on
insert tuc (
<Explicit column list (all columns in table)>
)
select
<Explicit column list (all columns in table - same order as above)>
from #tmp_tuc
set identity_insert tuc off
drop table #tmp_tuc
commit
print 'Successful!'
end try
begin catch
print 'Error - Rollback'
if ##trancount > 0
rollback
declare #ErrMsg nvarchar(4000), #ErrSeverity int
select #ErrMsg = ERROR_MESSAGE(), #ErrSeverity = ERROR_SEVERITY()
set identity_insert tuc off
RAISERROR(#ErrMsg, #ErrSeverity, 1)
end catch

How do I conditionally create a stored procedure in SQL Server?

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

Resources