Insert vs update

W

wjasonstrutz

how does Infopath determine whether to use Update or Insert when submitting
to a database?

I have a form that's created from an SQL Server database connection. The
main connection consists of a purchase_request which is joined (using Shape)
to a purchase_request_item and also a purchase_request_discussion. I can
update existing purchase requests just fine, but when I try to enter a new
purchase request, I get this error:

[0x80040E38][Microsoft Cursor Engine] Row cannot be located for updating.
Some values may have been changed since it was last read.

It's as if Infopath is trying to run an Update when it should be running an
Insert.

Thanks,
jason.
 
S

Steve van Dongen [MSFT]

how does Infopath determine whether to use Update or Insert when submitting
to a database?

I have a form that's created from an SQL Server database connection. The
main connection consists of a purchase_request which is joined (using Shape)
to a purchase_request_item and also a purchase_request_discussion. I can
update existing purchase requests just fine, but when I try to enter a new
purchase request, I get this error:

[0x80040E38][Microsoft Cursor Engine] Row cannot be located for updating.
Some values may have been changed since it was last read.

It's as if Infopath is trying to run an Update when it should be running an
Insert.

Does either table have a trigger?

Regards,
Steve
 
W

wjasonstrutz

Steve,

Yes, there is a trigger. When I remove that trigger, the problem goes away.
Thought I had the logic sorted out so the trigger wouldn't have anything to
insert / update on new inserts, but it appears to not be working. So, back
to trigger debugging.

Thanks for the help!
jason.

Steve van Dongen said:
how does Infopath determine whether to use Update or Insert when submitting
to a database?

I have a form that's created from an SQL Server database connection. The
main connection consists of a purchase_request which is joined (using Shape)
to a purchase_request_item and also a purchase_request_discussion. I can
update existing purchase requests just fine, but when I try to enter a new
purchase request, I get this error:

[0x80040E38][Microsoft Cursor Engine] Row cannot be located for updating.
Some values may have been changed since it was last read.

It's as if Infopath is trying to run an Update when it should be running an
Insert.

Does either table have a trigger?

Regards,
Steve
 
S

Steve van Dongen [MSFT]


When it queries the database, it stores a copy of the original data
in, ironically, an originalData node that is not exposed in the UI.
When you submit it diffs the current data vs. the original data and
generates the appropriate insert, update, and delete statements.
I have a form that's created from an SQL Server database connection. The
main connection consists of a purchase_request which is joined (using Shape)
to a purchase_request_item and also a purchase_request_discussion. I can
update existing purchase requests just fine, but when I try to enter a new
purchase request, I get this error:

[0x80040E38][Microsoft Cursor Engine] Row cannot be located for updating.
Some values may have been changed since it was last read.

It's as if Infopath is trying to run an Update when it should be running an
Insert.

Does either table have a trigger?
Yes, there is a trigger. When I remove that trigger, the problem goes away.
Thought I had the logic sorted out so the trigger wouldn't have anything to
insert / update on new inserts, but it appears to not be working. So, back
to trigger debugging.

Thanks for the help!

Don't thank me yet, I didn't help yet. ;)

Here is the code for a trigger I had created on one of my databases:

CREATE TRIGGER transferAssetOnUpdate ON dbo.[df-assets]
AFTER UPDATE
AS
BEGIN
PRINT 'transferAssetOnUpdate - UPDATE'
DELETE from [df-assets]
WHERE asset_pk NOT IN (SELECT asset_pk FROM inserted)
AND asset_num IN (SELECT asset_num FROM inserted)
END

When I would update rows in my database I would sometimes see the
error you got. Running a trace on the server, the top-level RPC call
was

exec sp_executesql N'
UPDATE "df-assets"."dbo"."df-assets" SET "dual_proc"=@P1 WHERE
"asset_pk"=@P2 AND "dual_proc"=@P3;
UPDATE "df-assets"."dbo"."df-assets" SET "dual_proc"=@P4 WHERE
"asset_pk"=@P5 AND "dual_proc"=@P6
', N'@P1 bit,@P2 int,@P3 bit,@P4 bit,@P5 int,@P6 bit', 1, 308, 0, 0,
307, 1

and that translates into the following sequence of SQL statements
(including trigger code firing):

1. UPDATE "df-assets"."dbo"."df-assets" SET "dual_proc"=1 WHERE
"asset_pk"=308 AND "dual_proc"=0;
-- transferAssetOnUpdate
2. DELETE from [df-assets] where asset_pk NOT IN (SELECT asset_pk FROM
inserted) and asset_num IN (SELECT asset_num FROM inserted)

3. UPDATE "df-assets"."dbo"."df-assets" SET "dual_proc"=0 WHERE
"asset_pk"=307 AND "dual_proc"=1;
-- transferAssetOnUpdate
4. DELETE from [df-assets] where asset_pk NOT IN (SELECT asset_pk FROM
inserted) and asset_num IN (SELECT asset_num FROM inserted)

