Обсуждение: interesting sequence

Поиск
Список
Период
Сортировка

interesting sequence

От
John Fabiani
Дата:
Hi,

I have a special need to create a sequence like function.

"O-20110704 -2"  which is 
"O" for order (there are other types)
"20110704" is for July 4, 2011
'2' the second order of the day for July 4, 2011

I of course can get the type and date.  What I don't know is how to get is the 
last number.  It would seem to be that I would need a loop to determine if the 
next number existed.

LOOP
--Check to see if the string exist in a table
-- count = count +1
-- until I don't find the string
END LOOP;

but then I thought I could do something like

for $1 in  (select string from sometable)LOOPcount = count + 1

or something like this

for i in 1..999 LOOP-- check for the existence of the string in a table using 'i'
-- there will never be 999 orders in one day.
END LOOP    


So here is the question what would be the best way for a multi-user system?
If someone has a better thought - it would be helpful.

BTW I did NOT design the number - in fact it seems silly to me.

Johnf




Re: interesting sequence

От
Samuel Gendler
Дата:


On Tue, Jul 5, 2011 at 6:52 AM, John Fabiani <johnf@jfcomputer.com> wrote:
Hi,

I have a special need to create a sequence like function.

"O-20110704 -2"  which is
"O" for order (there are other types)
"20110704" is for July 4, 2011
'2' the second order of the day for July 4, 2011

I of course can get the type and date.  What I don't know is how to get is the
last number.  It would seem to be that I would need a loop to determine if the
next number existed.

LOOP
--Check to see if the string exist in a table
-- count = count +1
-- until I don't find the string
END LOOP;

but then I thought I could do something like

for $1 in  (select string from sometable)
 LOOP
 count = count + 1

or something like this

for i in 1..999 LOOP
 -- check for the existence of the string in a table using 'i'
-- there will never be 999 orders in one day.
END LOOP


So here is the question what would be the best way for a multi-user system?
If someone has a better thought - it would be helpful.

BTW I did NOT design the number - in fact it seems silly to me.

I'd probably do the following.  Create a table to hold the current date as a string appropriate for use in ids.  I'd also create a sequence for each of the id types. I'd set up a cron job (or equivalent) to run at midnight which updates the date and resets all of the sequences to 1 within a transaction.  You can probably do all of it in a single query.

Then I'd do inserts which generate the id by concatenating the type initial with the date and a sequence, probably in an insert trigger on the table if you are ok with server generated ids. Otherwise, you could do insert with a subquery which generates the id:

insert into order (id, x, y) values ((select 'O-' || d.datestring || '-' || nextval('order_id_sequence') from date_table d), 'x_value', 'y_value');

If you are using hibernate or some other ORM, you can surely use an insert trigger to generate the id and tell the ORM to use a server generated id.



Re: interesting sequence

От
Kevin Crain
Дата:
You don't need a loop there.  Assuming your order id field is of type
varchar you can just build the first part of your string and then do a
count to get the last part using a LIKE comparison:

select count(id_order) + 1  from sometable WHERE id_order LIKE 'O-20110704 -%';

If you do this inside a function it will be like running it in a
transaction so you shouldn't have to worry about it being a multi-user
system.



On Tue, Jul 5, 2011 at 12:38 PM, Samuel Gendler
<sgendler@ideasculptor.com> wrote:
>
>
> On Tue, Jul 5, 2011 at 6:52 AM, John Fabiani <johnf@jfcomputer.com> wrote:
>>
>> Hi,
>>
>> I have a special need to create a sequence like function.
>>
>> "O-20110704 -2"  which is
>> "O" for order (there are other types)
>> "20110704" is for July 4, 2011
>> '2' the second order of the day for July 4, 2011
>>
>> I of course can get the type and date.  What I don't know is how to get is
>> the
>> last number.  It would seem to be that I would need a loop to determine if
>> the
>> next number existed.
>>
>> LOOP
>> --Check to see if the string exist in a table
>> -- count = count +1
>> -- until I don't find the string
>> END LOOP;
>>
>> but then I thought I could do something like
>>
>> for $1 in  (select string from sometable)
>>  LOOP
>>  count = count + 1
>>
>> or something like this
>>
>> for i in 1..999 LOOP
>>  -- check for the existence of the string in a table using 'i'
>> -- there will never be 999 orders in one day.
>> END LOOP
>>
>>
>> So here is the question what would be the best way for a multi-user
>> system?
>> If someone has a better thought - it would be helpful.
>>
>> BTW I did NOT design the number - in fact it seems silly to me.
>
> I'd probably do the following.  Create a table to hold the current date as a
> string appropriate for use in ids.  I'd also create a sequence for each of
> the id types. I'd set up a cron job (or equivalent) to run at midnight which
> updates the date and resets all of the sequences to 1 within a transaction.
>  You can probably do all of it in a single query.
> Then I'd do inserts which generate the id by concatenating the type initial
> with the date and a sequence, probably in an insert trigger on the table if
> you are ok with server generated ids. Otherwise, you could do insert with a
> subquery which generates the id:
> insert into order (id, x, y) values ((select 'O-' || d.datestring || '-' ||
> nextval('order_id_sequence') from date_table d), 'x_value', 'y_value');
> If you are using hibernate or some other ORM, you can surely use an insert
> trigger to generate the id and tell the ORM to use a server generated id.
> sequence documentation is here:
> http://www.postgresql.org/docs/8.1/static/functions-sequence.html
>
>


