| Author |
Topic |
|
X-Factor
Constraint Violating Yak Guru
392 Posts |
Posted - 2005-02-17 : 20:06:29
|
Take the following..use tempdbcreate table pages( pageID int identity, ordinal int)insert pages ( ordinal ) values ( 1 )insert pages ( ordinal ) values ( 2 )declare @Ordinal intselect @Ordinal = max(Ordinal) + 1 from Pagesinsert pages ( ordinal ) values ( @Ordinal )select * from pages order by ordinal The value of the ordinal is used to order the pages and the pages have to have a unique ordinal value else some code which is used to change the ordinal will not work.So I'm using the 'max' function to work out what the next value will be for a new page's position and, as you can see if you run the code, it works.However, what if a concurrent transaction, B, is running at the same time as transacion, A, and uses max just after A has but before A has done its insert? In this situation, A and B will use the same ordinal value for the insert.So the question is, how can I lock the max ordinal value after its been read until the end of a transaction?Cheers,XF |
|
|
TG
Master Smack Fu Yak Hacker
6065 Posts |
Posted - 2005-02-17 : 20:29:11
|
One way to deal with that situation is to have a table whose job it is is just to generate the next ID value.ie:--existing table:create table OrdinalFactory (Ordinal int identity(1,1) ,active bit)--in your insert SP:declare @Ordinal intinsert OrdinalFactory (active) values(1)Select @Ordinal = scope_identity()insert pages ( ordinal ) values ( @Ordinal ) Be One with the OptimizerTG |
 |
|
|
ehorn
Master Smack Fu Yak Hacker
1632 Posts |
Posted - 2005-02-17 : 20:31:43
|
| OrdinalFactory... Nice! |
 |
|
|
robvolk
Most Valuable Yak
15732 Posts |
Posted - 2005-02-17 : 20:33:59
|
| Why not put the identity on the ordinal column instead? |
 |
|
|
X-Factor
Constraint Violating Yak Guru
392 Posts |
Posted - 2005-02-17 : 20:58:32
|
| Because the ordinal needs to be updatable. The pages need to be re-orderable. |
 |
|
|
X-Factor
Constraint Violating Yak Guru
392 Posts |
Posted - 2005-02-17 : 21:03:57
|
| When you use GROUP, do the rows used to create the group rows have a shared lock put on them? They are being read right? So what if I put an exclusive lock on them so that another transaction couldn't even read them? Then the max couldn't be run until the first tran's insert had been commited?? |
 |
|
|
X-Factor
Constraint Violating Yak Guru
392 Posts |
Posted - 2005-02-17 : 21:30:00
|
| I suppose that's not that different from a table lock. |
 |
|
|
TG
Master Smack Fu Yak Hacker
6065 Posts |
Posted - 2005-02-17 : 21:41:49
|
| X-Factor, are you saying that after process A has completed it's read but before the insert is performed, that Process B still can't read from the table because process A hasn't committed it's transaction? I assume that your table's order has nothing to do with the sequence the records were added. So after the table has been "reordered" by updating the ordinal column, and new records are added with incrementing ordinal values, aren't those new records out of order (until the next re-ordering)?Be One with the OptimizerTG |
 |
|
|
spirit1
Cybernetic Yak Master
11752 Posts |
Posted - 2005-02-18 : 05:57:25
|
well those kind of things usually get solved preety nicely with an extra status column. so until the transaction is finished put the status to say 2 and when it's finished put it to 3 (or whatever you like).Go with the flow & have fun! Else fight the flow |
 |
|
|
X-Factor
Constraint Violating Yak Guru
392 Posts |
Posted - 2005-02-18 : 11:13:27
|
quote: So after the table has been "reordered" by updating the ordinal column, and new records are added with incrementing ordinal values, aren't those new records out of order (until the next re-ordering)?
Errr yeah! A new page would have a default position at the end. |
 |
|
|
Michael Valentine Jones
Yak DBA Kernel (pronounced Colonel)
7020 Posts |
Posted - 2005-02-19 : 20:24:29
|
You could do it this way:declare @Ordinal intselect @Ordinal = max(Ordinal) from Pagesinsert pages ( ordinal )select max(Ordinal)+1from Pageshaving max(Ordinal) = @Ordinal Of course, you will have to check to make sure it didn't fail, and decide what to do if it does.quote: Originally posted by X-Factor Take the following..use tempdbcreate table pages( pageID int identity, ordinal int)insert pages ( ordinal ) values ( 1 )insert pages ( ordinal ) values ( 2 )declare @Ordinal intselect @Ordinal = max(Ordinal) + 1 from Pagesinsert pages ( ordinal ) values ( @Ordinal )select * from pages order by ordinal The value of the ordinal is used to order the pages and the pages have to have a unique ordinal value else some code which is used to change the ordinal will not work.So I'm using the 'max' function to work out what the next value will be for a new page's position and, as you can see if you run the code, it works.However, what if a concurrent transaction, B, is running at the same time as transacion, A, and uses max just after A has but before A has done its insert? In this situation, A and B will use the same ordinal value for the insert.So the question is, how can I lock the max ordinal value after its been read until the end of a transaction?Cheers,XF
Codo Ergo Sum |
 |
