Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
Home
Discussion Groups
DB Engine
SQL ServerMSDESQL Server CE
Services
Analysis (Data Mining)Analysis (OLAP)DTSIntegration ServicesNotification ServicesReporting Services
Programming
CLRConnectivitySQLXML
Other Technologies
ClusteringEnglish QueryFull-Text SearchReplicationService Broker
General
Data WarehousingPerformanceSecuritySetupSQL Server ToolsOther SQL Server Topics
DirectoryUser Groups
Related Topics
MS AccessOther DB ProductsMS Server Products.NET DevelopmentVB DevelopmentJava DevelopmentMore Topics ...

SQL Server Forum / DB Engine / SQL Server / December 2008

Tip: Looking for answers? Try searching our database.

Trigger and Misc.

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Rodger Dultrie (not the actor) - 28 Nov 2008 16:43 GMT
We have a few issues that we hope to get some information on as follows. We are not sql gurus and are learning on the fly (not the best, but we have to deal with this on a day by day basis while we interview SQL dbs).

A) We have a DB that looks like this, we inherited this from a company that we just bought, so we need to keep this intact until we move to another application development platform and redesign the DB,

B) Here is the partial schema:

A) DB: Customer_Activities (there are more tables etc, but I think this is the basics that would be needed to understand the problem):
B) Customers: customerno (PK), purchase_no (idx), total_purchase, no_purchases (int), other columns
C) Purchases: purchase_no (pk), customerno (idx-dupes), item_qty (int), item_cost (float), item_total (float) other columns,

relationship looks like this: customers -> purchases (1:many) on customerno.

Our current VB app performs the following:

1) when a new customer is added, the total_purchases is set to 0.00, no_purchases is set to 0 (zero),
2) when a customer makes a purchase, the following is performed "in the app",
   a) item_total is calculated as item_qty * item_cost,
   b) item_total is added to [customers].[total_purchases],
   c) item_qty is added to [customers].[no_purchases],

As we are looking at moving to a different dev platform (such as vb.net or Delphi etc), we would like to have sql2k5 perform these calculations via triggers. I personally don't think the app should be doing this anyway. :)

So we would like to have a trigger(s) on purchases that does the following (there would never be more than on record inserted/updated or deleted at a time):

i) adds the value from 2-b above to the total_purchase in Customers,
ii) adds the value from 2-c above to no_purchases in Customers,
iii) if a value in purchases.item_qty or item_cost is changed, this needs to be recalculated in the trigger (as an update I would suspect). Then this would have to be reflected in customers.total_purchases and no_purchases.

Another thing this app doesn't take into account is if a customers purchase is deleted, then there are inaccurate totals left in the appropriate customers columns (total_purchases and no_purchases). We think that the out-going db was doing this via his query tool or something similar (he's not cooperative in a few areas) and documentation is sparse if not incomplete. Very frustrating.

We've tried several different triggers and have the following problems and just can't seem to get the result we need. We do have a few triggers that update total_purchases (when added), but nothing for deletion or change.

I do realize that this is a lot to ask, and as mentioned above the whole thing needs a redesign (which hopefully a new SQL db can do for us), but we need something for the interim.

I've spent the better part of 2 days scouring the web and have made some progress, but alas not nearly enough. I've purchased several resource books for SQL but just don't have the time to spend on this as much as needed at this point.

Any help or suggestions (pointers to web sites etc), is "hugely" appreciated.

I can give more information if needed, but I hope this is basically enough to give you all an idea as to where we are at.

If anyone can suggest a "good" source for DB admins, we would appreciate that as well). Monster, Workopolis etc) have far too many submissions to review at this time.

Thanks in advance Rodger.
newscorrespondent@charter.net - 28 Nov 2008 19:09 GMT
This should be close to what you need. It could all be done in a single
trigger but three triggers may be easier to understand.

Be sure to check my math in case I did not fully understand your question.

Happy Thanksgiving

Tom

SET NOCOUNT ON;