The ADO concurrency model relies on the rowcount to detect when a row
matches given the WHERE clause. When rowcount = 0, ADO assumes that
there was no row match and raises a concurrency error.
The problem here is that the DELETE statement in the trigger ( 2. )
doesn't match any rows (there is no duplicates), which trick ADO into
thinking there was a concurrency conflict when there was none!
The solution is to add "set nocount" statements in the trigger code,
so that the rowcount accurately reflects the effect of the UPDATE
statement.

i.e. the correct solution for my example would be this

BEGIN
SET NOCOUNT ON
PRINT 'transferAssetOnUpdate - UPDATE'
DELETE from [df-assets]
WHERE asset_pk NOT IN (SELECT asset_pk FROM inserted)
AND asset_num IN (SELECT asset_num FROM inserted)
SET NOCOUNT FF
END

If you still have trouble, try posting your trigger code.

Regards,
Steve
 
W

wjasonstrutz

Steve,

Thanks for the detail. I figured that was how the model worked, but it's
good to know for sure.

I managed to get the trigger fixed pretty quickly. I added an IF statement
so the trigger only executes if the inserted table has non-null values in a
particular field. I may go back and use your solution when I tune for
performance though.

Thanks,
jason.

Steve van Dongen said:

When it queries the database, it stores a copy of the original data
in, ironically, an originalData node that is not exposed in the UI.
When you submit it diffs the current data vs. the original data and
generates the appropriate insert, update, and delete statements.
I have a form that's created from an SQL Server database connection. The
main connection consists of a purchase_request which is joined (using Shape)
to a purchase_request_item and also a purchase_request_discussion. I can
update existing purchase requests just fine, but when I try to enter a new
purchase request, I get this error:

[0x80040E38][Microsoft Cursor Engine] Row cannot be located for updating.
Some values may have been changed since it was last read.

It's as if Infopath is trying to run an Update when it should be running an
Insert.

Does either table have a trigger?
Yes, there is a trigger. When I remove that trigger, the problem goes away.
Thought I had the logic sorted out so the trigger wouldn't have anything to
insert / update on new inserts, but it appears to not be working. So, back
to trigger debugging.

Thanks for the help!

Don't thank me yet, I didn't help yet. ;)

Here is the code for a trigger I had created on one of my databases:

CREATE TRIGGER transferAssetOnUpdate ON dbo.[df-assets]
AFTER UPDATE
AS
BEGIN
PRINT 'transferAssetOnUpdate - UPDATE'
DELETE from [df-assets]
WHERE asset_pk NOT IN (SELECT asset_pk FROM inserted)
AND asset_num IN (SELECT asset_num FROM inserted)
END

When I would update rows in my database I would sometimes see the
error you got. Running a trace on the server, the top-level RPC call
was

exec sp_executesql N'
UPDATE "df-assets"."dbo"."df-assets" SET "dual_proc"=@P1 WHERE
"asset_pk"=@P2 AND "dual_proc"=@P3;
UPDATE "df-assets"."dbo"."df-assets" SET "dual_proc"=@P4 WHERE
"asset_pk"=@P5 AND "dual_proc"=@P6
', N'@P1 bit,@P2 int,@P3 bit,@P4 bit,@P5 int,@P6 bit', 1, 308, 0, 0,
307, 1

and that translates into the following sequence of SQL statements
(including trigger code firing):

1. UPDATE "df-assets"."dbo"."df-assets" SET "dual_proc"=1 WHERE
"asset_pk"=308 AND "dual_proc"=0;
-- transferAssetOnUpdate
2. DELETE from [df-assets] where asset_pk NOT IN (SELECT asset_pk FROM
inserted) and asset_num IN (SELECT asset_num FROM inserted)

3. UPDATE "df-assets"."dbo"."df-assets" SET "dual_proc"=0 WHERE
"asset_pk"=307 AND "dual_proc"=1;
-- transferAssetOnUpdate
4. DELETE from [df-assets] where asset_pk NOT IN (SELECT asset_pk FROM
inserted) and asset_num IN (SELECT asset_num FROM inserted)

The ADO concurrency model relies on the rowcount to detect when a row
matches given the WHERE clause. When rowcount = 0, ADO assumes that
there was no row match and raises a concurrency error.
The problem here is that the DELETE statement in the trigger ( 2. )
doesn't match any rows (there is no duplicates), which trick ADO into
thinking there was a concurrency conflict when there was none!
The solution is to add "set nocount" statements in the trigger code,
so that the rowcount accurately reflects the effect of the UPDATE
statement.

i.e. the correct solution for my example would be this

BEGIN
SET NOCOUNT ON
PRINT 'transferAssetOnUpdate - UPDATE'
DELETE from [df-assets]
WHERE asset_pk NOT IN (SELECT asset_pk FROM inserted)
AND asset_num IN (SELECT asset_num FROM inserted)
SET NOCOUNT FF
END

If you still have trouble, try posting your trigger code.

Regards,
Steve
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top