SQL и реляционные базы данных
Таблицы, ключи, CRUD, JOIN, связи и параметризованные запросы.
Обновлено
Что такое реляционная база данных
Реляционная база данных (PostgreSQL, MySQL, SQLite) хранит данные в таблицах: у каждой таблицы есть колонки с фиксированными типами, а каждая строка — это один элемент. Таблицы связаны между собой ключами — отсюда «реляционная».
users posts
id | name | email id | user_id | title
---+-------+------------ ---+---------+---------
1 | Ana | ana@x.com 1 | 1 | Privet
2 | Ivan | ivan@x.com 2 | 1 | Next 16
SQL — язык, на котором запрашивают и меняют эти данные. Даже если вы используете ORM, выполняется именно SQL — его стоит свободно читать. Это приложение использует PostgreSQL на Neon.
Ключи
| Ключ | Роль |
|---|---|
| primary key | однозначно определяет строку (id) |
| foreign key | колонка, указывающая на ключ другой таблицы (posts.user_id → users.id) |
| unique | значение, которое не может повторяться (email) |
| индекс | структура, ускоряющая поиск по колонке |
Четыре операции (CRUD)
SELECT id, title FROM posts WHERE user_id = 1 ORDER BY created_at DESC LIMIT 10;
INSERT INTO posts (user_id, title) VALUES (1, 'Privet') RETURNING id;
UPDATE posts SET title = 'Privet!' WHERE id = 1;
DELETE FROM posts WHERE id = 1;UPDATE и DELETE без WHERE затрагивают все строки. Это первое, что вы проверяете.
SELECT по частям
| Часть | Роль | Аналог в JS |
|---|---|---|
SELECT col1, col2 |
какие колонки | map |
FROM таблица |
из какой таблицы | массив |
WHERE условие |
какие строки | filter |
ORDER BY col DESC |
порядок | toSorted |
LIMIT n OFFSET m |
сколько и откуда | slice |
GROUP BY col + COUNT(*) |
группировка и агрегация | Object.groupBy + length |
JOIN — объединение таблиц
SELECT posts.title, users.name
FROM posts
JOIN users ON users.id = posts.user_id;| Вид | Сохраняет |
|---|---|
JOIN (inner) |
только строки, у которых есть пара в обеих таблицах |
LEFT JOIN |
все строки слева; справа = NULL, если пары нет |
Связи — виды
| Связь | Пример | Как моделируется |
|---|---|---|
| один-ко-многим | один пользователь → много постов | posts.user_id |
| многие-ко-многим | посты ↔ теги | связующая таблица post_tags (post_id, tag_id) |
| один-к-одному | пользователь → профиль | profiles.user_id unique |
SQL-инъекции — золотое правило
Никогда не вставляйте ввод пользователя прямо в текст SQL:
sql(`SELECT * FROM users WHERE email = '${email}'`) // ✗ email = "' OR '1'='1" → все пользователи
sql`SELECT * FROM users WHERE email = ${email}` // ✓ параметр — отправляется отдельно от запросаШаблон sql`...` из @neondatabase/serverless (используется в этом приложении), ORM и конструкторы запросов отправляют значения как параметры ($1, $2), а не как текст.
Коротко
- Таблицы с типизированными колонками, связанные первичными / внешними ключами.
SELECT … WHERE … ORDER BY … LIMIT,INSERT,UPDATE … WHERE,DELETE … WHERE;JOINдля связей.- Всегда параметризованные запросы — никогда не вставляйте ввод в текст.