Re: interesting sequence

От
Kevin Crain
Дата:
My previous reply was intended for John.

On Tue, Jul 5, 2011 at 1:11 PM, Kevin Crain <kevin.crain1@gmail.com> wrote:
> You don't need a loop there.  Assuming your order id field is of type
> varchar you can just build the first part of your string and then do a
> count to get the last part using a LIKE comparison:
>
> select count(id_order) + 1  from sometable WHERE id_order LIKE 'O-20110704 -%';
>
> If you do this inside a function it will be like running it in a
> transaction so you shouldn't have to worry about it being a multi-user
> system.
>
>
>
> On Tue, Jul 5, 2011 at 12:38 PM, Samuel Gendler
> <sgendler@ideasculptor.com> wrote:
>>
>>
>> On Tue, Jul 5, 2011 at 6:52 AM, John Fabiani <johnf@jfcomputer.com> wrote:
>>>
>>> Hi,
>>>
>>> I have a special need to create a sequence like function.
>>>
>>> "O-20110704 -2"  which is
>>> "O" for order (there are other types)
>>> "20110704" is for July 4, 2011
>>> '2' the second order of the day for July 4, 2011
>>>
>>> I of course can get the type and date.  What I don't know is how to get is
>>> the
>>> last number.  It would seem to be that I would need a loop to determine if
>>> the
>>> next number existed.
>>>
>>> LOOP
>>> --Check to see if the string exist in a table
>>> -- count = count +1
>>> -- until I don't find the string
>>> END LOOP;
>>>
>>> but then I thought I could do something like
>>>
>>> for $1 in  (select string from sometable)
>>>  LOOP
>>>  count = count + 1
>>>
>>> or something like this
>>>
>>> for i in 1..999 LOOP
>>>  -- check for the existence of the string in a table using 'i'
>>> -- there will never be 999 orders in one day.
>>> END LOOP
>>>
>>>
>>> So here is the question what would be the best way for a multi-user
>>> system?
>>> If someone has a better thought - it would be helpful.
>>>
>>> BTW I did NOT design the number - in fact it seems silly to me.
>>
>> I'd probably do the following.  Create a table to hold the current date as a
>> string appropriate for use in ids.  I'd also create a sequence for each of
>> the id types. I'd set up a cron job (or equivalent) to run at midnight which
>> updates the date and resets all of the sequences to 1 within a transaction.
>>  You can probably do all of it in a single query.
>> Then I'd do inserts which generate the id by concatenating the type initial
>> with the date and a sequence, probably in an insert trigger on the table if
>> you are ok with server generated ids. Otherwise, you could do insert with a
>> subquery which generates the id:
>> insert into order (id, x, y) values ((select 'O-' || d.datestring || '-' ||
>> nextval('order_id_sequence') from date_table d), 'x_value', 'y_value');
>> If you are using hibernate or some other ORM, you can surely use an insert
>> trigger to generate the id and tell the ORM to use a server generated id.
>> sequence documentation is here:
>> http://www.postgresql.org/docs/8.1/static/functions-sequence.html
>>
>>
>


Re: interesting sequence

От
John Fabiani
Дата:
On Tuesday, July 05, 2011 01:11:11 pm Kevin Crain wrote:
> You don't need a loop there.  Assuming your order id field is of type
> varchar you can just build the first part of your string and then do a
> count to get the last part using a LIKE comparison:
> 
> select count(id_order) + 1  from sometable WHERE id_order LIKE 'O-20110704
> -%';
> 
> If you do this inside a function it will be like running it in a
> transaction so you shouldn't have to worry about it being a multi-user
> system.
> 
> 
> 

I like this - looks better than what I'm currently doing.  Thanks
Johnf


Re: interesting sequence

От
Samuel Gendler
Дата:


On Tue, Jul 5, 2011 at 2:42 PM, John Fabiani <johnf@jfcomputer.com> wrote:
On Tuesday, July 05, 2011 01:11:11 pm Kevin Crain wrote:
> You don't need a loop there.  Assuming your order id field is of type
> varchar you can just build the first part of your string and then do a
> count to get the last part using a LIKE comparison:
>
> select count(id_order) + 1  from sometable WHERE id_order LIKE 'O-20110704
> -%';
>
> If you do this inside a function it will be like running it in a
> transaction so you shouldn't have to worry about it being a multi-user
> system.
>
>
>

