постгрес

Upsert в PostgreSQL

Ура, появился коммит, добавляющий к оператору INSERT «Постгреса» конструкцию, которая позволяет использовать его как upsert (в «Оракле» это конструкция MERGE, в «Майэскуэеле» — INSERT… ON DUPLICATE KEY UPDATE):

-- Обновить, если не удалась вставка
INSERT INTO distributors AS d (did, dname) VALUES (8, 'Anvil Distribution')
ON CONFLICT (did) DO UPDATE
SET dname = EXCLUDED.dname || ' (formerly ' || d.dname || ')'
WHERE d.zipcode != '21201';

-- Если не удалась вставка, ничего не делать
INSERT INTO distributors (did, dname) VALUES (10, 'Conrad International')
ON CONFLICT (did) WHERE is_active DO NOTHING;

Счастье-то какое!

5 комментариев

PostgreSQL: сортировка без учёта регистра

Мы переводим свои некоторые продукты с «Оракла» на «Постргрес» и постоянно сталкиваемся с какими-то различиями разной величины. Сегодняшнее открытие — «Постгрес» (в отличие от «Оракла») сортирует числовые значения без учёта регистра:

test=# SELECT * FROM (SELECT 'Ба' UNION ALL SELECT 'бб' UNION ALL SELECT 'Бв') _ ORDER BY 1;
 ?column?
----------
 Ба
 бб
 Бв
(3 строки)

Ни в одном описании опыта такого же перехода я почему-то не натыкался на такую проблему. Если регистр для вас важен, то есть целых два выхода. Либо сменить принцип сравнения:

test=# SELECT * FROM (SELECT 'Ба' AS str UNION ALL SELECT 'бб' UNION ALL SELECT 'Бв') _ ORDER BY str COLLATE "C";
 str
-----
 Ба
 Бв
 бб
(3 строки)

Либо преобразовать строку к бинарному типу:

test=# SELECT * FROM (SELECT 'Ба' AS str UNION ALL SELECT 'бб' UNION ALL SELECT 'Бв') _ ORDER BY str::bytea;
 str
-----
 Ба
 Бв
 бб
(3 строки)

Правда в последнем случае неясно сможет ли «Постгрес» использовать индекс — мы пока таких испытаний не проводили.

9 комментариев

POSTQUEL

POSTQUEL — это язык запросов, который когда-то поддерживала СУБД «Постгрес». Наверняка многие задумывались почему языков программирования так много, а языков в базах данных, кроме SQL и его диалектов, не видать; SQL оказался исключительно удачным, но у него были когда-то и соперники. «Постквел» один из них.

POSTQUEL расшифровывается как «POSTgres QUEry Language», «язык запросов Постгреса», он был разработам в 1985 году в Калифорнийском университете в Беркли под руководством Майкла Стоунбрейкера, POSTQUEL основан на языке запросов QUEL, который использовался на тот момент в БД «Ингрес».

Примеры того на что был похож POSTQUEL есть в польской Википедии:

Получить размер заработной платы сотрудника Ковальски (pracownicy — сотрудники, płace — зарплата):

retrieve (PRACOWNICY.placa) from PRACOWNICY where PRACOWNICY.nazwisko = "Kowalski"

Все сотрудники старше 40 лет (wiek — возраст, nazwisko — имя):

retrieve (P.nazwisko) from P in PRACOWNICY where P.wiek > 40

Найти все департаменты, целиком занимающие один этаж (pietro — этаж, dnazwa — сокращение от «название департамента»):

retrieve (DEPART.dnazwa)
where DEPART.pietro NOT-IN {D.pietro from D in DEPART where D.dnazwa != DEPART.dnazwa}

«Постквел» был более формализованным языком, чем Эскюэль, все команды строились по одному образцу и не допускали вариаций. Других подробностей я не нашёл, к сожалению, детального описания языка мне обнаружить пока не удалось, было бы любопытно посмотреть на него и проанализировать сильные стороны.

Добавлено позднее. Я обнаружил документацию 1992 года на «Постгрес», где довольно подробно упоминается и «Постквел» (ссылка есть в комментариях). Интересный язык-то был!

Например, оперировал он объектами, поддерживал наследование (все примеры из упомянутой документации):

create STUD_EMP (location = point) inherits (EMP)

Поддерживал массивы (индексы начинаются с единицы):

create SAL_EMP (name = char[], pay_by_quarter = int4[4])

append SAL_EMP (name = "bill", pay_by_quarter="{10000, 10000, 10000, 10000}")

retrieve (SAL_EMP.name) where SAL_EMP.pay_by_quarter[1] !=SAL_EMP.pay_by_quarter[2]