IF EXISTS (SELECT 1 FROM  SYS.OBJECTS WHERE  OBJECT_ID =
OBJECT_ID('dbo.Customers') AND TYPE = 'U ')
BEGIN    drop table dbo.Customers;
END
GO
create table dbo.Customers
(    customerno        varchar(255)    primary key
    ,purchase_no    int
    ,total_purchase    float            default 0.0
    ,no_purchases    int                default 0.0
);
go
IF EXISTS (SELECT 1 FROM  SYS.OBJECTS WHERE  OBJECT_ID =
OBJECT_ID('dbo.Purchases') AND TYPE = 'U ')
BEGIN    drop table dbo.Purchases;
END
GO
go
create table dbo.Purchases
(    purchase_no        int identity primary key
    ,customerno            varchar(255)
    ,item_qty            int
    ,item_cost            float   
    ,item_total            float
)
go
IF EXISTS (SELECT 1 FROM  SYS.OBJECTS WHERE  OBJECT_ID =
OBJECT_ID('dbo.PurchasesInsert') AND TYPE = 'TR')
BEGIN    DROP TRIGGER dbo.PurchasesInsert;
END
GO
create trigger dbo.PurchasesInsert
    on    dbo.Purchases for insert
as
UPDATE    dbo.Customers
    SET    total_purchase  =    INSERTED.item_total
        ,no_purchases    =    INSERTED.item_qty
FROM    INSERTED
JOIN    dbo.Customers ON INSERTED.customerno = Customers.customerno;
return;
go
IF EXISTS (SELECT 1 FROM  SYS.OBJECTS WHERE  OBJECT_ID =
OBJECT_ID('dbo.PurchasesUpdate') AND TYPE = 'TR')
BEGIN    DROP TRIGGER dbo.PurchasesUpdate;
END
GO
create trigger dbo.PurchasesUpdate
    on    dbo.Purchases for update
as
UPDATE    dbo.Customers
    SET    total_purchase  =    total_purchase    +    (INSERTED.item_total    -
    DELETED.item_total)
        ,no_purchases    =    no_purchases    +    (INSERTED.item_qty        - DELETED.item_qty)
FROM    INSERTED
JOIN    dbo.Customers ON INSERTED.customerno = Customers.customerno
JOIN    DELETED         ON INSERTED.customerno = DELETED.customerno;
return;
go
IF EXISTS (SELECT 1 FROM  SYS.OBJECTS WHERE  OBJECT_ID =
OBJECT_ID('dbo.PurchasesDelete') AND TYPE = 'TR')
BEGIN    DROP TRIGGER dbo.PurchasesDelete;
END
GO
create trigger dbo.PurchasesDelete
    on    dbo.Purchases for delete
as
UPDATE    dbo.Customers
    SET    total_purchase  =    total_purchase    - DELETED.item_total
        ,no_purchases    =    no_purchases    - DELETED.item_qty
FROM    DELETED
JOIN    dbo.Customers ON DELETED.customerno = Customers.customerno;
return;
go

INSERT dbo.Customers (customerno) VALUES('Cust 1');
SELECT * FROM dbo.Customers;

INSERT dbo.Purchases (customerno, item_qty, item_cost, item_total) VALUES
('Cust 1', 1, 2.00, 2.00);
SELECT * FROM dbo.Customers;
UPDATE dbo.Purchases
    SET  item_qty = 2
        ,item_total = 4
WHERE customerno = 'Cust 1';
SELECT * FROM dbo.Customers;

DELETE dbo.Purchases WHERE customerno = 'Cust 1';
SELECT * FROM dbo.Customers;
steve dassin - 28 Nov 2008 21:11 GMT
Hello,
 >As we are looking at moving to a different dev platform (such as vb.net or Delphi etc),

 You should check out Dataphor as well @
 www.dataphor.org
 And
 www.databaseconsultinggroup.com/index.html

 Note I have no connection to the above but use and advocate the Dataphor system for application development.
 Dataphor can use sql server for the data repository and can easily communicate with it.

 >we would like to have sql2k5 perform these calculations via triggers. I personally don't think the app should be doing this anyway. :)

 Sql server triggers are a poor choice for application development. Dataphor will completely take over all these functions for you. Dataphor triggers (events) are 'row' oriented and there are true 'before' , 'after' and instead of options. In effect you'll do all your development in Dataphor
 and let sql server take are of data storage and access.

 More on Dataphor (with many code samples) on my blog:
 www.beyondsql.blogspot.com

 best,
 steve