I like this - looks better than what I'm currently doing.  Thanks
Johnf


It is simpler, but it will result in id collision if two inserts runs at the same time, particularly if the count query takes a while to run, so be prepared to handle that.  Make sure you have an index which can satisfy that count query quickly.  If you are not using the C locale for your database, that means you must create an index on that column that uses text_pattern_ops or varchar_pattern_ops (depending on if it is text or varchar column) so that postgresql can use the index for that comparison, otherwise LIKE clauses will force a sequential scan of the whole table every time.  C locale does byte by byte text comparison, so the special index isn't required.



Re: interesting sequence

От
Kevin Crain
Дата:
That's why you need to do this inside a function.  Basically just make
an insert function for the table and have it calculate the count and
do the insert in one transaction.

On Tue, Jul 5, 2011 at 5:41 PM, Samuel Gendler
<sgendler@ideasculptor.com> wrote:
>
>
> On Tue, Jul 5, 2011 at 2:42 PM, John Fabiani <johnf@jfcomputer.com> wrote:
>>
>> On Tuesday, July 05, 2011 01:11:11 pm Kevin Crain wrote:
>> > You don't need a loop there.  Assuming your order id field is of type
>> > varchar you can just build the first part of your string and then do a
>> > count to get the last part using a LIKE comparison:
>> >
>> > select count(id_order) + 1  from sometable WHERE id_order LIKE
>> > 'O-20110704
>> > -%';
>> >
>> > If you do this inside a function it will be like running it in a
>> > transaction so you shouldn't have to worry about it being a multi-user
>> > system.
>> >
>> >
>> >
>>
>> I like this - looks better than what I'm currently doing.  Thanks
>> Johnf
>>
>
> It is simpler, but it will result in id collision if two inserts runs at the
> same time, particularly if the count query takes a while to run, so be
> prepared to handle that.  Make sure you have an index which can satisfy that
> count query quickly.  If you are not using the C locale for your database,
> that means you must create an index on that column that uses
> text_pattern_ops or varchar_pattern_ops (depending on if it is text or
> varchar column) so that postgresql can use the index for that comparison,
> otherwise LIKE clauses will force a sequential scan of the whole table every
> time.  C locale does byte by byte text comparison, so the special index
> isn't required.
> http://www.postgresql.org/docs/9.0/interactive/indexes-opclass.html
>
>


Re: interesting sequence

От
Jasen Betts
Дата:
On 2011-07-06, Kevin Crain <kevin.crain1@gmail.com> wrote:
> That's why you need to do this inside a function.  Basically just make
> an insert function for the table and have it calculate the count and
> do the insert in one transaction.

you will still get duplicates, so include code in the function to
retry if there is an error.

-- 
⚂⚃ 100% natural



Re: interesting sequence

От
Gavin Flower
Дата:
On 06/07/11 01:52, John Fabiani wrote: <blockquote cite="mid:201107050652.22187.johnf@jfcomputer.com" type="cite"><pre
wrap="">Hi,

I have a special need to create a sequence like function.

"O-20110704 -2"  which is 
"O" for order (there are other types)
"20110704" is for July 4, 2011
'2' the second order of the day for July 4, 2011

I of course can get the type and date.  What I don't know is how to get is the 
last number.  It would seem to be that I would need a loop to determine if the 
next number existed.

LOOP
--Check to see if the string exist in a table
-- count = count +1
-- until I don't find the string
END LOOP;

but then I thought I could do something like

for $1 in  (select string from sometable)LOOPcount = count + 1

or something like this

for i in 1..999 LOOP-- check for the existence of the string in a table using 'i'
-- there will never be 999 orders in one day.
END LOOP    


So here is the question what would be the best way for a multi-user system?
If someone has a better thought - it would be helpful.

BTW I did NOT design the number - in fact it seems silly to me.

Johnf

