Groups | Blog | Home
all groups > sql server programming > january 2007 >

sql server programming : Instead Of trigger and accessing inserted virtual table from dynamic query



Kyle
1/24/2007 10:31:06 PM
I have had a cascading series of problems stemming from partitioned
tables with identity columns, and it's ended at Instead Of triggers and
the inserted virtual table needing to be referenced from dynamic sql.
SQL Server 2000.

I've got partitioned tables and a partitioned view, and each of the
partitioned tables has an identity column (each table properly seeded
so id column is unique across all tables). This existence of the
identity columns prevents the partitioned view from being insertable (I
don't say updatable, because the partitioned view accepts updates and
deletes just fine).

So, I have created an Instead Of trigger on the partitioned view and I
will properly distribute the values to the correct partitioned table
upon insert myself if SQL Server 2K won't do it for me. However, the
name of the table to insert into is dependent on one of the values in
the insert statement - obviously, because this is a partitioned view
and by definition there must be a partitioned column that determines
base table. So, I want to look at the value of the partitioned column
in the inserted virtual table in order to determine which base table to
insert into.

That's easy, and then I create SQL to insert the data into the
appropriate base table, but it must be dynamic SQL because the name of
the table must be determined at runtime. Only one row will be inserted
at a time so I don't have to worry about bulk inserts.

select @dt = max(event_date) from inserted
Execute sp_executesql N'INSERT INTO ' + dbo.GetPartitionTableName(@dt)
+ ' select a, b, c, d from inserted'

This does not work, presumably because inserted is a virtual table. Is
there any way to solve this problem? Insert performance is critical,
so copying to a real temp table is not an option. It is OK to be
forced to use a dummy for the identity field in the insert (as the
instead of trigger mandates), but it is not acceptable to change all
the client code that currently is 'insert into massive
unpartitionedtable' to 'figure out which base table applies' and
'insert into partitionedbasetable'. That must be abstracted from the
client calls.

Any help is mucho mucho appreciated.
ML
1/24/2007 11:58:41 PM
Build your dynamic query *before* invoking sp_executesql.

E.g.

declare @q_str nvarchar(4000)

set @q_str = N'INSERT INTO ' + dbo.GetPartitionTableName(@dt) + ' select a,
b, c, d from inserted'

However, to do things properly do not use dynamic SQL, but rather use a
set-based approach:

insert table1
(
<columns>
)
select <columns>
from inserted
where (<partitioning condition for table1>)

insert table2
(
<columns>
)
select <columns>
from inserted
where (<partitioning condition for table2>)

....


ML

---
kyleyost NO[at]SPAM gmail.com
1/25/2007 8:31:07 AM
Thank you for the response.

I do build the dynamic sql before invoking sp_executesql and still get
error about table not found - "inserted". Does executing a sp change
the scope of what's accessible in some way?

I would like to do your non-dynamic SQL approach, but as the tablename
is determined by the event_date which is a column of the insert, I
cannot specify all possible tablenames as you describe below.
Tablename = xxxYYMMDD for the Sunday of the week of event_date. So, if
event_date was 11/14/2006 tablename would be xxx061112, if it were
1/25/07 it would be xxx070121. Every week a new table is created with
proper indexes and check constraints, the partitioned view updated to
include this new table for the week and to remove the oldest table.

So, if I cannot specify in the trigger definition the tables, then I
must use dynamic sql. And if dynamic sql cannot use the
memory-resident inserted table then I am stuck.

It seems that what I am doing should be pretty common. I'm
partitioning one very large table with a unique identity id by date and
dynamically modifying the partitioned view to keep the "operational"
set of data limited. This inability of partitioned tables to use
identity columns seems pretty much to render partitioning useless as
far as I can tell.



[quoted text, click to view]
Alex Kuznetsov
1/25/2007 9:00:46 AM


[quoted text, click to view]


Don't do it - static SQL performs much better in such cases:

INSERT INTO Sales2005(...) SELECT ... FROM INSERTED WHERE '20050101' <=
SaleDate AND SaleDate < '20060101'
INSERT INTO Sales2006(...) SELECT ... FROM INSERTED WHERE '20060101' <=
SaleDate AND SaleDate < '20070101'
INSERT INTO Sales2007(...) SELECT ... FROM INSERTED WHERE '20070101' <=
SaleDate AND SaleDate < '20080101'


-----------------------
Alex Kuznetsov
http://sqlserver-tips.blogspot.com/
http://sqlserver-puzzles.blogspot.com/
Kyle
1/25/2007 12:05:55 PM
I don't know the names of the tables! I can't make it static.
I can't use the virtual table inserted in a dynamic sql.

I have no options. As far as I know.

On Jan 25, 12:00 pm, "Alex Kuznetsov" <AK_TIREDOFS...@hotmail.COM>
[quoted text, click to view]
Alex Kuznetsov
1/25/2007 12:30:50 PM


[quoted text, click to view]

Because insert performance is critical for you, do as follows:

When you create your partitioned view, you do know all the table names
it selects from. List all the tables in your INSTEAD OF trigger.
Suppose you have just 3 partitions, then the body of your INSTEAD OF
trigger would look as follows:

INSERT INTO Sales2005(...) SELECT ... FROM INSERTED WHERE '20050101' <=

SaleDate AND SaleDate < '20060101'
INSERT INTO Sales2006(...) SELECT ... FROM INSERTED WHERE '20060101' <=

SaleDate AND SaleDate < '20070101'
INSERT INTO Sales2007(...) SELECT ... FROM INSERTED WHERE '20070101' <=

SaleDate AND SaleDate < '20080101'

When you add one more partition to your view, add another insert to
your trigger. Also consider inserting more than one row at a time -
this might be way faster.

-----------------------
Alex Kuznetsov
http://sqlserver-tips.blogspot.com/
http://sqlserver-puzzles.blogspot.com/
Kyle
1/25/2007 9:19:59 PM
Thank you. Changing the definition of the trigger when I change the
definition of the view makes sense and never occured to me. I will try
that. I do wish I could eliminate the use of the identity column in
this table, but it's too late for that.

[quoted text, click to view]
Erland Sommarskog
1/25/2007 11:07:40 PM
Kyle (knygroups@gmail.com) writes:
[quoted text, click to view]

Of course you know the name of the tables! How else were you able
to create the view?

Yes, the view definition changes every once in a while. So does the
trigger. Write a program that generates the trigger. Possibly the
creation of the view is automated already, then just add the trigger
to that.

[quoted text, click to view]

Correct. inserted/deleted are only visible directly in the trigger,
and dynamic SQL is a scope, just as if you had called a stored
procedure.

[quoted text, click to view]

You could also skip the IDENTITY column and roll your own. This is
certainly far more painless when it comes to implementation. However,
IDENTITY is good when you have a high insertion frequency which you
seemed to have, so maybe you need to write that trigger generator anyway.


--
Erland Sommarskog, SQL Server MVP, esquel@sommarskog.se

Books Online for SQL Server 2005 at
http://www.microsoft.com/technet/prodtechnol/sql/2005/downloads/books.mspx
Books Online for SQL Server 2000 at
ML
1/26/2007 1:35:01 AM
In SQL 2005 you can automate the (re-)creation of both the view as well as
the trigger through the use of a DDL trigger: as soon as a new table is added
or removed both dependent objects can be updated appropriately.

Then again, SQL 2005 supports table partitioning. :)


ML

---
AddThis Social Bookmark Button