newscorrespondent@charter.net - 28 Nov 2008 23:09 GMT
Exactly why are SQL Server triggers a poor choice?

Tom
steve dassin - 29 Nov 2008 02:49 GMT
Hi,

I think these articles, by someone who works at MS, sum up the current state
of MS triggers (and more),

The Trouble with Triggers
http://www.sqlskills.com/blogs/conor/default.aspx

CHECK Constraints vs. TRIGGERS
http://blogs.msdn.com/conor_cunningham_msft/default.aspx

The vote is in! (CHECK CONSTRAINTS vs. TRIGGERS vs. STORED PROCEDURE
business logic)
http://blogs.msdn.com/conor_cunningham_msft/default.aspx

I'm not even going into their implementation, it's the nature of MS triggers
that is the problem. Applications should be developed with the idea that it
should be a set based exercise because that is what MS triggers are best
suited for. A classic case of the tail wagging the dog. That application
development should fit neatly into the framework of sql server is
ridiculous. AD involves granularity which is either missing in the MS
trigger or is so obtuse it turns developers off. AD involves the idea of a
'row'. There is no concept of a row anywhere in sql server. A set, a table
is NOT a row. I agree  there's a multi-row consideration. And I consider it
ridiculous. The idea that an application is an event driven excercise has no
built in intent in sql server. And the problems extend out from triggers.
The constraint in sql server is itself a constraint to AD. It's far to
immature to be of use to enterprise apps. It would seem that those at MS who
work on sql server are not application developers. Those that are work on
Linq. What's wrong with this picture?:) Too many sql experts are neophytes
in AD. And too many sql experts have not worked on any other system. So they
cannot see themselves arguing to fit a round peg into a square whole:)
Sql server is a kewl sixteen wheeler that can haul a_s down a superhighway.
It's for long hauls and heavy loads. But you don't take a 16 wheeler to Le
Mans. There you need a finely tuned and detailed thoroughbred than can
maneuver in tight and small quarters. With Dataphor/sql server you get the
benefit of each:) Try it and see for yourself. You only have to pay for
one:)