Поддерживал историю, вот, например, срез данных с первого января 1970 по наши дни:

retrieve (E.salary)
    from E in EMP["Jan 1 00:00:00 1970 GMT", "now"]
    where E.name = "Sam"

Была возможность создавать пользовательские типы (тут создаётся новый тип массива):

define type int_array
    (element = int4, internallength = variable,
    input = array_in, output = array_out)

Так же как и пользовательские функции:

define function eq_area_circle
    (language = "c", returntype = bool)
    arg is (circle, circle)
    as "/usr/postgres/tutorial/circle.o"

define function manager
    (language = "postquel", returntype = EMP)
    arg is (EMP)
    as "retrieve (E.all) from E in EMP
    where E.name = DEPT.manager
    and DEPT.name = $1.dept"

retrieve (EMP.name)
    where name(manager(EMP)) = "Joe"

Была даже перегрузка операторов!

define operator =
    (arg1 = circle, arg2 = circle,
    procedure = eq_area_circle)

И так далее.

23 комментария

Базы данных. Наведем порядок

Когда-то давно, мне надоело припоминать форматы таблиц и значения их полей, возвращаясь к какому-то Веб-проекту, сданному два, три месяца, полгода назад. На бумажную память надежды у меня мало — к сожалению бумаги слишком легко теряются и путаются, в записях нет единства, да и найти в груде бумаги последнюю версию описания проблематично.

Устав бороться со своей и чужой ленью, я решил принять какие-то меры, чтобы облегчить жизнь себе и другим. Одна из таких мер — файл описания таблиц, который мы теперь вкладываем в корневой директорий каждого проекта. Я обычно называю его .tables (естественно в настройках Apache прописывается запрет на получение этого файла через Web), где в достаточно свободном формате описываются таблицы, поля, связи между ними и все остальные объекты.

Естественно, что файл предназначен для описания достаточно небольших баз данных (менее 7—10 таблиц). В этом случае больших баз без хорошо составленной наглядной документации все равно не обойтись. Не надейтесь. :)

Четкий формат представления имеют пока только таблицы, в силу того, что остальными объектами приходится пользоваться достаточно редко, да и синтаксис их описания различается для разных СУБД, но, возможно, в следующих версиях формата появятся правила и их описания тоже.

Вот пример описания двух таблиц, связанных между собой

[/usr/local/pgsql/bin/psql eshop]

items / товары
-----------------------
id              serial / идентификатор
gid             int4   / категория товара -> group.id
name            text   / наименование
price           real   / цена
comment         text   / описание товара

CREATE INDEX items_price_idx ON items (price)

group / категории
-----------------------
id              serial  / идентификатор -> items.gid
parent          int4    / родитель в дереве. -1, если верхний уровень -> id
name            text    / наименование

Думаю из примера и так все понятно, но я все-таки дам пару комментариев.

Строчка, заключенная в квадратные скобки, — команда, принимающая данные. Пустые строки и строки, начинающиеся с двух минусов игнорируются. В абзаце, где описывается таблица, первым идет ее имя, ниже — описания ее полей: наименования, через табуляцию тип. В конце каждой значимой строки через пробел и слеш находится ее краткое описание.

Инструкции заведения секвенсоров, представлений и хранимых процедур записываются в одну строку.

Так как формат файла представлений достаточно легко может быть разобран и преобразован в команды мониторов mSQL, PostgreSQL и MySQL я, пару дней назад, выбрав время, написал небольшую программку на Перле. Правда толком ее еще не обкатал, но не думаю, что с ее эксплуатацией возникнут какие-то проблемы.

Интепретатор файлов таблиц: intp.pl

#!/usr/bin/perl

sub StripComment
{
        my ($line) = @_;
        unless ($line=~s{^([^/]*)\s+/.+}{$1}o)
        {
                print "Предупреждение: не указан комментарий [ $line ].\n"
                unless ($line=~/^CREATE/o);
        };

        return $line;
};

sub AddData
{
        my ($table, @query) = @_;
        my $query  = join ',', @query;
        my $result = '';

        if ($todrop)
        {
                for (@query)
                {
                        if (/([^ ]+) serial$/)
                        {
                        $result.= "DROP SEQUENCE ${table}_${1}_seq\\g\n";
                        last;
                        };
                };


                $result.= "DROP TABLE $table\\g\n";
        };

        $result.= "CREATE TABLE $table ($query)\\g\n";

        return $result."\n";
};

sub AddDrop
{
        my $table = $_[0];

        if ($todrop && $table=~/^CREATE ([^ ]+ +[^ ]+)/o)
        {
                return "DROP $1\\g\n$table\\g\n";
        } else
        {
                return "$table\\g\n";
        };
};

