From: jonpb on
Hi,

Taken from the example here:
http://msdn.microsoft.com/en-us/library/ks9f57t0(VS.71).aspx

I have this procedure defined:
----------------------------------
create procedure sp_insert_get_id
@sql varchar(3000),
@identity int output
as
execute (@sql);
set @identity = scope_identity();
go
----------------------------------

then, if I run this script:
----------------------------------
declare @sql varchar(3000);
declare @identity int;

set @sql = N'insert into [dwOptions2D] ([DoBevels]) values (1)';
set @identity = -1;

exec dbo.sp_insert_get_id @sql, @identity;

print @identity;
go
----------------------------------

-1 is printed to the screen, even though the insert was successful

I can't see what is wrong with the code, thanks.
From: Jeroen Mostert on
On 2010-06-02 19:09, jonpb wrote:
> Hi,
>
> Taken from the example here:
> http://msdn.microsoft.com/en-us/library/ks9f57t0(VS.71).aspx
>
> I have this procedure defined:
> ----------------------------------
> create procedure sp_insert_get_id
> @sql varchar(3000),
> @identity int output
> as
> execute (@sql);
> set @identity = scope_identity();
> go
> ----------------------------------
>
> then, if I run this script:
> ----------------------------------
> declare @sql varchar(3000);
> declare @identity int;
>
> set @sql = N'insert into [dwOptions2D] ([DoBevels]) values (1)';
> set @identity = -1;
>
> exec dbo.sp_insert_get_id @sql, @identity;
>
First of all, you need to specify OUTPUT in the call to the stored procedure
as well, otherwise it's only passed as input. So

exec dbo.sp_insert_get_id @sql = @sql, @identity = @identity OUTPUT;

This may be slightly confusing initially; just remember that the left-hand
side is the stored procedure formal argument and the right-hand side is your
actual value (which may be a variable that may have the same name as the
argument).

If you now execute your script, you will find that @identity is NULL on
returning from the stored procedure. This is because the EXECUTE statement
begins a new scope, so there is no SCOPE_IDENTITY() value in the stored
procedure. If you need to get an identity from a dynamically executed
statement, the SCOPE_IDENTITY() select has to be part of that statement:

create procedure #sp_insert_get_id
@sql nvarchar(3000),
@identity int output
as
set @sql = @sql + N';select @identity = scope_identity()';
exec sp_executesql @sql, @parameters = N'@identity int output',
@identity = @identity output;
go

Of course, this use of dynamic SQL is horrible and convoluted and not what
you should be doing in production code, it's just to illustrate the point.

--
J.
From: jonpb on
On 02/06/2010 12:40 PM, Jeroen Mostert wrote:
> If you now execute your script, you will find that @identity is NULL on
> returning from the stored procedure. This is because the EXECUTE
> statement begins a new scope, so there is no SCOPE_IDENTITY() value in
> the stored procedure.

Thanks, that makes sense, so I changed the procedure definition to:

create procedure sp_insert_get_id
@table varchar(50),
@fields varchar(1000),
@values varchar(5000),
@identity int output
as
insert into @table (@fields) values (@values);
set @identity = scope_identity();
go

Now I get the error:
Incorrect syntax near '@fields'

And again, I can't tell from looking at the code why is doesn't work.

From: Jeroen Mostert on
On 2010-06-03 0:49, jonpb wrote:
> On 02/06/2010 12:40 PM, Jeroen Mostert wrote:
>> If you now execute your script, you will find that @identity is NULL on
>> returning from the stored procedure. This is because the EXECUTE
>> statement begins a new scope, so there is no SCOPE_IDENTITY() value in
>> the stored procedure.
>
> Thanks, that makes sense, so I changed the procedure definition to:
>
> create procedure sp_insert_get_id
> @table varchar(50),
> @fields varchar(1000),
> @values varchar(5000),
> @identity int output
> as
> insert into @table (@fields) values (@values);
> set @identity = scope_identity();
> go
>
> Now I get the error:
> Incorrect syntax near '@fields'
>
> And again, I can't tell from looking at the code why is doesn't work.
>
Because @table, @fields and @values all cannot be variables. T-SQL can't
construct dynamic statements that way.

If I understand you correctly, you want to be able to execute any statement
and get the identity value it produced. Why? Why can't you simply write the
SCOPE_IDENTITY() assignment after the statement itself? There seems to be
little value in a stored procedure for this.

You *can* do this, but not without dynamic SQL of the form I demonstrated
earlier. If you are going to dynamic SQL route, see
http://www.sommarskog.se/dynamic_sql.html for a thorough treatise.

In this particular case, if replication and triggers are not an issue, you
may want to use @@IDENTITY instead. Although it's recommended to use
SCOPE_IDENTITY(), @@IDENTITY is the last identity value generated by any
statement, regardless of scope, so it can be used if you can't or don't want
to modify the original statement executed in its original scope.

--
J.
From: jonpb on
On 02/06/2010 9:42 PM, Jeroen Mostert wrote:
> If I understand you correctly, you want to be able to execute any
> statement and get the identity value it produced. Why? Why can't you
> simply write the SCOPE_IDENTITY() assignment after the statement itself?
> There seems to be little value in a stored procedure for this.

Because I want a generic way of doing this and it was my understanding
that it needed to be in a stored procedure to make the "transaction" atomic.

> You *can* do this, but not without dynamic SQL of the form I
> demonstrated earlier. If you are going to dynamic SQL route, see
> http://www.sommarskog.se/dynamic_sql.html for a thorough treatise.

Thanks very much for the link, I obviously have fair bit to learn about
how dynamic sql works, why it works they it does.

I may actually use what you call "horrible and convoluted and not suited
for production code" because my concern is not so much an ugly stored
procedure but a simple client interface.

Thanks again Jeroen