Только что натолкнулся на возможность 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 под рукой нет чтобы проверить.

Поделиться с друзьями
-->

Комментарии (33)


  1. zoroda
    31.05.2017 08:14
    +4

    Это особенность реализации для ALTER TABLE, чтобы при очередном DROP COLUMN не валилась ошибка


    1. CPro
      31.05.2017 09:39

      Тоже странная вещь ведь. По-сути это как новую таблицу сделать, если все поля дропаешь и новые добавляешь.


      1. zoroda
        31.05.2017 10:46
        +3

        Да, наверное странно, но такое решение было принято чтобы обойти потенциальные проблемы с DROP COLUMN


  1. hdfan2
    31.05.2017 09:41
    +7

    Любимая таблица дзен-буддистов.


    1. mwambanatanga
      31.05.2017 09:45
      +5

      Да, эта табличка очень даже ничего.


  1. spendlively
    31.05.2017 09:46
    +13

    Для кого «боян»...

    Правильно писать «бАян»! Извините, не смог, сдержаться. Просто 8 лет играл на нем в школе. Наконец-то пригодилось!


    1. fireSparrow
      31.05.2017 10:06
      +5

      Из википедии:
      Боян (Boian) — археологическая культура эпохи неолита (4-е тыс. до. н. э.), на территории Молдавии, Румынии и Болгарии.

      То есть, когда про какую-то тему говорят «ну, это уже боян», значит этой теме 5-6 тысяч лет.


      1. spendlively
        31.05.2017 10:17
        +10

        Ах, чёрт, значит еще не пригодилось!


      1. KozzyKoder
        31.05.2017 11:42
        +1

        Еще в «Слове о полку Игореве» упоминается древнерусский певец и сказитель Боян. Всегда думал что это слово с ним связано.


    1. CPro
      31.05.2017 11:19
      +1

      Поправил. Баян может спать спокойно!


    1. dmitry_dvm
      31.05.2017 11:35

      В этом контексте как раз таки боян, а не баян. Т.к. автор имел в виду нечто давно известное, а не музыкальный инструмент.


      1. CPro
        31.05.2017 11:37
        +17

        Пусть будет баян. Человек 8 лет учился.


      1. fireSparrow
        31.05.2017 11:53
        +2

        На самом деле, всё-таки «баян».
        Существуют три основных версии, почему это слово стали использовать в значении «повторно опубликованная шутка или информация».
        По двум из них это слово как раз-таки пошло от названия музыкального инструмента, по третьей — от крейсера «Баян» времён русско-японской войны.


        1. Razoomnick
          31.05.2017 15:50
          +1

          Я всегда почему-то думал, что это отсылка к анекдоту «хоронили тещу, порвали два баяна».


          1. fireSparrow
            31.05.2017 15:58

            Да, это как раз таки самая популярная версия.
            Считается, что на неком форуме этот анекдот постили настолько часто, что в итоге он начал вызывать резко негативную реакцию у старожилов, они-то и начали употреблять слово «баян» в современном значении.
            Но есть и другие версии.


  1. ploop
    31.05.2017 09:55

    У меня одного данный пример возвращает только syntax error?


    1. Michael13
      31.05.2017 10:48

      Та же фигня. Может от версии зависит…


      1. ploop
        31.05.2017 10:50

        9.1.13 у меня


    1. Germanets
      31.05.2017 11:03

      Postgresql 9.6.2, работает так же как и у автора.


    1. animhotep
      31.05.2017 11:11
      +1

      думаю стоит версию указывать, на 9.5.6 работает


    1. CPro
      31.05.2017 11:21

      Добавил комментарий про версию. В 9.3 не работает, в 9.5, говорят, работает. Осталось проверить 9.4 у кого есть и мы будем знать мажорную версию, когда добавили эту супер фишку :)
      Ну не читать же release notes, в самом деле :)


      1. HunterNNm
        31.05.2017 11:39

        На 10 версии тоже работает. Так что фичу не выпилили )


      1. kshvakov
        31.05.2017 12:40
        +4

        Это появилось в 9,4
        https://www.postgresql.org/docs/9.4/static/release-9-4.html

        Allow SELECT to have an empty target list (Tom Lane)
        This was added so that views that select from a table with zero columns can be dumped and restored correctly.


        1. CPro
          31.05.2017 12:45
          +1

          О, ты наш герой! Думал уж никто до release notes не доберётся :)
          То что там написано как то не очень пролило свет:

          This was added so that views that select from a table with zero columns can be dumped and restored correctly.

          Ну ок, чтобы работал дамп и рестор вьюх на таблицы без колонок. А зачем нужны вьюхи которые селектят из таблицы без колонок? Зачем вообще эти таблицы?


    1. kshvakov
      31.05.2017 12:38

      Это из-за того, что до версии 9,4 нельзя было делать SELECT без указания колонок; например для EXISTS приходилось ставить null (SELECT * FROM T WHERE f EXISTS (SELECT null FROM ...), сейчас это не обязательно


      1. CPro
        31.05.2017 12:41

        Это точно, или предположение? Всё ради того, чтобы сэкономить на наборе 5 символов?


    1. lagranzh
      31.05.2017 22:28

      9.4.9 тоже работает


  1. Movimento5Litri
    31.05.2017 12:24

    => select pg_size_pretty(pg_total_relation_size('t'));
    pg_size_pretty
    — 27 MB

    А почему так много?


    1. CPro
      31.05.2017 12:28
      +2

      Я же вставил миллион записей. Только хедер у одной строки 23 байта, это уже 23 мегабайта, если считать хипстерскими мегабайтами (1 млн байт).


      1. AK74U
        31.05.2017 21:17
        +1

        Вы мне подарили замечательный термин («хипстерский мегабайт»), спасибо вам!


  1. guai
    31.05.2017 18:10
    +1

    Хорошая, годная фича. Сделал пустую таблицу, потом напихал в нее колонок — красота. То же самое при удалении, все удалил, нужные создал. Иначе был бы головняк с последней колонкой — сделай колонку затычку, чтоб не ругалось, потом удали — ужос.


  1. Melkij
    31.05.2017 18:47
    +1

    Для внимательных, create table без колонок в мануале описаны, в самом конце «Zero-column Tables»
    Судя по комментарию, разрешили просто чтобы не запариваться с логикой проверки в drop column.


  1. excoder
    01.06.2017 00:37

    Это классика реляционной алгебры. Dee и Dum Дэйта:

    [Dee] is the relation that has no attributes and a single tuple. It plays the role of True.
    [Dum] is the relation that has no attributes and no tuples. It plays the role of False.

    Отсюда: https://www.amazon.com/Foundation-Future-Database-Systems-Manifesto/dp/0201709287.