currval i problemy z selectami

jak może wiecie w postgresie jest funkcja currval() zwracająca ostatnio nadane id z podanej sekwencji.

rozpatrzmy prosty przypadek:

# CREATE TABLE test (id serial PRIMARY KEY, pole int4);
CREATE TABLE
# INSERT INTO test (pole) SELECT * FROM generate_series(1, 10000);
INSERT 0 10000
# SELECT COUNT(*) FROM test;
 COUNT
-------
 10000
(1 ROW)
# SELECT currval('test_id_seq');
 currval
---------
   10000
(1 ROW)

wszystko wygląda ok. więc sprawdźmy jeszcze jeden insert mały:

# INSERT INTO test(pole) VALUES (12313);
INSERT 0 1
# SELECT currval('test_id_seq');
 currval
---------
   10001
(1 ROW)

nadal wszystko ok. i teraz:

# SELECT * FROM test WHERE id = currval('test_id_seq');
  id   | pole
-------+-------
 10001 | 12313
(1 ROW)
TIME: 93.358 ms

działa, ale coś wolno, sprawdźmy:

# EXPLAIN analyze SELECT * FROM test WHERE id = currval('test_id_seq');
                                            QUERY PLAN
--------------------------------------------------------------------------------------------------
 Seq Scan ON test  (cost=0.00..200.02 ROWS=1 width=8) (actual TIME=64.375..64.379 ROWS=1 loops=1)
   FILTER: (id = currval('test_id_seq'::regclass))
 Total runtime: 64.431 ms
(3 ROWS)

seq scan? sprawdźmy więc ręcznie podaną wartość:

# EXPLAIN analyze SELECT * FROM test WHERE id = 10001;
                                                   QUERY PLAN
----------------------------------------------------------------------------------------------------------------
 INDEX Scan USING test_pkey ON test  (cost=0.00..8.02 ROWS=1 width=8) (actual TIME=0.029..0.033 ROWS=1 loops=1)
   INDEX Cond: (id = 10001)
 Total runtime: 0.086 ms
(3 ROWS)

hmm .. tu jest dobrze. skąd seq scan? aby nie przynudzać z każdym zapytaniem, powiem tyle, że nie jest to kwestia błędnych typów czy czegoś tak oczywistego. problemem jest zmienność funkcji.

dokładniej: funkcja currval() jest zadeklarowana jako “volatile" – co oznacza, że jej wynik ma prawo zmienić się w czasie pojedynczego skanu tabeli. tak jak np. random(). to oznacza, że nie można użyć jej jako dostarczyciela wartości i potem tą wartością przeszukać indeksy.

cóż więc można zrobić – no cóż. trzeba powiedzieć postgresowi, że interesuje nas tylko pierwsza zwrócona wartość currvala – idealnie do tego nadają się podzapytania:

# EXPLAIN analyze SELECT * FROM test WHERE id = (SELECT currval('test_id_seq'));
                                                   QUERY PLAN
----------------------------------------------------------------------------------------------------------------
 INDEX Scan USING test_pkey ON test  (cost=0.01..8.03 ROWS=1 width=8) (actual TIME=0.047..0.050 ROWS=1 loops=1)
   INDEX Cond: (id = $0)
   InitPlan
     ->  RESULT  (cost=0.00..0.01 ROWS=1 width=0) (actual TIME=0.010..0.012 ROWS=1 loops=1)
 Total runtime: 0.187 ms
(5 ROWS)

jak widać jest już zdecydowanie lepiej.

One thought on “currval i problemy z selectami”

Comments are closed.