If this is your first visit, be sure to check out the FAQ by clicking the link above. You may have to register before you can post: click the register link above to proceed. To start viewing messages, select the forum that you want to visit from the selection below. |
|
|
Thread Tools | Display Modes |
#1
|
|||
|
|||
General solution for missing sequence numbers
I occasionally need to determine a number that I don't have in a sequence,
either the first missing one in a gap in a set of sequential numbers or the next one in line at the end of a numbered series. Always it meant some fumbling around, with either VBA at first or later with SQL when I got good enough at it, establishing the proper join parameters and such. For SQL experts, this is probably routine and trivial, but for me it was always a bit of a chore. The last straw came with a database which I recently wrote, where the converted data had such a numbered series, and the owner wanted to be able to do both, fill in missing numbers in the gaps AND add new numbers at the end. Walking home from a bar last night, I got to thinking about it and realized that both problems are actually fairly similar and that a simple and general solution is possible. I put together a simple table containing one field with the following entries: 1,2,3,4, 8,9,10, 15,16,17,18, 20, 22,23,24,25, 28,29,30 Missing a 5,6,7, 11,12,13,14, 19, 21, 26,27 and 31 on up. This is the dataset used for all of the following examples. Finding the next new number at the end of a series with SQL is trivial; here is a simplified version of a statement that I found somewhere in the discussion groups a few years ago: SELECT Max(MyTable.MySeqFld)+1 FROM MyTable; This will return a one-record, one-field recordset containing exactly one value: 31, which is one greater than the largest value so far used in that field. This is what you would want to use instead of Access's autonumber, if the field is to contain meaningful sequence numbering, rather than just a unique identifier. Locating gaps is a little more complicated: it involves a self-join from N to N+1 and finding where N+1 doesn't exist, indicating a gap at that point. SELECT MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL; This generates a recordset of 5, 11, 19, 21, 26, 31, where each value is the first missing value in a gap, including the "open gap" at the end, and that's where the trick to a general solution begins. Since these situations normally call for either the first (lowest number) gap or last (end of recordset) gap, you need either the first or last record returned by this query. Sorting and using the TOP predicate gives you exactly that. SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY MT1.MySeqFld; This will again return a one-record, one-field recordset containing exactly one value: 5, the first missing number in the first gap in the sequence. Ascending sort order is the default, so the smallest number is the first returned. SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY MT1.MySeqFld DESC; This will return a one-record, one-field recordset containing exactly one value: 31, the same "one greater than the highest value so far used in that field" that is returned by the first simple example. Specifying the descending order here is necessary, since we want the last (greatest) record from the set and Access SQL does not have a BOTTOM predicate. Finally, an even more general statement can be used: SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY ((INSTR("LF",[First or Last (F or L)]) *2)-3)*MT1.MySeqFld; This expects one parameter, F or L and will return either the first missing number or the next number at the end of the line. The INSTR expression evaluates to either -1 or 1 (or -3 if the parameter supplied is neither F nor L, but that has the same effect as -1 in this instance), that is then used as a multiplier for the sort field, so the sort is either by the field or by the negative of the field (or 3 times the negative of the field), giving either ascending or descending order and with the TOP 1 predicate again returns exactly the one value of interest. -- Pete This e-mail address is fake to keep spammers and their auto-harvesters out of my hair. If you need to get in touch personally, I am 'pdanes' and I use Yahoo mail. But please use the newsgroups whenever possible, so that all may benefit from the exchange of ideas. |
#2
|
|||
|
|||
General solution for missing sequence numbers
"Peter Danes" wrote in message ... snip SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY MT1.MySeqFld DESC; This will return a one-record, one-field recordset containing exactly one value: 31, the same "one greater than the highest value so far used in that field" that is returned by the first simple example. Specifying the descending order here is necessary, since we want the last (greatest) record from the set and Access SQL does not have a BOTTOM predicate. Peter Danes, I am not sure what the difference is between the above and the below. SELECT MAX(MT1.MySeqFld) + 1 FROM MyTable AS MT1; Sincerely, Chris O. |
#3
|
|||
|
|||
General solution for missing sequence numbers
You might be interested in the analysis I had in my April, 2004 "Access
Answers" column in Pinnacle Publication's "Smart Access". You can download the column (and sample database) for free from http://www.accessmvp.com/djsteele/SmartAccess.html -- Doug Steele, Microsoft Access MVP http://I.Am/DougSteele (no e-mails, please!) "Peter Danes" wrote in message ... I occasionally need to determine a number that I don't have in a sequence, either the first missing one in a gap in a set of sequential numbers or the next one in line at the end of a numbered series. Always it meant some fumbling around, with either VBA at first or later with SQL when I got good enough at it, establishing the proper join parameters and such. For SQL experts, this is probably routine and trivial, but for me it was always a bit of a chore. The last straw came with a database which I recently wrote, where the converted data had such a numbered series, and the owner wanted to be able to do both, fill in missing numbers in the gaps AND add new numbers at the end. Walking home from a bar last night, I got to thinking about it and realized that both problems are actually fairly similar and that a simple and general solution is possible. I put together a simple table containing one field with the following entries: 1,2,3,4, 8,9,10, 15,16,17,18, 20, 22,23,24,25, 28,29,30 Missing a 5,6,7, 11,12,13,14, 19, 21, 26,27 and 31 on up. This is the dataset used for all of the following examples. Finding the next new number at the end of a series with SQL is trivial; here is a simplified version of a statement that I found somewhere in the discussion groups a few years ago: SELECT Max(MyTable.MySeqFld)+1 FROM MyTable; This will return a one-record, one-field recordset containing exactly one value: 31, which is one greater than the largest value so far used in that field. This is what you would want to use instead of Access's autonumber, if the field is to contain meaningful sequence numbering, rather than just a unique identifier. Locating gaps is a little more complicated: it involves a self-join from N to N+1 and finding where N+1 doesn't exist, indicating a gap at that point. SELECT MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL; This generates a recordset of 5, 11, 19, 21, 26, 31, where each value is the first missing value in a gap, including the "open gap" at the end, and that's where the trick to a general solution begins. Since these situations normally call for either the first (lowest number) gap or last (end of recordset) gap, you need either the first or last record returned by this query. Sorting and using the TOP predicate gives you exactly that. SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY MT1.MySeqFld; This will again return a one-record, one-field recordset containing exactly one value: 5, the first missing number in the first gap in the sequence. Ascending sort order is the default, so the smallest number is the first returned. SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY MT1.MySeqFld DESC; This will return a one-record, one-field recordset containing exactly one value: 31, the same "one greater than the highest value so far used in that field" that is returned by the first simple example. Specifying the descending order here is necessary, since we want the last (greatest) record from the set and Access SQL does not have a BOTTOM predicate. Finally, an even more general statement can be used: SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY ((INSTR("LF",[First or Last (F or L)]) *2)-3)*MT1.MySeqFld; This expects one parameter, F or L and will return either the first missing number or the next number at the end of the line. The INSTR expression evaluates to either -1 or 1 (or -3 if the parameter supplied is neither F nor L, but that has the same effect as -1 in this instance), that is then used as a multiplier for the sort field, so the sort is either by the field or by the negative of the field (or 3 times the negative of the field), giving either ascending or descending order and with the TOP 1 predicate again returns exactly the one value of interest. -- Pete This e-mail address is fake to keep spammers and their auto-harvesters out of my hair. If you need to get in touch personally, I am 'pdanes' and I use Yahoo mail. But please use the newsgroups whenever possible, so that all may benefit from the exchange of ideas. |
#4
|
|||
|
|||
General solution for missing sequence numbers
What is the specific *NEED* to find the missing numbers?
Peter Danes wrote: I occasionally need to determine a number that I don't have in a sequence, either the first missing one in a gap in a set of sequential numbers or the next one in line at the end of a numbered series. Always it meant some fumbling around, with either VBA at first or later with SQL when I got good enough at it, establishing the proper join parameters and such. For SQL experts, this is probably routine and trivial, but for me it was always a bit of a chore. The last straw came with a database which I recently wrote, where the converted data had such a numbered series, and the owner wanted to be able to do both, fill in missing numbers in the gaps AND add new numbers at the end. Walking home from a bar last night, I got to thinking about it and realized that both problems are actually fairly similar and that a simple and general solution is possible. I put together a simple table containing one field with the following entries: 1,2,3,4, 8,9,10, 15,16,17,18, 20, 22,23,24,25, 28,29,30 Missing a 5,6,7, 11,12,13,14, 19, 21, 26,27 and 31 on up. This is the dataset used for all of the following examples. Finding the next new number at the end of a series with SQL is trivial; here is a simplified version of a statement that I found somewhere in the discussion groups a few years ago: SELECT Max(MyTable.MySeqFld)+1 FROM MyTable; This will return a one-record, one-field recordset containing exactly one value: 31, which is one greater than the largest value so far used in that field. This is what you would want to use instead of Access's autonumber, if the field is to contain meaningful sequence numbering, rather than just a unique identifier. Locating gaps is a little more complicated: it involves a self-join from N to N+1 and finding where N+1 doesn't exist, indicating a gap at that point. SELECT MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL; This generates a recordset of 5, 11, 19, 21, 26, 31, where each value is the first missing value in a gap, including the "open gap" at the end, and that's where the trick to a general solution begins. Since these situations normally call for either the first (lowest number) gap or last (end of recordset) gap, you need either the first or last record returned by this query. Sorting and using the TOP predicate gives you exactly that. SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY MT1.MySeqFld; This will again return a one-record, one-field recordset containing exactly one value: 5, the first missing number in the first gap in the sequence. Ascending sort order is the default, so the smallest number is the first returned. SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY MT1.MySeqFld DESC; This will return a one-record, one-field recordset containing exactly one value: 31, the same "one greater than the highest value so far used in that field" that is returned by the first simple example. Specifying the descending order here is necessary, since we want the last (greatest) record from the set and Access SQL does not have a BOTTOM predicate. Finally, an even more general statement can be used: SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY ((INSTR("LF",[First or Last (F or L)]) *2)-3)*MT1.MySeqFld; This expects one parameter, F or L and will return either the first missing number or the next number at the end of the line. The INSTR expression evaluates to either -1 or 1 (or -3 if the parameter supplied is neither F nor L, but that has the same effect as -1 in this instance), that is then used as a multiplier for the sort field, so the sort is either by the field or by the negative of the field (or 3 times the negative of the field), giving either ascending or descending order and with the TOP 1 predicate again returns exactly the one value of interest. |
#5
|
|||
|
|||
General solution for missing sequence numbers
There are three differences:
1. Your example is the same as my first example which returns only the "greatest +1", except that you additionally include an alias to the table, the "AS MT1" at the end of the statement. It doesn't hurt anything, but isn't really necessary. 2. Youe example doesn't call for a parameter, mine does, to determine the sort order and so whether you get the first missing number or the next in line greater than all numbers used so far. 3. Obviously, the example you posted is considerably simpler, and if you only need what it returns, simpler is preferable. The point of my 'lecture' was simply that a general solution to these related problems is possible with a single SQL statement. I do not claim that it is preferable in all situations, or even any particular situation. Pete "Chris2" píše v diskusním příspěvku ... "Peter Danes" wrote in message ... snip SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY MT1.MySeqFld DESC; This will return a one-record, one-field recordset containing exactly one value: 31, the same "one greater than the highest value so far used in that field" that is returned by the first simple example. Specifying the descending order here is necessary, since we want the last (greatest) record from the set and Access SQL does not have a BOTTOM predicate. Peter Danes, I am not sure what the difference is between the above and the below. SELECT MAX(MT1.MySeqFld) + 1 FROM MyTable AS MT1; Sincerely, Chris O. |
#6
|
|||
|
|||
General solution for missing sequence numbers
Thank you Doug, interesting article. I like your addition of the range, I
think I'll be able to use that in something I'm working on now. And many of the other titles look intriguing as well - time to do some reading. (BTW, the description for invoice 11 says how about sending me an e-mail, but your signature says no e-mails, please. I'm feeling schizophrenic. Maybe if I write you one but don't send it...?) Pete "Douglas J Steele" píše v diskusním příspěvku ... You might be interested in the analysis I had in my April, 2004 "Access Answers" column in Pinnacle Publication's "Smart Access". You can download the column (and sample database) for free from http://www.accessmvp.com/djsteele/SmartAccess.html -- Doug Steele, Microsoft Access MVP http://I.Am/DougSteele (no e-mails, please!) "Peter Danes" wrote in message ... I occasionally need to determine a number that I don't have in a sequence, either the first missing one in a gap in a set of sequential numbers or the next one in line at the end of a numbered series. Always it meant some fumbling around, with either VBA at first or later with SQL when I got good enough at it, establishing the proper join parameters and such. For SQL experts, this is probably routine and trivial, but for me it was always a bit of a chore. The last straw came with a database which I recently wrote, where the converted data had such a numbered series, and the owner wanted to be able to do both, fill in missing numbers in the gaps AND add new numbers at the end. Walking home from a bar last night, I got to thinking about it and realized that both problems are actually fairly similar and that a simple and general solution is possible. I put together a simple table containing one field with the following entries: 1,2,3,4, 8,9,10, 15,16,17,18, 20, 22,23,24,25, 28,29,30 Missing a 5,6,7, 11,12,13,14, 19, 21, 26,27 and 31 on up. This is the dataset used for all of the following examples. Finding the next new number at the end of a series with SQL is trivial; here is a simplified version of a statement that I found somewhere in the discussion groups a few years ago: SELECT Max(MyTable.MySeqFld)+1 FROM MyTable; This will return a one-record, one-field recordset containing exactly one value: 31, which is one greater than the largest value so far used in that field. This is what you would want to use instead of Access's autonumber, if the field is to contain meaningful sequence numbering, rather than just a unique identifier. Locating gaps is a little more complicated: it involves a self-join from N to N+1 and finding where N+1 doesn't exist, indicating a gap at that point. SELECT MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL; This generates a recordset of 5, 11, 19, 21, 26, 31, where each value is the first missing value in a gap, including the "open gap" at the end, and that's where the trick to a general solution begins. Since these situations normally call for either the first (lowest number) gap or last (end of recordset) gap, you need either the first or last record returned by this query. Sorting and using the TOP predicate gives you exactly that. SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY MT1.MySeqFld; This will again return a one-record, one-field recordset containing exactly one value: 5, the first missing number in the first gap in the sequence. Ascending sort order is the default, so the smallest number is the first returned. SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY MT1.MySeqFld DESC; This will return a one-record, one-field recordset containing exactly one value: 31, the same "one greater than the highest value so far used in that field" that is returned by the first simple example. Specifying the descending order here is necessary, since we want the last (greatest) record from the set and Access SQL does not have a BOTTOM predicate. Finally, an even more general statement can be used: SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY ((INSTR("LF",[First or Last (F or L)]) *2)-3)*MT1.MySeqFld; This expects one parameter, F or L and will return either the first missing number or the next number at the end of the line. The INSTR expression evaluates to either -1 or 1 (or -3 if the parameter supplied is neither F nor L, but that has the same effect as -1 in this instance), that is then used as a multiplier for the sort field, so the sort is either by the field or by the negative of the field (or 3 times the negative of the field), giving either ascending or descending order and with the TOP 1 predicate again returns exactly the one value of interest. -- Pete This e-mail address is fake to keep spammers and their auto-harvesters out of my hair. If you need to get in touch personally, I am 'pdanes' and I use Yahoo mail. But please use the newsgroups whenever possible, so that all may benefit from the exchange of ideas. |
#7
|
|||
|
|||
General solution for missing sequence numbers
Such situations are common, for a variety of reasons. Depends on the
database and the user and what the data is for. The particular example that inspired this outburst is a mycological database, where the numbers are used to sequentially number the scientist's samples. She told me that numbering is important for others in the field to know roughly how many samples a particular researcher has, and for internal inventory purposes, that they don't expect to have holes in the numbering sequence. If someone who has 1,000 samples in their collection publishes something about their sample number 10,000 and it is known that the person does not have anywhere near 10,000 samples, it would be viewed as odd at the very least, possibly unethical and such a person would find himself not taken seriously by other researchers. One or two numbers amiss in this situation is obviously not a major concern. And for the internal inventory controls, if someone sees sample 152 next to 150, they are going to wonder where is number 151. The inventory methods used expect sequential numbering and a missing number is an indication of something wrong. For inventory numbers in the original database, she used the record number that appears in the text box of Access's navigation control in conjunction with an autonumber field. You may guess what sort of hash resulted from that. I started out trying to fix a few things for her and wound up doing almost a complete re-write of the entire thing and this numbering issue is one of the things that surfaced. She wants to be able to fill in all the gaps as well as add new numbers to the end as she collects new samples. Pete "David C. Holley" píse v diskusním príspevku ... What is the specific *NEED* to find the missing numbers? Peter Danes wrote: I occasionally need to determine a number that I don't have in a sequence, either the first missing one in a gap in a set of sequential numbers or the next one in line at the end of a numbered series. Always it meant some fumbling around, with either VBA at first or later with SQL when I got good enough at it, establishing the proper join parameters and such. For SQL experts, this is probably routine and trivial, but for me it was always a bit of a chore. The last straw came with a database which I recently wrote, where the converted data had such a numbered series, and the owner wanted to be able to do both, fill in missing numbers in the gaps AND add new numbers at the end. Walking home from a bar last night, I got to thinking about it and realized that both problems are actually fairly similar and that a simple and general solution is possible. I put together a simple table containing one field with the following entries: 1,2,3,4, 8,9,10, 15,16,17,18, 20, 22,23,24,25, 28,29,30 Missing a 5,6,7, 11,12,13,14, 19, 21, 26,27 and 31 on up. This is the dataset used for all of the following examples. Finding the next new number at the end of a series with SQL is trivial; here is a simplified version of a statement that I found somewhere in the discussion groups a few years ago: SELECT Max(MyTable.MySeqFld)+1 FROM MyTable; This will return a one-record, one-field recordset containing exactly one value: 31, which is one greater than the largest value so far used in that field. This is what you would want to use instead of Access's autonumber, if the field is to contain meaningful sequence numbering, rather than just a unique identifier. Locating gaps is a little more complicated: it involves a self-join from N to N+1 and finding where N+1 doesn't exist, indicating a gap at that point. SELECT MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL; This generates a recordset of 5, 11, 19, 21, 26, 31, where each value is the first missing value in a gap, including the "open gap" at the end, and that's where the trick to a general solution begins. Since these situations normally call for either the first (lowest number) gap or last (end of recordset) gap, you need either the first or last record returned by this query. Sorting and using the TOP predicate gives you exactly that. SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY MT1.MySeqFld; This will again return a one-record, one-field recordset containing exactly one value: 5, the first missing number in the first gap in the sequence. Ascending sort order is the default, so the smallest number is the first returned. SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY MT1.MySeqFld DESC; This will return a one-record, one-field recordset containing exactly one value: 31, the same "one greater than the highest value so far used in that field" that is returned by the first simple example. Specifying the descending order here is necessary, since we want the last (greatest) record from the set and Access SQL does not have a BOTTOM predicate. Finally, an even more general statement can be used: SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY ((INSTR("LF",[First or Last (F or L)]) *2)-3)*MT1.MySeqFld; This expects one parameter, F or L and will return either the first missing number or the next number at the end of the line. The INSTR expression evaluates to either -1 or 1 (or -3 if the parameter supplied is neither F nor L, but that has the same effect as -1 in this instance), that is then used as a multiplier for the sort field, so the sort is either by the field or by the negative of the field (or 3 times the negative of the field), giving either ascending or descending order and with the TOP 1 predicate again returns exactly the one value of interest. |
#8
|
|||
|
|||
General solution for missing sequence numbers
I'm sorry, I don't understand what you mean by "invoice 11"
-- Doug Steele, Microsoft Access MVP http://I.Am/DougSteele (no e-mails, please!) "Peter Danes" wrote in message ... Thank you Doug, interesting article. I like your addition of the range, I think I'll be able to use that in something I'm working on now. And many of the other titles look intriguing as well - time to do some reading. (BTW, the description for invoice 11 says how about sending me an e-mail, but your signature says no e-mails, please. I'm feeling schizophrenic. Maybe if I write you one but don't send it...?) Pete "Douglas J Steele" píše v diskusním příspěvku ... You might be interested in the analysis I had in my April, 2004 "Access Answers" column in Pinnacle Publication's "Smart Access". You can download the column (and sample database) for free from http://www.accessmvp.com/djsteele/SmartAccess.html -- Doug Steele, Microsoft Access MVP http://I.Am/DougSteele (no e-mails, please!) "Peter Danes" wrote in message ... I occasionally need to determine a number that I don't have in a sequence, either the first missing one in a gap in a set of sequential numbers or the next one in line at the end of a numbered series. Always it meant some fumbling around, with either VBA at first or later with SQL when I got good enough at it, establishing the proper join parameters and such. For SQL experts, this is probably routine and trivial, but for me it was always a bit of a chore. The last straw came with a database which I recently wrote, where the converted data had such a numbered series, and the owner wanted to be able to do both, fill in missing numbers in the gaps AND add new numbers at the end. Walking home from a bar last night, I got to thinking about it and realized that both problems are actually fairly similar and that a simple and general solution is possible. I put together a simple table containing one field with the following entries: 1,2,3,4, 8,9,10, 15,16,17,18, 20, 22,23,24,25, 28,29,30 Missing a 5,6,7, 11,12,13,14, 19, 21, 26,27 and 31 on up. This is the dataset used for all of the following examples. Finding the next new number at the end of a series with SQL is trivial; here is a simplified version of a statement that I found somewhere in the discussion groups a few years ago: SELECT Max(MyTable.MySeqFld)+1 FROM MyTable; This will return a one-record, one-field recordset containing exactly one value: 31, which is one greater than the largest value so far used in that field. This is what you would want to use instead of Access's autonumber, if the field is to contain meaningful sequence numbering, rather than just a unique identifier. Locating gaps is a little more complicated: it involves a self-join from N to N+1 and finding where N+1 doesn't exist, indicating a gap at that point. SELECT MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL; This generates a recordset of 5, 11, 19, 21, 26, 31, where each value is the first missing value in a gap, including the "open gap" at the end, and that's where the trick to a general solution begins. Since these situations normally call for either the first (lowest number) gap or last (end of recordset) gap, you need either the first or last record returned by this query. Sorting and using the TOP predicate gives you exactly that. SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY MT1.MySeqFld; This will again return a one-record, one-field recordset containing exactly one value: 5, the first missing number in the first gap in the sequence. Ascending sort order is the default, so the smallest number is the first returned. SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY MT1.MySeqFld DESC; This will return a one-record, one-field recordset containing exactly one value: 31, the same "one greater than the highest value so far used in that field" that is returned by the first simple example. Specifying the descending order here is necessary, since we want the last (greatest) record from the set and Access SQL does not have a BOTTOM predicate. Finally, an even more general statement can be used: SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY ((INSTR("LF",[First or Last (F or L)]) *2)-3)*MT1.MySeqFld; This expects one parameter, F or L and will return either the first missing number or the next number at the end of the line. The INSTR expression evaluates to either -1 or 1 (or -3 if the parameter supplied is neither F nor L, but that has the same effect as -1 in this instance), that is then used as a multiplier for the sort field, so the sort is either by the field or by the negative of the field (or 3 times the negative of the field), giving either ascending or descending order and with the TOP 1 predicate again returns exactly the one value of interest. -- Pete This e-mail address is fake to keep spammers and their auto-harvesters out of my hair. If you need to get in touch personally, I am 'pdanes' and I use Yahoo mail. But please use the newsgroups whenever possible, so that all may benefit from the exchange of ideas. |
#9
|
|||
|
|||
General solution for missing sequence numbers
Have you thought about using a DAO approach where you loop through the
records one by one and compare the current value to the previous? Peter Danes wrote: There are three differences: 1. Your example is the same as my first example which returns only the "greatest +1", except that you additionally include an alias to the table, the "AS MT1" at the end of the statement. It doesn't hurt anything, but isn't really necessary. 2. Youe example doesn't call for a parameter, mine does, to determine the sort order and so whether you get the first missing number or the next in line greater than all numbers used so far. 3. Obviously, the example you posted is considerably simpler, and if you only need what it returns, simpler is preferable. The point of my 'lecture' was simply that a general solution to these related problems is possible with a single SQL statement. I do not claim that it is preferable in all situations, or even any particular situation. Pete "Chris2" píše v diskusním příspěvku ... "Peter Danes" wrote in message .. . snip SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY MT1.MySeqFld DESC; This will return a one-record, one-field recordset containing exactly one value: 31, the same "one greater than the highest value so far used in that field" that is returned by the first simple example. Specifying the descending order here is necessary, since we want the last (greatest) record from the set and Access SQL does not have a BOTTOM predicate. Peter Danes, I am not sure what the difference is between the above and the below. SELECT MAX(MT1.MySeqFld) + 1 FROM MyTable AS MT1; Sincerely, Chris O. |
#10
|
|||
|
|||
General solution for missing sequence numbers
Small joke. (Very small) In the sample database for the article to which you
referred me, you have two tables, PossibleInvoices and Invoices, one with just numbers and one with numbers and a text field. In the text field next to invoice number 11, you had this comment "If so, how about sending me an e-mail?" Sorry for the confusion. Pete "Douglas J Steele" píše v diskusním příspěvku ... I'm sorry, I don't understand what you mean by "invoice 11" -- Doug Steele, Microsoft Access MVP http://I.Am/DougSteele (no e-mails, please!) "Peter Danes" wrote in message ... Thank you Doug, interesting article. I like your addition of the range, I think I'll be able to use that in something I'm working on now. And many of the other titles look intriguing as well - time to do some reading. (BTW, the description for invoice 11 says how about sending me an e-mail, but your signature says no e-mails, please. I'm feeling schizophrenic. Maybe if I write you one but don't send it...?) Pete "Douglas J Steele" píše v diskusním příspěvku ... You might be interested in the analysis I had in my April, 2004 "Access Answers" column in Pinnacle Publication's "Smart Access". You can download the column (and sample database) for free from http://www.accessmvp.com/djsteele/SmartAccess.html -- Doug Steele, Microsoft Access MVP http://I.Am/DougSteele (no e-mails, please!) "Peter Danes" wrote in message ... I occasionally need to determine a number that I don't have in a sequence, either the first missing one in a gap in a set of sequential numbers or the next one in line at the end of a numbered series. Always it meant some fumbling around, with either VBA at first or later with SQL when I got good enough at it, establishing the proper join parameters and such. For SQL experts, this is probably routine and trivial, but for me it was always a bit of a chore. The last straw came with a database which I recently wrote, where the converted data had such a numbered series, and the owner wanted to be able to do both, fill in missing numbers in the gaps AND add new numbers at the end. Walking home from a bar last night, I got to thinking about it and realized that both problems are actually fairly similar and that a simple and general solution is possible. I put together a simple table containing one field with the following entries: 1,2,3,4, 8,9,10, 15,16,17,18, 20, 22,23,24,25, 28,29,30 Missing a 5,6,7, 11,12,13,14, 19, 21, 26,27 and 31 on up. This is the dataset used for all of the following examples. Finding the next new number at the end of a series with SQL is trivial; here is a simplified version of a statement that I found somewhere in the discussion groups a few years ago: SELECT Max(MyTable.MySeqFld)+1 FROM MyTable; This will return a one-record, one-field recordset containing exactly one value: 31, which is one greater than the largest value so far used in that field. This is what you would want to use instead of Access's autonumber, if the field is to contain meaningful sequence numbering, rather than just a unique identifier. Locating gaps is a little more complicated: it involves a self-join from N to N+1 and finding where N+1 doesn't exist, indicating a gap at that point. SELECT MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL; This generates a recordset of 5, 11, 19, 21, 26, 31, where each value is the first missing value in a gap, including the "open gap" at the end, and that's where the trick to a general solution begins. Since these situations normally call for either the first (lowest number) gap or last (end of recordset) gap, you need either the first or last record returned by this query. Sorting and using the TOP predicate gives you exactly that. SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY MT1.MySeqFld; This will again return a one-record, one-field recordset containing exactly one value: 5, the first missing number in the first gap in the sequence. Ascending sort order is the default, so the smallest number is the first returned. SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY MT1.MySeqFld DESC; This will return a one-record, one-field recordset containing exactly one value: 31, the same "one greater than the highest value so far used in that field" that is returned by the first simple example. Specifying the descending order here is necessary, since we want the last (greatest) record from the set and Access SQL does not have a BOTTOM predicate. Finally, an even more general statement can be used: SELECT TOP 1 MT1.MySeqFld+1 FROM MyTable AS MT1 LEFT JOIN MyTable AS MT2 ON MT1.MySeqFld+1=MT2.MySeqFld WHERE MT2.MySeqFld IS NULL ORDER BY ((INSTR("LF",[First or Last (F or L)]) *2)-3)*MT1.MySeqFld; This expects one parameter, F or L and will return either the first missing number or the next number at the end of the line. The INSTR expression evaluates to either -1 or 1 (or -3 if the parameter supplied is neither F nor L, but that has the same effect as -1 in this instance), that is then used as a multiplier for the sort field, so the sort is either by the field or by the negative of the field (or 3 times the negative of the field), giving either ascending or descending order and with the TOP 1 predicate again returns exactly the one value of interest. -- Pete This e-mail address is fake to keep spammers and their auto-harvesters out of my hair. If you need to get in touch personally, I am 'pdanes' and I use Yahoo mail. But please use the newsgroups whenever possible, so that all may benefit from the exchange of ideas. |
Thread Tools | |
Display Modes | |
|
|
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
How to look for a sequence of numbers in a string? | brett | General Discussion | 2 | October 27th, 2005 01:44 AM |
Checking Winning Numbers in the Lottery. | Ann | General Discussion | 4 | May 18th, 2005 10:55 AM |
sequence numbers | su su | General Discussion | 4 | May 12th, 2005 02:51 AM |
Generating excel combinations | mark4006 | General Discussion | 2 | March 6th, 2005 04:40 PM |
Problem Updating New Messages from NTTP News Server OE | Chad Harris | Outlook Express | 19 | February 7th, 2005 07:21 PM |