</pre></blockquote> Hi John,<br /><br /> How about using a table to hold the latest sequence for each order type and
date,along with a function to insert a new order?<br /><br /> (I've included the code to test the idea and the results,
Iam using 9.1beta2, but it should not make any difference - I think!):<br /><br /><br /><font face="Courier New,
Courier,monospace">DROP TABLE IF EXISTS my_order;<br /> DROP TABLE IF EXISTS order_sequence;<br /><br /><br /> CREATE
TABLEmy_order<br /> (<br />     order_num   text PRIMARY KEY,<br />     payload     text<br /> );<br /><br /><br />
CREATETABLE order_sequence<br /> (<br />     type    int,<br />     day     date,<br />     seq     int     NOT
NULL,<br/>     PRIMARY KEY (type, day)<br /> );<br /><br /><br /> CREATE OR REPLACE FUNCTION create_my_order<br /> (<br
/>    IN  type    int,<br />     IN  day     date,<br />     IN  payload text<br /> ) RETURNS VOID<br /> AS<br /> $$<br
/>    DECLARE<br />         v_order_num text;<br />         v_seq_old   int;<br />         v_seq_new   int;<br />    
BEGIN<br/>         SELECT <br />             os.seq<br />         FROM <br />             order_sequence os <br />
       WHERE<br />             os.type = create_my_order.type AND<br />             os.day = create_my_order.day<br />
       INTO<br />              v_seq_old;<br />             <br />         IF  v_seq_old IS NULL THEN<br />            
v_seq_new:= 1;<br />             INSERT INTO order_sequence(type, day, seq)<br />             VALUES (type, day,
v_seq_new);           <br />         ELSE<br />             v_seq_new := v_seq_old + 1;<br />             UPDATE <br />
               order_sequence AS os<br />             SET<br />                 seq = v_seq_new<br />            
WHERE<br/>                 os.type = create_my_order.type AND<br />                 os.day = create_my_order.day;<br />
       END IF;<br />         <br />         v_order_num := type::text ||<br />                        '-' ||<br />
                      to_char(day, 'YYMMDD') || <br />                        '-' ||<br />                       
v_seq_new::text;<br/>             <br />         INSERT INTO my_order(order_num, payload)<br />         VALUES
(v_order_num,payload);<br />     END;<br /> $$ LANGUAGE plpgsql<br /> VOLATILE<br /> ;<br /><br /><br /> SELECT
create_my_order(0, '2010-03-24', 'order #1 details');<br /> SELECT create_my_order (0, '2010-03-24', 'order #2
details');<br/> SELECT create_my_order (0, '2010-06-15', 'order #3 details');<br /> SELECT create_my_order (5,
'2010-03-24','order #4 details');<br /> SELECT create_my_order (0, '2010-06-15', 'order #5 details');<br /> SELECT
create_my_order(3, '2010-06-14', 'order #6 details');<br /><br /> TABLE order_sequence;<br /> TABLE my_order;<br /><br
/><br/> ////////// This outputs the following:<br /><br />  type |    day     | seq <br /> ------+------------+-----<br
/>    0 | 2010-03-24 |   2<br />     5 | 2010-03-24 |   1<br />     0 | 2010-06-15 |   2<br />     3 | 2010-06-14 |  
1<br/> (4 rows)<br /><br />  order_num  |     payload      <br /> ------------+------------------<br />  0-100324-1 |
order#1 details<br />  0-100324-2 | order #2 details<br />  0-100615-1 | order #3 details<br />  5-100324-1 | order #4
details<br/>  0-100615-2 | order #5 details<br />  3-100614-1 | order #6 details<br /> (6 rows)</font><br /><br /> 

Re: interesting sequence (Correctin)

От
Gavin Flower
Дата:
On 06/07/11 21:47, Gavin Flower wrote:<br /><br /> I forgot the format required of the order number, so to get the
fullyesr, I should have used:<br /><font face="Courier New, Courier, monospace">to_char(day, 'YYYYMMDD')</font><br
/><br/> [...] <blockquote cite="mid:4E142F1A.8060900@archidevsys.co.nz" type="cite"><font face="Courier New, Courier,
monospace">       v_order_num := type::text ||<br />                        '-' ||<br />                       
to_char(day,'YYMMDD') || <br />                        '-' ||<br />                       
v_seq_new::text;</font></blockquote>[...]<br /><br /> Cheers,<br /> Gavin  

Re: interesting sequence

От
Kevin Crain
Дата:
IF this field is unique you shouldn't get duplicates from a function;
the transaction will either succeed or fail; the beauty of a function
is that you can return an error message.  I personally prefer to
handle errors at the application level, but if you have admins running
ad-hoc queries on the database level doing inserts then you definitely
do need to handle that properly.  Triggers are handy in that regard.
You can do loops to check for collisions...I haven't done that so
don't know the best way to code that though.

On Wed, Jul 6, 2011 at 5:28 AM, Jasen Betts <jasen@xnet.co.nz> wrote:
> On 2011-07-06, Kevin Crain <kevin.crain1@gmail.com> wrote:
>> That's why you need to do this inside a function.  Basically just make
>> an insert function for the table and have it calculate the count and
>> do the insert in one transaction.
>
> you will still get duplicates, so include code in the function to
> retry if there is an error.
>
> --
> ⚂⚃ 100% natural
>
>
> --
> Sent via pgsql-sql mailing list (pgsql-sql@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-sql
>