unless (open F, (shift or '.tables'))
{
        print <DATA>;
        die "\n"
};

@query  = ();
$table  = '';
$result = '';
$comm   = '';
$todrop = shift or 0;

while (<F>)
{
        chomp;
        next if ($_ eq '' || /^\-\-/);

        if (/^\[(.+?)\]$/)
        {
                $comm = $1;
                next;
        };


        @data =  split (/       +/,StripComment $_);
        if (@data < 2)
        {
                $result.= @query?AddData($table, @query):AddDrop ($table)
                if ($table ne '');

                $table  = $data[0];
                @query  = ();
        } else
        {
                push @query, "$data[0] $data[1]";
        };
};

close F;

$result.= AddData ($table, @query) if (@query);

if ($result ne '')
{
        if ($comm ne '')
        {
                open  F, "|$comm";
                print F "$result\\q";
                close F;
        } else
        {
                print "Ошибка: не указана команда для приема данных.";
        };
} else
{
        print "Ошибка: файл для обработки пуст.";
};

__END__

Интерпретатор файлов таблиц для СУБД.
Написал Евгений Степанищев (2000 год).

Использование: inpt.pl имя_файла [DROP]

DROP - предварительно сделать DROP для таблиц и SEQUENCE

Формат:

-- БД stat                              комментарий

tablename / описание                    имя таблицы
---------------------                   оформительский элемент

поле1   тип / описание                  поля таблицы,
поле2   тип / описание                  описание интепретатор игнорирует
и т.д.

[/usr/local/mysql/bin/mysql stat]       комманда, принимающая данные

P.S. Если кто-то разовьет этот формат или предложит мне адекватную замену буду очень рад опубликовать и его тоже.
P.P.S. Вышеописанное ни в коем случае не претендует на панацею и всеобъемлющий инструмент, а всего лишь на роль мелочи, которая поможет избежать некоторых возможных трудностей.

Оставить комментарий

Софт. FAR 1.70b2, MySQL, PostgreSQL…

Новость дня — вышел FAR 1.70b2 build 321. С некоторый пор этот, один из лучших (скажу лучший — заклюют :) файловых менеджеров, разрабатывается не одним человеком, а группой разработчиков (FAR Team). С этих же самых пор список WhatsNew каждой новой версии поражает своим размером. Вот и на этот раз, я насчитал 64 пункта! Приставки Beta бояться не нужно, FAR Team обещает выпустить release только в случае полного отсутствия багов. То есть никогда :). Так что — качать!

Продолжая тему FARa. 14-го декабря появилась новая версия одного из моих любимых плагинов к FARy — ProxyNews 1.0 b8. Исправление только одно — пофиксен баг с листингом сообщений.

Смешно, но факт — я знаю людей, которые пользуются FARом, начиная чуть ли не с первой версии и не знают о существовании такий вещи, как плагины. На всякий случай, сообщу. Сам FAR — это еще не все. К нему существуют «довески», называемые плагинами, которые сильно расширяют его функциональность. Например, есть плагины для чтения почты, работы с FTP через Proxy-сервер, проигрывания музыкальных файлов (точнее управления WinAMP’ом) и многого другого. Плагины можно взять на сайте PlugRing, ссылка на который есть в конце статьи (см. «ссылки по теме»).

Из других новостей мира вареза — появление MySQL 3.23.29a-gamma. В списке изменений — в основном исправление ошибок.

Почему-то, MySQL считается лидером по производительности в мире Веба. По-моему, мнение это распространяется за счет испытаний на время обработки одного простейшего запроса. На мой взгляд, у PostgreSQL достоинств гораздо больше. Во-первых, он производительнее MySQL, если запросов несколько (надо только запустить его в асинхронном режиме), при условии, что сайты, обращающиеся к СУБД, достаточно посещаемы, наличие нескольких запросов одновременно — обычное дело. Во-вторых, в PostgreSQL есть несколько встроенных языков для определения хранимых процедур. В-третьих, в PSQL есть подзапросы, в отличие от MySQL, где их можно найти только в TODO list. В-четвертых, в PSQL есть UNION. В MySQL приходится пользоваться временными таблицами. Да и вообще, что перечислять? В MySQL есть, на мой взгляд, только две вещи, которых нет в Postgres: переменные (которые, кстати, не везде могут быть использованы) и условие IF (которое с успехом заменятся CASE и процедурами).

Так что, если будете выбирать СУБД для Веба… вы знаете, что я бы посоветовал.

Оставить комментарий

← 1 2 →