Hi,
I have a hierarchy of tables, like "Company" on top, with "Offices"
underneath and "People" underneath those and "Contact info" under those,
etc.
Now, a record gets deleted in the "Company" table. This means the Offices
and People attached to them get deleted also.
I could do this via triggers to ensure consistency, or via a stored
procedure:
CREATE PROCEDURE myspDeleteCompany ( @.Id INT )
{
-- delete other stuff first belonging to the company
DELETE FROM Stuff WHERE ParentId = @.Id
-- delete offices first
EXEC myspDeleteOffices( @.Id );
-- delete self
DELETE FROM Companies WHERE Id=@.Id
}
CREATE PROCEDURE myspDeleteOffices ( @.ParentId )
{
-- delete people first
DELETE FROM People WHERE ParentId IN (SELECT Id FROM Companies WHERE
ParentId = @.Id)
-- delete offices
DELETE FROM Companies WHERE ParentId = @.Id
}
Triggers would be better, but I need to use procedures because I am not
really deleting records, rather setting up a flag Deleted=1. So all DELETE
statements are actually UPDATE <record> SET Deleted=1. This gets tricky when
using triggers. So let's forget about it.
Questions:
- myspDeleteOffices needs to delete people. For this I have to list all
offices first and then delete people that work at that office. Would a
cursor not be better?
- More importantly, deleting people or offices could fail.. in which case
things from the Stuff table may have already been deleted. To roll these
changes back, so that either everything gets deleted (all steps succeed) or
everything gets restored, what should I do? Use transactions?
Something like this?
CREATE PROCEDURE myspDeleteCompany ( @.Id INT )
{
BEGIN TRANSACTION
-- delete other stuff first belonging to the company
DELETE FROM Stuff WHERE ParentId = @.Id
-- delete offices first
EXEC myspDeleteOffices( @.Id );
-- delete self
DELETE FROM Companies WHERE Id=@.Id
COMMIT TRANSACTION
}
If an error occurs, does the transaction get rolled back automatically
without me calling ROLLBACK TRANSACTION ?
Usually when an error occurs, the query aborts, so I wouldn't be able to
call ROLLBACK after that.
Just have no clue how this is done in real life situations.
LisaLisa
Have you considered using INSTEAD OF DELETE Triggers?
"Lisa Pearlson" <no@.spam.plz> wrote in message
news:%23twTI99SFHA.1896@.TK2MSFTNGP14.phx.gbl...
> Hi,
> I have a hierarchy of tables, like "Company" on top, with "Offices"
> underneath and "People" underneath those and "Contact info" under those,
> etc.
> Now, a record gets deleted in the "Company" table. This means the Offices
> and People attached to them get deleted also.
> I could do this via triggers to ensure consistency, or via a stored
> procedure:
> CREATE PROCEDURE myspDeleteCompany ( @.Id INT )
> {
> -- delete other stuff first belonging to the company
> DELETE FROM Stuff WHERE ParentId = @.Id
> -- delete offices first
> EXEC myspDeleteOffices( @.Id );
> -- delete self
> DELETE FROM Companies WHERE Id=@.Id
> }
> CREATE PROCEDURE myspDeleteOffices ( @.ParentId )
> {
> -- delete people first
> DELETE FROM People WHERE ParentId IN (SELECT Id FROM Companies WHERE
> ParentId = @.Id)
> -- delete offices
> DELETE FROM Companies WHERE ParentId = @.Id
> }
> Triggers would be better, but I need to use procedures because I am not
> really deleting records, rather setting up a flag Deleted=1. So all DELETE
> statements are actually UPDATE <record> SET Deleted=1. This gets tricky
when
> using triggers. So let's forget about it.
> Questions:
> - myspDeleteOffices needs to delete people. For this I have to list all
> offices first and then delete people that work at that office. Would a
> cursor not be better?
> - More importantly, deleting people or offices could fail.. in which case
> things from the Stuff table may have already been deleted. To roll these
> changes back, so that either everything gets deleted (all steps succeed)
or
> everything gets restored, what should I do? Use transactions?
> Something like this?
> CREATE PROCEDURE myspDeleteCompany ( @.Id INT )
> {
> BEGIN TRANSACTION
> -- delete other stuff first belonging to the company
> DELETE FROM Stuff WHERE ParentId = @.Id
> -- delete offices first
> EXEC myspDeleteOffices( @.Id );
> -- delete self
> DELETE FROM Companies WHERE Id=@.Id
> COMMIT TRANSACTION
> }
> If an error occurs, does the transaction get rolled back automatically
> without me calling ROLLBACK TRANSACTION ?
> Usually when an error occurs, the query aborts, so I wouldn't be able to
> call ROLLBACK after that.
> Just have no clue how this is done in real life situations.
> Lisa
>|||You can do that with DRI (declaritive referential integrity) where you can
modify (Delete,Update) the "parent" which automatically takes affect on all
"children" referenced to this.
Example:
ALTER TABLE [dbo].[Order Details] ADD
CONSTRAINT [FK_Order_Details_Orders] FOREIGN KEY
(
[OrderID]
) REFERENCES [dbo].[Orders] (
[OrderID]
) ON DELETE CASCADE ON UPDATE CASCADE ,
CONSTRAINT [FK_Order_Details_Products] FOREIGN KEY
(
[ProductID]
) REFERENCES [dbo].[Products] (
[ProductID]
)
GO
HTH, Jens Suessmeyer.
http://www.sqlserver2005.de
--
"Lisa Pearlson" <no@.spam.plz> schrieb im Newsbeitrag
news:%23twTI99SFHA.1896@.TK2MSFTNGP14.phx.gbl...
> Hi,
> I have a hierarchy of tables, like "Company" on top, with "Offices"
> underneath and "People" underneath those and "Contact info" under those,
> etc.
> Now, a record gets deleted in the "Company" table. This means the Offices
> and People attached to them get deleted also.
> I could do this via triggers to ensure consistency, or via a stored
> procedure:
> CREATE PROCEDURE myspDeleteCompany ( @.Id INT )
> {
> -- delete other stuff first belonging to the company
> DELETE FROM Stuff WHERE ParentId = @.Id
> -- delete offices first
> EXEC myspDeleteOffices( @.Id );
> -- delete self
> DELETE FROM Companies WHERE Id=@.Id
> }
> CREATE PROCEDURE myspDeleteOffices ( @.ParentId )
> {
> -- delete people first
> DELETE FROM People WHERE ParentId IN (SELECT Id FROM Companies WHERE
> ParentId = @.Id)
> -- delete offices
> DELETE FROM Companies WHERE ParentId = @.Id
> }
> Triggers would be better, but I need to use procedures because I am not
> really deleting records, rather setting up a flag Deleted=1. So all DELETE
> statements are actually UPDATE <record> SET Deleted=1. This gets tricky
> when using triggers. So let's forget about it.
> Questions:
> - myspDeleteOffices needs to delete people. For this I have to list all
> offices first and then delete people that work at that office. Would a
> cursor not be better?
> - More importantly, deleting people or offices could fail.. in which case
> things from the Stuff table may have already been deleted. To roll these
> changes back, so that either everything gets deleted (all steps succeed)
> or everything gets restored, what should I do? Use transactions?
> Something like this?
> CREATE PROCEDURE myspDeleteCompany ( @.Id INT )
> {
> BEGIN TRANSACTION
> -- delete other stuff first belonging to the company
> DELETE FROM Stuff WHERE ParentId = @.Id
> -- delete offices first
> EXEC myspDeleteOffices( @.Id );
> -- delete self
> DELETE FROM Companies WHERE Id=@.Id
> COMMIT TRANSACTION
> }
> If an error occurs, does the transaction get rolled back automatically
> without me calling ROLLBACK TRANSACTION ?
> Usually when an error occurs, the query aborts, so I wouldn't be able to
> call ROLLBACK after that.
> Just have no clue how this is done in real life situations.
> Lisa
>|||Lisa,
Its Ok if you are deleteing or Updating, you can use a trigger for the
purpose. This is what i believe.
for myspDeleteOffices, u can write a simple delete query instead of going
for Cursors. Cursors bring down the performance of the system because they
involve iterations and buffer storage
As u said, you can use transactions if u feel the batch job fails
thanks and regards
Chandra
"Lisa Pearlson" wrote:
> Hi,
> I have a hierarchy of tables, like "Company" on top, with "Offices"
> underneath and "People" underneath those and "Contact info" under those,
> etc.
> Now, a record gets deleted in the "Company" table. This means the Offices
> and People attached to them get deleted also.
> I could do this via triggers to ensure consistency, or via a stored
> procedure:
> CREATE PROCEDURE myspDeleteCompany ( @.Id INT )
> {
> -- delete other stuff first belonging to the company
> DELETE FROM Stuff WHERE ParentId = @.Id
> -- delete offices first
> EXEC myspDeleteOffices( @.Id );
> -- delete self
> DELETE FROM Companies WHERE Id=@.Id
> }
> CREATE PROCEDURE myspDeleteOffices ( @.ParentId )
> {
> -- delete people first
> DELETE FROM People WHERE ParentId IN (SELECT Id FROM Companies WHERE
> ParentId = @.Id)
> -- delete offices
> DELETE FROM Companies WHERE ParentId = @.Id
> }
> Triggers would be better, but I need to use procedures because I am not
> really deleting records, rather setting up a flag Deleted=1. So all DELETE
> statements are actually UPDATE <record> SET Deleted=1. This gets tricky wh
en
> using triggers. So let's forget about it.
> Questions:
> - myspDeleteOffices needs to delete people. For this I have to list all
> offices first and then delete people that work at that office. Would a
> cursor not be better?
> - More importantly, deleting people or offices could fail.. in which case
> things from the Stuff table may have already been deleted. To roll these
> changes back, so that either everything gets deleted (all steps succeed) o
r
> everything gets restored, what should I do? Use transactions?
> Something like this?
> CREATE PROCEDURE myspDeleteCompany ( @.Id INT )
> {
> BEGIN TRANSACTION
> -- delete other stuff first belonging to the company
> DELETE FROM Stuff WHERE ParentId = @.Id
> -- delete offices first
> EXEC myspDeleteOffices( @.Id );
> -- delete self
> DELETE FROM Companies WHERE Id=@.Id
> COMMIT TRANSACTION
> }
> If an error occurs, does the transaction get rolled back automatically
> without me calling ROLLBACK TRANSACTION ?
> Usually when an error occurs, the query aborts, so I wouldn't be able to
> call ROLLBACK after that.
> Just have no clue how this is done in real life situations.
> Lisa
>
>|||All of this seems like it would work ... however, what is the long term
impact of building a system like this? It seems like you will end up having
to write regular queries that skip over records that have the deleted flag
set to one. Over time, you will end up querying on a field that may not hav
e
good selectively which will result in scans.
If you need to maintain deleted data you may want to consider using DRI with
cascading deletes and then use delete triggers move your deleted data to
archive audit tables. This also typically has the benefit of adding additia
l
audit data such as users names, dates and times to your audit record.
"Chandra" wrote:
> Lisa,
> Its Ok if you are deleteing or Updating, you can use a trigger for the
> purpose. This is what i believe.
> for myspDeleteOffices, u can write a simple delete query instead of going
> for Cursors. Cursors bring down the performance of the system because they
> involve iterations and buffer storage
>
> As u said, you can use transactions if u feel the batch job fails
> thanks and regards
> Chandra
>
> "Lisa Pearlson" wrote:
>|||That's a SQL 8/2000 feature, isn't it?
"Uri Dimant" <urid@.iscar.co.il> wrote in message
news:%23Nok1$9SFHA.2756@.tk2msftngp13.phx.gbl...
> Lisa
> Have you considered using INSTEAD OF DELETE Triggers?
> "Lisa Pearlson" <no@.spam.plz> wrote in message
> news:%23twTI99SFHA.1896@.TK2MSFTNGP14.phx.gbl...
> when
> or
>|||Yes. Both cascading foreign key as well as instead of triggers were introduc
ed in 2000.
Tibor Karaszi, SQL Server MVP
http://www.karaszi.com/sqlserver/default.asp
http://www.solidqualitylearning.com/
"Lisa Pearlson" <no@.spam.plz> wrote in message news:edfskS$SFHA.3056@.TK2MSFTNGP14.phx.gbl..
.
> That's a SQL 8/2000 feature, isn't it?
> "Uri Dimant" <urid@.iscar.co.il> wrote in message news:%23Nok1$9SFHA.2756@.t
k2msftngp13.phx.gbl...
>|||The deleted flag is used for 2 reasons:
- Deleted records can be undeleted.
- All fields with deleted flag also have updated (timestamp) flag. This way
client program can poll 'changed records' efficiently
In SQL Server 2000 I heard you can use something like service that can
notify clients in some way of changed records. Mine was developed for SQL 7.
And you can create a type of maintanance plan that executes a DELETE FROM
<tablename> WHERE Deleted=1 AND TIMEDIFF(NOW(), Updated) > 1 month (or
something like this)
This should not impact performance too bad.. but you are right that moving
it to another "deleted" table would be better, because I do have to be
consistent to always check "deleted!=1" (because deleted can be 0 or NULL)
It's a bit of a mess :)
But I can do 'deleted' automatically with cascaded updates or something?
Lisa
"Chris Stransky" <ChrisStransky@.discussions.microsoft.com> wrote in message
news:821AB7CF-FABC-4C5B-A5E5-C134E9102DCC@.microsoft.com...
> All of this seems like it would work ... however, what is the long term
> impact of building a system like this? It seems like you will end up
> having
> to write regular queries that skip over records that have the deleted flag
> set to one. Over time, you will end up querying on a field that may not
> have
> good selectively which will result in scans.
> If you need to maintain deleted data you may want to consider using DRI
> with
> cascading deletes and then use delete triggers move your deleted data to
> archive audit tables. This also typically has the benefit of adding
> additial
> audit data such as users names, dates and times to your audit record.
> "Chandra" wrote:
>|||You could undelete by moving records back into the main table. Your undelet
e
function would have to be smart enough to move child records back.
Or
If you use DRI and include the deleted field in the PK then the cascade
would handle the child updates for you in all cases.
If you are worried about always selecting records that are not deleted you
could create a view that includes the deleted column ... but limit it to onl
y
undeleted records. All you have to do then is ensure that the view permits
updates that allow records to disappear from the view on update. That's in
BOL.
"Lisa Pearlson" wrote:
> The deleted flag is used for 2 reasons:
> - Deleted records can be undeleted.
> - All fields with deleted flag also have updated (timestamp) flag. This wa
y
> client program can poll 'changed records' efficiently
> In SQL Server 2000 I heard you can use something like service that can
> notify clients in some way of changed records. Mine was developed for SQL
7.
> And you can create a type of maintanance plan that executes a DELETE FROM
> <tablename> WHERE Deleted=1 AND TIMEDIFF(NOW(), Updated) > 1 month (or
> something like this)
> This should not impact performance too bad.. but you are right that moving
> it to another "deleted" table would be better, because I do have to be
> consistent to always check "deleted!=1" (because deleted can be 0 or NULL)
> It's a bit of a mess :)
> But I can do 'deleted' automatically with cascaded updates or something?
> Lisa
>
> "Chris Stransky" <ChrisStransky@.discussions.microsoft.com> wrote in messag
e
> news:821AB7CF-FABC-4C5B-A5E5-C134E9102DCC@.microsoft.com...
>
>|||On Thu, 28 Apr 2005 12:45:12 +0200, Lisa Pearlson wrote:
(snip)
>Questions:
>- myspDeleteOffices needs to delete people. For this I have to list all
>offices first and then delete people that work at that office. Would a
>cursor not be better?
Hi Lisa,
No - cursors are almost never better. In over 99.9% of all casees, they
are actually far worse.
You'll find that the cascaded delete is lots easier to carry out if you
start at the bottom of the hierarchy - delete Contact Info for all
people in an office of the ex-company first, then all people in an
office of the ex-company, then all offices of the ex-company, and save
the deleting of the company to the last.
>- More importantly, deleting people or offices could fail.. in which case
>things from the Stuff table may have already been deleted. To roll these
>changes back, so that either everything gets deleted (all steps succeed) or
>everything gets restored, what should I do? Use transactions?
Yep.
>Something like this?
>CREATE PROCEDURE myspDeleteCompany ( @.Id INT )
>{
> BEGIN TRANSACTION
> -- delete other stuff first belonging to the company
> DELETE FROM Stuff WHERE ParentId = @.Id
> -- delete offices first
> EXEC myspDeleteOffices( @.Id );
> -- delete self
> DELETE FROM Companies WHERE Id=@.Id
> COMMIT TRANSACTION
>}
>If an error occurs, does the transaction get rolled back automatically
>without me calling ROLLBACK TRANSACTION ?
>Usually when an error occurs, the query aborts, so I wouldn't be able to
>call ROLLBACK after that.
No, you'll have to add error handling. Without that, the execution would
continue and the transaction commited, even though some of your
statements have failed. That would ruin your consistency!
Check these articles, that describe error handling in detail:
http://www.sommarskog.se/error-handling-I.html
http://www.sommarskog.se/error-handling-II.html
Best, Hugo
--
(Remove _NO_ and _SPAM_ to get my e-mail address)
No comments:
Post a Comment