|
|
TG
Master Smack Fu Yak Hacker
6065 Posts |
Posted - 2005-02-19 : 20:54:58
|
| Ok, how about this. Since the ordinal column is just used for ordering the display, do away with Unique constraints and contention issues all together. Just allow new records to be instered with a NULL ordinal value. When you select out of that table, order by isNull(Ordinal,100000000). Since the newly inserted records would just be ordered randomly anyway it shouldn't matter.Be One with the OptimizerTG |
 |
|
|
X-Factor
Constraint Violating Yak Guru
392 Posts |
Posted - 2005-02-20 : 16:20:40
|
quote: well those kind of things usually get solved preety nicely with an extra status column. so until the transaction is finished put the status to say 2 and when it's finished put it to 3 (or whatever you like).
Can you explain a bit more about what you mean? quote: Ok, how about this. Since the ordinal column is just used for ordering the display, do away with Unique constraints and contention issues all together. Just allow new records to be instered with a NULL ordinal value. When you select out of that table, order by isNull(Ordinal,100000000). Since the newly inserted records would just be ordered randomly anyway it shouldn't matter.
When would the page be given a non-null ordinal value?The user doesn't manualy enter an ordinal value at any point. What happens is that they're given a list of pages and each page has an up and down arrow next to it. Using these buttons, the user can move shift a page around. So if page x is moved above page y, page x's ordinal value is swapped with page y's ordinal value. So if page x has a null value, upon the swap, it needs to be given a proper value and I think that leads back to the original problem. |
 |
|
|
X-Factor
Constraint Violating Yak Guru
392 Posts |
Posted - 2005-02-20 : 16:25:13
|
quote: You could do it this way:declare @Ordinal intselect @Ordinal = max(Ordinal) from Pagesinsert pages ( ordinal )select max(Ordinal)+1from Pageshaving max(Ordinal) = @Ordinal
Or I could just put a real unique constraint on the ordinal column and handle a clash. I'd handle it by having it try the insert up to a 1000 times and then throw an exception if it still hadn't worked.I have not put a unique constraint on yet because when you swap values, the first update would result in the page x's and page y's ordinal value being the same. But I figure I could get around this by temporarily negating the value being swapped for one of the pages. |
 |
|
|
Michael Valentine Jones
Yak DBA Kernel (pronounced Colonel)
7020 Posts |
Posted - 2005-02-21 : 14:17:53
|
I don't think that would work because a constraint violation is a fatal error that would terminate the stored procedure. You would never get to your error handler code.quote: Or I could just put a real unique constraint on the ordinal column and handle a clash. I'd handle it by having it try the insert up to a 1000 times and then throw an exception if it still hadn't worked.I have not put a unique constraint on yet because when you swap values, the first update would result in the page x's and page y's ordinal value being the same. But I figure I could get around this by temporarily negating the value being swapped for one of the pages.
Codo Ergo Sum |
 |
|
|
X-Factor
Constraint Violating Yak Guru
392 Posts |
Posted - 2005-02-21 : 14:46:22
|
| Ok. Then I'd try calling the sp again from the app. |
 |
|
|
Michael Valentine Jones
Yak DBA Kernel (pronounced Colonel)
7020 Posts |
Posted - 2005-02-21 : 16:05:39
|
You can do that, but it is a lot of work for the from end to deal with.You could use the code I supplied, and just check the value of @@rowcout after the insert to see if it was <> 1. If it isn't, just loopback and try again. Limit it to a reasonable number of retrys, say 10 or less. That would mean you would only need one call to the DB from the front end.quote: Originally posted by X-Factor Ok. Then I'd try calling the sp again from the app.
Codo Ergo Sum |
 |
|
|
X-Factor
Constraint Violating Yak Guru
392 Posts |
Posted - 2005-02-21 : 16:31:01
|
| Yes ok. Thanks for your help. |
 |
|
|
|