Найти тему

Пятничный SQL-WTF или Понедельничный SQL-TIL

И вот как мы пишем SQL. Вопросы есть?
И вот как мы пишем SQL. Вопросы есть?

"Хочешь разобраться в чём-то - напиши статью"

Когда я начинал работать над текущим проектом, я программировал уже около 25 лет и с базами данных работал больше 20 из них.

Казалось, что я видел большую часть проблем, связанных с разработкой на SQL.

Но стандарт SQL и частные его реализации не перестают удивлять меня до сих пор.

Сегодня я поделюсь с вами несколькими внезапными синтаксическими конструкциями и неочевидным поведением.

По понятным причинам тестировалось это все в PostgreSQL, но большая часть будет работать и в других базах.

Скорее всего вы никогда не столкнетесь с таким синтаксисом в реальной жизни. Но зато сможете блеснуть эрудицией перед своими коллегами :)

1 wtf из 5 - пустые таблицы

Можно создать таблицу совсем без колонок. Или удалить все колонки из таблицы. И это не ошибка.

Зачем это может быть нужно я придумать не смог, но на всякий случай у нас об этом есть notice :)

2 wtf из 5 - системные колонки и зарезервированные слова

В каждой таблице в PostgreSQL есть специальные колонки tableoid, xmin, cmin, xmax, cmax, ctid, которые создаются автоматически и содержат системную информацию о строках.

Создавать свои колонки с такими именами запрещено. Значения из этих колонок можно читать, но изменять ни значение, ни сами колонки нельзя.

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


Валидно

CREATE TABLE "all" (a INT);

Не валидно

CREATE TABLE all (a INT);

Это причина, по которой все ORM генерируют запросы с кавычками для имен таблиц и колонок.

Но, что и предсказуемо, ORM ничего не знает про системные колонки и ошибку вы получите уже в рантайме применения миграции.

Все это может показаться ерундой, но наверняка много страданий принесет при необходимости в переезде с одной базы на другую. Например, в процессе импортозамещения.

В идеале стоило бы избегать зарезервированных имен из всех стандартов SQL. И у есть нас соответствующий notice.



3 wtf из 5 - сокращенный синтаксис для SELECT * FROM tbl

Можно короче? Да изи!

TABLE tbl

Никаких дополнительных условий этот синтаксис не предполагает.

4 wtf из 5 - SELECT tbl FROM tbl

Если в таблице нет поля, имя которого совпадает с именем таблицы, то следующие два запроса будут эквивалентны

SELECT tbl FROM tbl;
SELECT ROW(tbl.*) FROM tbl;

И даже больше!

SELECT * FROM tbl1, tbl2 WHERE tbl1 = tbl2;
SELECT * FROM tbl1, tbl2 WHERE ROW(tbl1) = ROW(tbl2);

ROW() - создает тип RECORD. Это кортеж, содержащий значение всех колонок по очереди их следования в источнике данных.

Возможно не все знают, но такие кортежи можно сравнивать.

Это очень удобно, когда нужно сравнить попарно несколько наборов значений.

Не считая того, что такой синтаксис может ввести не очень опытных разработчиков в заблуждение и затруднит чтение запросов, такой подход так же вреден как, и SELECT *.

Поэтому мы сделали warning.

5 wtf из 5 - замена имен полей в подзапросах

Если подзапрос возвращает одно поле (field1) и это поле сравнивается во внешнем условии с полем field2, то из первого подзапроса можно вернуть поле с именем field2:

WITH a(field1) AS (
VALUES(1)
),
b(field2) AS (
VALUES(1)
)
SELECT * FROM b WHERE field2 IN (SELECT field2 FROM a)

oO

Такого поворота не ожидал даже я...

Мы об этом пока не предупреждаем, но обязательно будем.

Это, конечно же, не все, чем я могу вас удивить :) Продолжение в следующих сериях :)

Больше на канале в телеграме:
https://t.me/nosingularity