[Администрирование баз данных, SQL, PostgreSQL, MySQL] Восемь интересных возможностей PostgreSQL, о которых вы, возможно, не знали (перевод)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Привет, Хабр! Приглашаем на бесплатный Demo-урок «Параллельный кластер CockroachDB», который пройдёт в рамках курса «PostgreSQL». Также публикуем перевод статьи Тома Брауна — Principal Systems Engineer at EnterpriseDB.
В этой статье рассмотрим несколько полезных советов по работе с PostgreSQL:
- Ссылка на всю строку целиком
- Сравнение нескольких столбцов
- Общие табличные выражения
- Пользовательские параметры конфигурации
- Сравнение логических значений без "равно"
- Изменение типа столбца без лишних затрат
- Информация о секции, в которой находится строка
- Таблицы — это типы
Ссылка на всю строку целикомВы когда-нибудь пробовали выполнить подобный запрос?
SELECT my_table FROM my_table;
Запрос выглядит странно. Он возвращает все столбцы таблицы в виде одного столбца. Зачем это может понадобиться? Что ж, я думаю, вы не раз ссылались на таблицы следующим образом:
SELECT table_a.column, table_b.column
FROM table_a
INNER JOIN table_b ON table_a.id = table_b.aid;
Здесь есть ссылка на строку, но только один столбец. И здесь ничего необычного нет. А как насчет этого?
SELECT data, backup_data
FROM data
FULL JOIN backup_data ON data = backup_data
WHERE data IS NULL OR backup_data IS NULL;
Здесь есть таблица data и ее резервная копия backup_data. И что, если мы хотим увидеть разницу между ними: найти изменения с момента резервного копирования и узнать не потеряли ли мы какие-то строки в резервной копии?Для демонстрации создадим таблицу и вставим три строки:
postgres=# CREATE TABLE data (id serial, person text, country text);
CREATE TABLE
postgres=# INSERT INTO data (person, country)
VALUES ('Tim','France'),('Dieter','Germany'),('Marcus','Finland');
INSERT 0 3
Теперь создадим идентичную копию таблицы и скопируем в нее данные:
postgres=# CREATE TABLE backup_data (id serial, person text, country text);
CREATE TABLE
postgres=# INSERT INTO backup_data SELECT * FROM data;
INSERT 0 3
Мы хотим, чтобы таблицы были разными, поэтому одну строку удалим и одну строку добавим:
postgres=# DELETE FROM data WHERE id = 2;
DELETE 1
postgres=# INSERT INTO data (person, country)
VALUES ('Roberto','Italy');
INSERT 0 1
Давайте посмотрим, что произойдет, если мы запустим запрос для сравнения таблиц:
postgres=# SELECT data, backup_data
postgres-# FROM data
postgres-# FULL JOIN backup_data ON data = backup_data
postgres-# WHERE data IS NULL OR backup_data IS NULL;
data | backup_data
-------------------+--------------------
| (2,Dieter,Germany)
(4,Roberto,Italy) |
(2 rows)
Мы видим, что таблица backup_data содержит строку, которая отсутствует в таблице data, и наоборот.Можно использовать эту возможность и так:
postgres=# SELECT to_jsonb(data) FROM data;
to_jsonb
-----------------------------------------------------
{"id": 1, "person": "Tim", "country": "France"}
{"id": 3, "person": "Marcus", "country": "Finland"}
{"id": 4, "person": "Roberto", "country": "Italy"}
(3 rows)
Мы превратили все наши данные в JSON!Сравнение нескольких столбцовЭто очень интересный трюк, с помощью которого можно сделать запросы короче и читабельнее.Допустим, у нас есть следующий запрос:
SELECT country, company, department
FROM suppliers
WHERE country = 'Australia'
AND company = 'Skynet'
AND department = 'Robotics';
Мы можем избавиться от AND:
SELECT country, company, department
FROM suppliers
WHERE (country, company, department) = ('Australia','Skynet','Robotics');
И также можем использовать IN для условий OR:
SELECT country, company, department
FROM suppliers
WHERE department = 'Robotics'
AND (
(country = 'Australia'
AND company = 'Skynet')
OR
(country = 'Norway'
AND company = 'Nortech')
);
Этот запрос можно сократить:
SELECT country, company, department
FROM suppliers
WHERE department = 'Robotics'
AND (country, company) IN (('Australia','Skynet'),('Norway','Nortech'));
Общие табличные выраженияДопустим, у вас есть доступ к базе данных только для чтения и вы не можете создавать таблицы. И также у вас есть небольшой набор данных, который вы бы хотели соединить (join) с существующими таблицами.
SELECT station, time_recorded, temperature
FROM weather_stations;
station | time_recorded | temperature
----------------+---------------------+-------------
Biggin_Hill_14 | 2020-02-02 13:02:44 | 22.4
Reigate_03 | 2020-02-02 16:05:12 | 20.9
Aberdeen_06 | 2020-02-02 15:52:49 | 8.5
Madrid_05 | 2020-02-02 14:05:27 | 30.1
(4 rows)
Допустим, вы хотите получить представление о том, насколько тепло или холодно на каждой из станций. Тогда можно сделать примерно такой запрос:
SELECT station,
CASE
WHEN temperature <= 0 THEN 'freezing'
WHEN temperature < 10 THEN 'cold'
WHEN temperature < 18 THEN 'mild'
WHEN temperature < 30 THEN 'warm'
WHEN temperature < 36 THEN 'hot'
WHEN temperature >= 36 THEN 'scorching'
END AS temp_feels
FROM weather_stations;
В данном способе неудобно добавлять условия. Это можно упростить, создав псевдо-таблицу с использованием общих табличных выражений (CTE, common table expression):
WITH temp_ranges (temp_range, feeling, colour) AS (
VALUES
('(,0]'::numrange, 'freezing', 'blue'),
('( 0,10)'::numrange, 'cold', 'white'),
('[10,18)'::numrange, 'mild', 'yellow'),
('[18,30)'::numrange, 'warm', 'orange'),
('[30,36)'::numrange, 'hot', 'red'),
('[36,)'::numrange, 'scorching', 'black')
)
SELECT ws.station, tr.feeling, tr.colour
FROM weather_stations ws
INNER JOIN temp_ranges tr ON ws.temperature <@ tr.temp_range;
Если вы не знакомы с диапазонными типами, то вас могут смутить "numrange". Это один из диапазонных типов, предназначенный для представления диапазона чисел. Круглые скобки означают исключение границы, квадратные — включение. Таким образом, '(0,10]' означает "от 0, но не включая 0, и до 10 включительно". Отсутствующая левая граница диапазона означает все значения меньше указанного числа, а отсутствующая правая — больше указанного числа.Пользовательские параметры конфигурацииPostgres поставляется с большим набором параметров, которые позволяют настраивать все аспекты системы базы данных, но вы также можете добавлять свои параметры и называть их так, как вам удобно, при условии, что вы укажите префикс.Например, можно добавить в postgresql.conf следующий параметр:
config.cluster_type = 'staging'
А затем получить его значение с помощью команды SHOW.
postgres=# SHOW config.cluster_type;
config.cluster_type
---------------------
staging
(1 row)
Обратите внимание, что эти параметры не отображаются в каталоге pgsettings и не выводятся командой SHOW ALL.Так почему мы должны обязательно указывать префикс конфигурации? До PostgreSQL 9.2 был параметр customvariable_classes, который принимал список классов, которые могли использоваться расширениями для их собственных параметров. Вам нужно было добавить класс расширения в этот список, если вы хотели настроить его через postgresql.conf. Однако это требование было убрано в более поздних версиях, и вам больше не нужно объявлять их явно. Встроенные параметры не имеют префиксов, поэтому любые пользовательские параметры должны иметь префиксы, иначе они не будут приниматься.Такие параметры удобно использовать для предоставления метаданных о кластере.Сравнение логических значений без "равно"Вероятно, вы писали подобные запросы:
SELECT user, location, active
FROM subscriptions
WHERE active = true;
Знаете ли вы, что вам не нужно писать "= true"? Можно упростить:
WHERE active
Это работает, потому что булевы значения не нужно сравнивать с другим булевым значением, поскольку выражения в любом случае возвращают true или false. Отрицание можно написать так:
WHERE NOT active
Это тоже читается лучше.Изменение типа столбца без лишних затратЧасто при изменении типа столбца в таблице с данными, необходимо пересоздавать всю таблицу. Но во многих случаях этого не происходит. И мы можем найти такие типы:
SELECT
castsource::regtype::text,
array_agg(casttarget::regtype ORDER BY casttarget::regtype::text) casttargets
FROM pg_cast
WHERE castmethod = 'b'
GROUP BY 1
ORDER BY 1;
Этот запрос вернет относительно небольшой список типов с информацией о том, в какие "бинарно совместимые" типы они могут быть преобразованы. Из результатов вы увидите, что типы text, xml, char и varchar являются взаимозаменяемыми. Поэтому если у вас есть таблица, содержащая XML-данные в столбце text, не бойтесь его преобразовывать (обратите внимание, что при невалидном XML Postgres возвратит ошибку).Информация о секции, в которой находится строкаТаблица может быть секционирована и вам может понадобиться узнать, в какой секции находится строка? Это легко: просто добавьте tableoid :: regclass в SELECT. Например:
postgres=# SELECT tableoid::regclass, * FROM customers;
tableoid | id | name | country | subscribed
--------------+-----+----------------+----------------+------------
customers_de | 23 | Hilda Schumer | Germany | t
customers_uk | 432 | Geoff Branshaw | United Kingdom | t
customers_us | 815 | Brad Moony | USA | t
(3 rows)
Здесь tableoid - это скрытый системный столбец, который просто нужно явно указать в SELECT. Он возвращает OID (Object Identifier) таблицы, к которой принадлежит строка. Если вы приведете его к типу regclass, то получите имя таблицы.Таблицы — это типыДа, вы все правильно услышали. Каждый раз, когда вы создаете таблицу, вы фактически также создаете новый тип. Смотрите:
CREATE TABLE books (isbn text, title text, rrp numeric(10,2));
Мы можем использовать этот тип таблицы при создании другой таблицы, как параметр функции или в качестве возвращаемого типа:
CREATE TABLE personal_favourites (book books, movie movies, song songs);
Затем можно вставить данные:
INSERT INTO personal_favourites (book)
VALUES (('0756404746','The Name of the Wind',9.99));
Чтобы получить отдельные столбцы из табличного значения, можно выбрать столбец из столбца:
SELECT (book).isbn, (book).title, (book).rrp
FROM personal_favourites;
Теперь я знаю, о чем вы думаете: таблица, содержит табличный тип, который содержит типы, также является типом? Да, но давайте не будем углубляться в эти детали, иначе мы окажемся в запутанной ситуации в Inception-стиле.И как я уже упоминал в "Ссылка на всю строку целиком", вы можете преобразовать всю строку в JSON:
postgres=# SELECT jsonb_pretty(to_jsonb(personal_favourites))
FROM personal_favourites;
jsonb_pretty
----------------------------------------------
{ +
"book": { +
"rrp": 9.99, +
"isbn": "0756404746", +
"title": "The Name of the Wind" +
}, +
"song": { +
"album": "Grace", +
"title": "This is our Last Goodbye",+
"artist": "Jeff Buckley" +
}, +
"movie": { +
"title": "Magnolia", +
"studio": "New Line Cinema", +
"release_date": "2000-03-24" +
} +
}
Эту функциональность можно использовать для создания схем с данными в виде JSON, чтобы получить NoSQL-подобную функциональность, но с данными, имеющими определенную структуру.Но подождите, а что, если я хочу хранить и запрашивать все мои любимые книги, песни и фильмы, а не только одну запись?Это тоже работает. Любой тип, в том числе и табличный, можно превратить в массив, добавив [] после имени типа данных. Вместо того чтобы заново создавать таблицу, давайте просто преобразуем столбцы массивы, а затем добавим еще одну книгу:
ALTER TABLE personal_favourites
ALTER COLUMN book TYPE books[] USING ARRAY[book];
ALTER TABLE personal_favourites
ALTER COLUMN movie TYPE movies[] USING ARRAY[movie];
ALTER TABLE personal_favourites
ALTER COLUMN song TYPE songs[] USING ARRAY[song];
Добавляем еще одну книгу:
UPDATE personal_favourites
SET book = book || ('1408891468','Jonathan Strange and Mr Norrell',7.99)::books;
Теперь результат выглядит следующим образом:
postgres=# SELECT jsonb_pretty(to_jsonb(personal_favourites))
FROM personal_favourites;
jsonb_pretty
--------------------------------------------------------
{ +
"book": [ +
{ +
"rrp": 9.99, +
"isbn": "0756404746", +
"title": "The Name of the Wind" +
}, +
{ +
"rrp": 7.99, +
"isbn": "1408891468", +
"title": "Jonathan Strange and Mr Norrell"+
} +
], +
"song": [ +
{ +
"album": "Grace", +
"title": "This is our Last Goodbye", +
"artist": "Jeff Buckley" +
} +
], +
"movie": [ +
{ +
"title": "Magnolia", +
"studio": "New Line Cinema", +
"release_date": "2000-03-24" +
} +
] +
}
Теперь запрос выводит массив книг и это без каких-либо его изменений. Надеюсь, эти советы помогут вам использовать Postgres более эффективно! Чтобы узнать больше, ознакомьтесь с нашей новой электронной книгой 5 Ways to Get More from PostgreSQL (Пять способов получить больше от PostgreSQL).Интересно развиваться в данном направлении? Запишитесь на бесплатный Demo-урок «Секционирование таблиц PostgreSQL» и участвуйте в онлайн-встрече с Евгением Аристовым — руководителем образовательной программы «PostgreSQL» и курсов «Базы данных», «Software Architect», «MS SQL Server Developer», «Нереляционные базы данных».
===========
Источник:
habr.com
===========
===========
Автор оригинала: Thom Brown
===========Похожие новости:
- [MongoDB] MongoDB — базовые возможности
- [PostgreSQL, SQL, Ненормальное программирование] «Жизнь» на PostgreSQL
- [Тестирование IT-систем, Java, Тестирование веб-сервисов] Flame-графики: «огонь» из всех движков (перевод)
- [Анализ и проектирование систем, Высокая производительность, Промышленное программирование, Распределённые системы] Выбор архитектурного стиля. Часть 4
- [Python] Почему интернационализация и локализация имеют значение (перевод)
- [Big Data, Data Engineering] Курс «Промышленный ML на больших данных» — что это, для кого и каких навыков требует?
- [Java, Oracle, Программирование, Промышленное программирование] Бинарные операторы в Java
- [] WSL эксперименты. Часть 2
- [Laravel] Laravel Jetstream. Зачем?
- [Microsoft SQL Server, Администрирование баз данных, Резервное копирование] MS SQL Server: BACKUP на стероидах
Теги для поиска: #_administrirovanie_baz_dannyh (Администрирование баз данных), #_sql, #_postgresql, #_mysql, #_postgresql, #_wholerow_references, #_comparing_a_selection_of_columns, #_hardcoded_tables, #_custom_config_parameters, #_booleans_can_stand_alone, #_convert_column_data_types_for_free, #_tables_are_types, #_blog_kompanii_otus._onlajnobrazovanie (
Блог компании OTUS. Онлайн-образование
), #_administrirovanie_baz_dannyh (
Администрирование баз данных
), #_sql, #_postgresql, #_mysql
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 19:50
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Привет, Хабр! Приглашаем на бесплатный Demo-урок «Параллельный кластер CockroachDB», который пройдёт в рамках курса «PostgreSQL». Также публикуем перевод статьи Тома Брауна — Principal Systems Engineer at EnterpriseDB.
SELECT my_table FROM my_table;
SELECT table_a.column, table_b.column
FROM table_a INNER JOIN table_b ON table_a.id = table_b.aid; SELECT data, backup_data
FROM data FULL JOIN backup_data ON data = backup_data WHERE data IS NULL OR backup_data IS NULL; postgres=# CREATE TABLE data (id serial, person text, country text);
CREATE TABLE postgres=# INSERT INTO data (person, country) VALUES ('Tim','France'),('Dieter','Germany'),('Marcus','Finland'); INSERT 0 3 postgres=# CREATE TABLE backup_data (id serial, person text, country text);
CREATE TABLE postgres=# INSERT INTO backup_data SELECT * FROM data; INSERT 0 3 postgres=# DELETE FROM data WHERE id = 2;
DELETE 1 postgres=# INSERT INTO data (person, country) VALUES ('Roberto','Italy'); INSERT 0 1 postgres=# SELECT data, backup_data
postgres-# FROM data postgres-# FULL JOIN backup_data ON data = backup_data postgres-# WHERE data IS NULL OR backup_data IS NULL; data | backup_data -------------------+-------------------- | (2,Dieter,Germany) (4,Roberto,Italy) | (2 rows) postgres=# SELECT to_jsonb(data) FROM data;
to_jsonb ----------------------------------------------------- {"id": 1, "person": "Tim", "country": "France"} {"id": 3, "person": "Marcus", "country": "Finland"} {"id": 4, "person": "Roberto", "country": "Italy"} (3 rows) SELECT country, company, department
FROM suppliers WHERE country = 'Australia' AND company = 'Skynet' AND department = 'Robotics'; SELECT country, company, department
FROM suppliers WHERE (country, company, department) = ('Australia','Skynet','Robotics'); SELECT country, company, department
FROM suppliers WHERE department = 'Robotics' AND ( (country = 'Australia' AND company = 'Skynet') OR (country = 'Norway' AND company = 'Nortech') ); SELECT country, company, department
FROM suppliers WHERE department = 'Robotics' AND (country, company) IN (('Australia','Skynet'),('Norway','Nortech')); SELECT station, time_recorded, temperature
FROM weather_stations; station | time_recorded | temperature ----------------+---------------------+------------- Biggin_Hill_14 | 2020-02-02 13:02:44 | 22.4 Reigate_03 | 2020-02-02 16:05:12 | 20.9 Aberdeen_06 | 2020-02-02 15:52:49 | 8.5 Madrid_05 | 2020-02-02 14:05:27 | 30.1 (4 rows) SELECT station,
CASE WHEN temperature <= 0 THEN 'freezing' WHEN temperature < 10 THEN 'cold' WHEN temperature < 18 THEN 'mild' WHEN temperature < 30 THEN 'warm' WHEN temperature < 36 THEN 'hot' WHEN temperature >= 36 THEN 'scorching' END AS temp_feels FROM weather_stations; WITH temp_ranges (temp_range, feeling, colour) AS (
VALUES ('(,0]'::numrange, 'freezing', 'blue'), ('( 0,10)'::numrange, 'cold', 'white'), ('[10,18)'::numrange, 'mild', 'yellow'), ('[18,30)'::numrange, 'warm', 'orange'), ('[30,36)'::numrange, 'hot', 'red'), ('[36,)'::numrange, 'scorching', 'black') ) SELECT ws.station, tr.feeling, tr.colour FROM weather_stations ws INNER JOIN temp_ranges tr ON ws.temperature <@ tr.temp_range; config.cluster_type = 'staging'
postgres=# SHOW config.cluster_type;
config.cluster_type --------------------- staging (1 row) SELECT user, location, active
FROM subscriptions WHERE active = true; WHERE active
WHERE NOT active
SELECT
castsource::regtype::text, array_agg(casttarget::regtype ORDER BY casttarget::regtype::text) casttargets FROM pg_cast WHERE castmethod = 'b' GROUP BY 1 ORDER BY 1; postgres=# SELECT tableoid::regclass, * FROM customers;
tableoid | id | name | country | subscribed --------------+-----+----------------+----------------+------------ customers_de | 23 | Hilda Schumer | Germany | t customers_uk | 432 | Geoff Branshaw | United Kingdom | t customers_us | 815 | Brad Moony | USA | t (3 rows) CREATE TABLE books (isbn text, title text, rrp numeric(10,2));
CREATE TABLE personal_favourites (book books, movie movies, song songs);
INSERT INTO personal_favourites (book)
VALUES (('0756404746','The Name of the Wind',9.99)); SELECT (book).isbn, (book).title, (book).rrp
FROM personal_favourites; postgres=# SELECT jsonb_pretty(to_jsonb(personal_favourites))
FROM personal_favourites; jsonb_pretty ---------------------------------------------- { + "book": { + "rrp": 9.99, + "isbn": "0756404746", + "title": "The Name of the Wind" + }, + "song": { + "album": "Grace", + "title": "This is our Last Goodbye",+ "artist": "Jeff Buckley" + }, + "movie": { + "title": "Magnolia", + "studio": "New Line Cinema", + "release_date": "2000-03-24" + } + } ALTER TABLE personal_favourites
ALTER COLUMN book TYPE books[] USING ARRAY[book]; ALTER TABLE personal_favourites ALTER COLUMN movie TYPE movies[] USING ARRAY[movie]; ALTER TABLE personal_favourites ALTER COLUMN song TYPE songs[] USING ARRAY[song]; UPDATE personal_favourites
SET book = book || ('1408891468','Jonathan Strange and Mr Norrell',7.99)::books; postgres=# SELECT jsonb_pretty(to_jsonb(personal_favourites))
FROM personal_favourites; jsonb_pretty -------------------------------------------------------- { + "book": [ + { + "rrp": 9.99, + "isbn": "0756404746", + "title": "The Name of the Wind" + }, + { + "rrp": 7.99, + "isbn": "1408891468", + "title": "Jonathan Strange and Mr Norrell"+ } + ], + "song": [ + { + "album": "Grace", + "title": "This is our Last Goodbye", + "artist": "Jeff Buckley" + } + ], + "movie": [ + { + "title": "Magnolia", + "studio": "New Line Cinema", + "release_date": "2000-03-24" + } + ] + } =========== Источник: habr.com =========== =========== Автор оригинала: Thom Brown ===========Похожие новости:
Блог компании OTUS. Онлайн-образование ), #_administrirovanie_baz_dannyh ( Администрирование баз данных ), #_sql, #_postgresql, #_mysql |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 19:50
Часовой пояс: UTC + 5