(A prior comment on triggers including Conors blog on triggers:
http://beyondsql.blogspot.com/2008/07/demoralization-by-trigger.html)

www.beyondsql.blogspot.com

best,
steve

> Exactly why are SQL Server triggers a poor choice?
Rodger Dultrie (not the actor) - 29 Nov 2008 19:29 GMT
Thank you all for the detailed information and insights.

As for now, we are looking at using and implementing the code fragments and
examples that were presented by Tom. When we have engaged a DB, we will
certainly look at the other options presented by Steve. I don't want to
complicate this scenario more that what I need to at this point.

Again, I thank everyone for their time, patience and contributions.

Rodger.

> Hi,
>
[quoted text clipped - 45 lines]
>
>> Exactly why are SQL Server triggers a poor choice?
Rodger Dultrie (not the actor) - 29 Nov 2008 20:23 GMT
> Thank you all for the detailed information and insights.
>
[quoted text clipped - 56 lines]
>>
>>> Exactly why are SQL Server triggers a poor choice?
Rodger Dultrie (not the actor) - 29 Nov 2008 20:26 GMT
I just wanted to reply to Tom's post specifically.

I went over your code and it's pretty much what we were trying to do. What
we didn't perform in all cases was the appropriate JOINS on the tables. For
example, when we wanted to decrement total purchases, we didn't join
anything to anything which explains why nothing was changed.

Not sure why the "Return" statement is there, but we left it in.

The help is greatly appreciated.

Thank you.

> Thank you all for the detailed information and insights.
>
[quoted text clipped - 56 lines]
>>
>>> Exactly why are SQL Server triggers a poor choice?
newscorrespondent@charter.net - 29 Nov 2008 21:24 GMT
The return statement is a left over habit. A long time ago when MS SQL was
being born it was a Sybase database in a Microsoft box. I worked in the
Sybase Advanced Systems Group. Sybase recomended a return statement after
all procedures and a trigger is a kind of procedure. It is not necessary and
could be removed.

I looked over those triggers again and there perhaps are two more statements
that should have been included. So much for coding on a holiday.

IF @@ROWCOUNT = 0 RETURN;
SET NOCOUNT ON;

The "IF" statement bypasses the trigger if no rows were updated.

The SET NOCUNT ON; eliminates rowcounts from flowing through the network.
You should do this in every procedure unless you specifically know that the
rowcount message is expected by the client.
The "IF" statement must be before the SET statement because the SET
statement will reset @@ROWCOUNT.

Have a great week-end.

Tom
steve dassin - 30 Nov 2008 03:43 GMT
> The return statement is a left over habit. A long time ago when MS SQL was
> being born it was a Sybase database in a Microsoft box. I worked in the
> Sybase Advanced Systems Group.

I'm sorry but the trigger must have been designed while partying. I can only
assume the MS group was invited too:(:)
Rodger Dultrie (not the actor) - 30 Nov 2008 13:07 GMT
Thanks Tom. As well, you have a good one.

Rodger
> The return statement is a left over habit. A long time ago when MS SQL was
> being born it was a Sybase database in a Microsoft box. I worked in the
[quoted text clipped - 22 lines]
>
> Tom
Rodger Dultrie (not the actor) - 03 Dec 2008 19:56 GMT
Hi again Tom. I understand your comments, however the question that comes to
mind is, if a Trigger were being fired, would that not suggest that there
were/are rows being updated?

I can think of a situation that wouldn't and that would be if one were
rolling back a transaction, at least that's what I am getting from the SQL
programmers books that I bought - could be out to lunch here tho.

Again, thanks.

> The return statement is a left over habit. A long time ago when MS SQL was
> being born it was a Sybase database in a Microsoft box. I worked in the
[quoted text clipped - 22 lines]
>
> Tom
newscorrespondent@charter.net - 03 Dec 2008 21:51 GMT
UPDATE SOMETABLE
    SET SOMECOLUMN = 123
WHERE SOME CONDITION IS NEVER MET.

This would result in no rows being updated. This would cause the trigger to
fire. A similar statement could cause DELETE to fire a trigger with no
deletes. Inserting rows with an INSERT .... SELECT.... WHERE could also
cause this to happen.

It may be useful to do something when this condition occurs particularly in
a test environment. You could notify the application what was going on and
perhaps the application could save a trip to the server.

The trigger is fired because an INSERT, UPDATE or DELETE was executed
whether or not anything was updated.

There is one other condition you might want to be aware of.

Replication will cause an additional update on the rows. You may not want
the work of your trigger to be done twice. If you only want parts of the
trigger to be run one time for each INSERT, DELETE or UPDATE you need to
check the nest level and not run if it is greater than 1.

IF TRIGGER_NESTLEVEL(object_ID('TRIGGER NAME')) > 1 RETURN;

Tom
Hugo Kornelis - 04 Dec 2008 00:03 GMT
(snip)
>I can think of a situation that wouldn't and that would be if one were
>rolling back a transaction, at least that's what I am getting from the SQL
>programmers books that I bought - could be out to lunch here tho.

Hi Rodger,

In addition to Tom's excellent reply, may I suggest dumping that book?
No triggers are fired during a rollback. Any changes made during the
trigger execution are in the log file, so when SQL Server undoes
everything in the log file as part of the ROLLBACK, all effects of the
trigger are automatically undone.

Signature

Hugo Kornelis, SQL Server MVP
My SQL Server blog: http://sqlblog.com/blogs/hugo_kornelis

Rodger Dultrie (not the actor) - 05 Dec 2008 17:52 GMT
Thank you both Tom and Hugo. The explanation is, as usual, quite complete
and very informative.

Book Dumped.

Cheers.

> (snip)
>>I can think of a situation that wouldn't and that would be if one were
[quoted text clipped - 8 lines]
> everything in the log file as part of the ROLLBACK, all effects of the
> trigger are automatically undone.
Alex Kuznetsov - 30 Nov 2008 03:01 GMT
> Hi,
>
[quoted text clipped - 44 lines]
>
> > Exactly why are SQL Server triggers a poor choice?

Also sometimes triggers do not fire:

www.devx.com/dbzone/Article/31985/0/page/2
steve dassin - 30 Nov 2008 21:05 GMT
My apologies for my oversight. I read your kewl article and should have
included it:(

>.
>Also sometimes triggers do not fire:

www.devx.com/dbzone/Article/31985/0/page/2
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2009 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.