Pull to refresh

Postgres и Пустота

Reading time 2 min
Views 14K

Только что натолкнулся на возможность Postgresql, показавшуюся мне забавной. Для кого "баян" — респект вам, я несколько лет работаю с Postgres и до сих пор не натыкался на такую штуку.


select; без указания полей, таблицы и условий возвращает одну строку. Но у этой строки нет полей:


=> select;
--
(1 row)

Для сравнения:


=> select null;
 ?column? 
----------

(1 row)
=> select null where 0=1;
 ?column? 
----------
(0 rows)

А сможем ли мы создать таблицу из такого "пустого" запроса? Таблицу без полей.


Да пожалуйста:


=> create table t as select;
SELECT 1
=> \d+ t
                          Table "t"
 Column | Type | Modifiers | Storage | Stats target | Description 
--------+------+-----------+---------+--------------+-------------
=> select * from t;
--
(1 row)

А можем ли мы в неё вставить?
Легко:


=> insert into t select;
INSERT 0 1
=> insert into t select;
INSERT 0 1
=> select * from t;
--
(3 rows)
=> select count(*) from t;
 count 
-------
     3

ЕЩЕ!


=> insert into t select from generate_series(1,1000000);
INSERT 0 1000000

Интересно, будет ли Postgresql сканировать такую таблицу?


=> explain analyze select * from t;
                                                 QUERY PLAN                                                 
------------------------------------------------------------------------------------------------------------
 Seq Scan on t  (cost=0.00..13438.67 rows=1000167 width=0) (actual time=0.018..96.389 rows=1000003 loops=1)
 Planning time: 0.024 ms
 Execution time: 134.654 ms
(3 rows)

Да, честно сканирует. Больше 100 ms — вполне себе заметное время.
Ну и чтобы убедиться, что всё по честному, посмотрим сколько места занимает наша супер-полезная таблица:


=> select pg_size_pretty(pg_total_relation_size('t'));
 pg_size_pretty 
----------------
 27 MB
(1 row)

То есть таблица есть, занимает место на диске, в блоках хранятся разные служебные данные, ну а то, что в ней нет полей — бывает, дело житейское!


=> select t.xmin, t.ctid from t limit 10;
  xmin   |  ctid  
---------+--------
 1029645 | (0,1)
 1029647 | (0,2)
 1029648 | (0,3)
 1029649 | (0,4)
 1029649 | (0,5)
 1029649 | (0,6)
 1029649 | (0,7)
 1029649 | (0,8)
 1029649 | (0,9)
 1029649 | (0,10)
(10 rows)

Я не придумал, зачем может понадобиться такая таблица. Но возможность есть, и это хорошо!
Я использую Postgresql 9.6. Как отмечают ниже в более низких версиях это не работает. В 9.3 выдаёт syntax error. 9.4, 9.5 под рукой нет чтобы проверить.

Tags:
Hubs:
+33
Comments 33
Comments Comments 33

Articles