Thread: List last value of all sequences
I'm interested in producing a list of all sequence names and the corresponding last value. Starting with a list of sequence names generated by SELECT c.relname FROM pg_class c WHERE (c.relkind = 'S'); my initial thought was to extend that with SELECT c.relname, currval(c.relname) FROM pg_class c WHERE (c.relkind = 'S'); but of course that doesn't work since "currval" is not defined until "nextval" has been called at least once, and I don't want to increment the sequences...just query the value. I know that for a given sequence, E.G., "city_city_seq" I can get its value using SELECT last_value FROM city_city_seq; So my next try used a function defined as CREATE FUNCTION public.get_sequence_last_value(name) RETURNS int4 AS ' DECLARE ls_sequence ALIAS FOR $1; lr_record RECORD; li_return INT4; BEGIN FOR lr_record IN EXECUTE ''SELECT last_value FROM '' || ls_sequence LOOP li_return := lr_record.last_value; END LOOP; RETURN li_return; END;' LANGUAGE 'plpgsql' VOLATILE; Followed by SELECT c.relname, get_sequence_last_value(c.relname) FROM pg_class c WHERE (c.relkind = 'S'); Which works and produces the result I want, but that function seems really messy. Is there a cleaner way to do this? ~Berend Tober
On Thursday 24 July 2003 13:46, btober@seaworthysys.com wrote: > I'm interested in producing a list of all sequence names and the > corresponding last value. Starting with a list of sequence names > generated by > > SELECT c.relname FROM pg_class c WHERE (c.relkind = 'S'); [snip] > So my next try used a function defined as > > CREATE FUNCTION public.get_sequence_last_value(name) RETURNS int4 AS ' > DECLARE > ls_sequence ALIAS FOR $1; > lr_record RECORD; > li_return INT4; > BEGIN > FOR lr_record IN EXECUTE ''SELECT last_value FROM '' || ls_sequence LOOP > li_return := lr_record.last_value; > END LOOP; > RETURN li_return; > END;' LANGUAGE 'plpgsql' VOLATILE; > > Followed by > > SELECT c.relname, get_sequence_last_value(c.relname) > FROM pg_class c WHERE (c.relkind = 'S'); > > Which works and produces the result I want, but that function seems > really messy. Is there a cleaner way to do this? Why not just have two (nested) loops in the function? FOR class_rec IN SELECT c.relname FROM pg_class WHERE c.relkind=''S'' LOOP FOR lr_record IN EXECTUTE ... || class_rec.relname... LOOP ... END LOOP END LOOP Or am I missing something? -- Richard Huxton Archonet Ltd
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 This will work in most cases: SELECT c.relname, setval(c.relname, CASE WHEN nextval(c.relname) > 1 THEN currval(c.relname)-1 ELSE 1 END,'true') FROM pg_class c WHERE c.relkind='S'; It works for simple sequences in which the number is incremented by 1 each time it is called. We need the CASE to account for newly created sequences in which the last_value is equal to min_value (1) and the "is_called" flag is set to false. It will still fail on other cases, such as sequences that start with something other than 1, increment other than +1, or are at their max_value. The function you provided should work fine as well, although it should return BIGINT, not int4. If you are doing this check often, you might want to also have a function that returns all the sequences for you when called. - -- Greg Sabino Mullane greg@turnstep.com PGP Key: 0x14964AC8 200307241009 -----BEGIN PGP SIGNATURE----- Comment: http://www.turnstep.com/pgp.html iD8DBQE/H+mHvJuQZxSWSsgRAicMAJ4zqV/UmDlUKdQtI8e3qAorEJeKPACfVs97 vx8Oc9kFaGd8tpd1+yhR7jY= =zJKz -----END PGP SIGNATURE-----
> This will work in most cases: > > SELECT c.relname, > setval(c.relname, CASE WHEN nextval(c.relname) > 1 THEN > currval(c.relname)-1 ELSE 1 END,'true') > FROM pg_class c WHERE c.relkind='S'; > The main problem with this approach is that, while you get the "current value", the sequence is incremented by the call. I just want to (strictly) look at the value. I think I forgot to cc the list in a reply to another respondent in which I explained the further, previously unstated objective of creating an updateble view so that I can conveniently see AND CHANGE the sequence values: CREATE FUNCTION public.set_sequence(name, int4) RETURNS int4 AS ' DECLARE l_sequence_name ALIAS FOR $1; l_last_value ALIAS FOR $2; BEGIN IF l_last_value = 0 THEN PERFORM setval(l_sequence_name,1, False); ELSE PERFORM setval(l_sequence_name,l_last_value); END IF; RETURN 1; END;' LANGUAGE 'plpgsql' VOLATILE; CREATE VIEW public.sequence_values AS SELECT pg_get_userbyid(c.relowner) AS sequenceowner, c.relname AS sequencename, get_sequence_last_value(c.relname) AS last_value FROM pg_class c WHERE (c.relkind = 'S') ORDER BY pg_get_userbyid(c.relowner), c.relname; CREATE RULE sequence_values_rd AS ON DELETE TO sequence_values DO INSTEAD NOTHING; CREATE RULE sequence_values_ri AS ON INSERT TO sequence_values DO INSTEAD NOTHING; CREATE RULE sequence_values_ru AS ON UPDATE TO sequence_values DO INSTEAD SELECT set_sequence(new.sequencename, new.last_value) AS set_sequence; ~Berend Tober
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 > The main problem with this approach is that, while you get the "current > value", the sequence is incremented by the call. I just want to > (strictly) look at the value. The sequence values do not change: test it for yourself. > IF l_last_value = 0 THEN > PERFORM setval(l_sequence_name,1, False); > ELSE > PERFORM setval(l_sequence_name,l_last_value); > END IF; Pretty good idea; just beware of sequences that do not start at 1 :) - -- Greg Sabino Mullane greg@turnstep.com PGP Key: 0x14964AC8 200307241520 -----BEGIN PGP SIGNATURE----- Comment: http://www.turnstep.com/pgp.html iD8DBQE/IDPUvJuQZxSWSsgRAv1MAJ9Ax8EpRTf2ElbdeN/hjMIkWSpPqwCgqAVV Xgese545H2Qa4Znwt1Y7AlE= =IkFq -----END PGP SIGNATURE-----
On Thu, 24 Jul 2003 greg@turnstep.com wrote: > > The main problem with this approach is that, while you get the "current > > value", the sequence is incremented by the call. I just want to > > (strictly) look at the value. > > The sequence values do not change: test it for yourself. I'm not sure, but if another transaction wanted to do a nextval at "the same time" couldn't the sequence of events end up going something like: T1: nextval T2: nextval T1: setval(currval-1) which would do the wrong thing since the next nextval would give the same value that T2 already got?
>> The main problem with this approach is that, while you get the >> "current value", the sequence is incremented by the call. I just want >> to (strictly) look at the value. > > The sequence values do not change: test it for yourself. I guess nothing beats empirical evidence! I was basing my comments on my (mis?)understanding of what the documentation says. >> IF l_last_value = 0 THEN >> PERFORM setval(l_sequence_name,1, False); >> ELSE >> PERFORM setval(l_sequence_name,l_last_value); >> END IF; > > Pretty good idea; just beware of sequences that do not start at 1 :) > Good point. I think I can deal with that. Thank you. ~Berend Tober
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Stephan Szabo wrote: > I'm not sure, but if another transaction wanted to do a nextval at "the > same time" couldn't the sequence of events end up going something like: > > T1: nextval > T2: nextval > T1: setval(currval-1) *shrug* It's possible, though perhaps unlikely, as transaction 2 has a /very/ small window in which to wreak its havok. I like the function solution better anyway; consider the pure SQL one an academic exercise. :) Berend Tober said: > I guess nothing beats empirical evidence! I was basing my comments on my > (mis?)understanding of what the documentation says. My query should match the docs. I'm getting the next value, which increments the sequence by one, then I set the sequence to one less than the value I just grabbed, so it should be back in its initial state. So, we are both correct: I am changing the value, and it stays the same. Aren't sequences fun? :) - -- Greg Sabino Mullane greg@turnstep.com PGP Key: 0x14964AC8 200307241716 -----BEGIN PGP SIGNATURE----- Comment: http://www.turnstep.com/pgp.html iD4DBQE/IE1XvJuQZxSWSsgRApJsAJdha8ZoVJi0QYxRK/2hgnZKLObyAJ4gkBJI 349WNU+i+5j6WttaGcgKtw== =C0O+ -----END PGP SIGNATURE-----