dm 1 week geleden
bovenliggende
commit
7d01aaf577

+ 8 - 0
.obsidian/hotkeys.json

@@ -14,5 +14,13 @@
       ],
       "key": "C"
     }
+  ],
+  "fast-text-color:remove-text-color": [
+    {
+      "modifiers": [
+        "Alt"
+      ],
+      "key": "V"
+    }
   ]
 }

+ 58 - 15
.obsidian/workspace.json

@@ -20,8 +20,51 @@
               "icon": "lucide-file",
               "title": "Homework 4"
             }
+          },
+          {
+            "id": "fbb5916c8bcc907b",
+            "type": "leaf",
+            "state": {
+              "type": "markdown",
+              "state": {
+                "file": "hardcore web development/11. SQL/Создание таблиц.md",
+                "mode": "source",
+                "source": false
+              },
+              "icon": "lucide-file",
+              "title": "Создание таблиц"
+            }
+          },
+          {
+            "id": "d01775aac0e239e9",
+            "type": "leaf",
+            "state": {
+              "type": "markdown",
+              "state": {
+                "file": "Boolean и всякое.md",
+                "mode": "source",
+                "source": false
+              },
+              "icon": "lucide-file",
+              "title": "Boolean и всякое"
+            }
+          },
+          {
+            "id": "df1a6b9ea9f869eb",
+            "type": "leaf",
+            "state": {
+              "type": "markdown",
+              "state": {
+                "file": "hardcore web development/11. SQL/Задание.md",
+                "mode": "source",
+                "source": false
+              },
+              "icon": "lucide-file",
+              "title": "Задание"
+            }
           }
-        ]
+        ],
+        "currentTab": 3
       }
     ],
     "direction": "vertical"
@@ -44,7 +87,7 @@
                 "autoReveal": false
               },
               "icon": "lucide-folder-closed",
-              "title": "Файловый менеджер"
+              "title": "Files"
             }
           },
           {
@@ -144,24 +187,28 @@
   },
   "left-ribbon": {
     "hiddenItems": {
-      "switcher:Меню быстрого перехода": false,
-      "graph:Граф": false,
-      "canvas:Создать новый холст": false,
-      "command-palette:Открыть палитру команд": false,
-      "markdown-importer:Импорт Markdown-файлов": false
+      "switcher:Open quick switcher": false,
+      "graph:Open graph view": false,
+      "canvas:Create new canvas": false,
+      "command-palette:Open command palette": false,
+      "markdown-importer:Open format converter": false
     }
   },
-  "active": "c7584343712d3d31",
+  "active": "df1a6b9ea9f869eb",
   "lastOpenFiles": [
-    "common.md",
+    "Boolean и всякое.md",
+    "hardcore web development/11. SQL/Создание таблиц.md",
+    "hardcore web development/11. SQL/Типы данных.md",
+    "hardcore web development/11. SQL/SQL настройка.md",
+    "hardcore web development/11. SQL/Дата и время.md",
+    "hardcore web development/4. Linux.md",
     "English/Homework 4.md",
+    "common.md",
     "English/Text 7.md",
     "English/Homework 3.md",
     "todo/Задачи по направлениям.md",
     "todo/Тренировки и занятия.md",
-    "hardcore web development/11. SQL/Дата и время.md",
     "hardcore web development/11. SQL/Сортировка данных.md",
-    "hardcore web development/11. SQL/Типы данных.md",
     "linux/server.md",
     "configs/gl_wg.conf",
     "configs/Текстовый документ.txt",
@@ -175,9 +222,7 @@
     "English/img/20-6.JPEG",
     "English/img/20-5.JPEG",
     "English/img/20-4.JPEG",
-    "hardcore web development/11. SQL/Создание таблиц.md",
     "hardcore web development/11. SQL/Задание.md",
-    "hardcore web development/11. SQL/SQL настройка.md",
     "English/homework 2.md",
     "English/Homework.md",
     "todo/Разное.md",
@@ -191,8 +236,6 @@
     "Python/venv.md",
     "Python/telegram bot",
     "proGit.md",
-    "Комментарии.md",
-    "ROTEK/BT_6714.md",
     "English/img/13-5.JPEG.~tmp",
     "English/img/13-4.JPEG.~tmp",
     "docs/sniffers_texto.pdf",

+ 150 - 0
Boolean и всякое.md

@@ -0,0 +1,150 @@
+Создание таблицы из видео:
+
+```sql
+drop table if exists rroom_user, email cascade;
+
+create table rroom_user(
+    user_id bigint generated always as identity primary key,
+    birth_date DATE
+);
+
+create table email (
+    email varchar(320),
+    user_id bigint references rroom_user(user_id),
+    is_verified boolean not null default false,
+    primary key (email, user_id)
+);
+```
+
+Обратите внимание, как здесь создан первичный ключ — он составной, из двух колонок `email` и `user_id`. Так можно делать. В данном случае это оправдано, потому что мы хотим, чтобы была уникальная связка `email` и `user_id`, чтобы эта связка не дублировалась. В такой схеме у нас один мейл может быть у разных пользователей, но при этом только у одного он может быть верифицирован:
+
+```sql
+create unique index unique_verified_email_per_user
+on email (email)
+where is_verified = true;
+```
+
+Об индексах мы поговори позже, но сейчас можно проверить, что оно работает. Создадим пару пользователей:
+
+```sql
+insert into rroom_user(birth_date) values ('1988-07-29') returning user_id; -- 1
+insert into rroom_user(birth_date) values ('1996-01-01') returning user_id; -- 2
+```
+
+Обратите внимание на конструкцию `returning`. С её помощью можно одним запросом как вставить данные, так и вернуть какие-то данные этой вставленной записи, в данном случае поле `user_id` генерируется последовательностью, поэтому очень удобно его значение вернуть в одном запросе с `insert`.
+
+Теперь создадим два одинаковых мейла у каждого:
+
+```sql
+insert into email (email, user_id) values ('sterx@rl6.ru', 1);
+insert into email (email, user_id) values ('sterx@rl6.ru', 2);
+
+select * from email;
+```
+
+Видим, что у обоих записей `is_verified=false`. Это окей. У нас может быть мейл, принадлежащий сначала одному пользователю, а потом другому — какой-то рабочий мейл, например. То есть в нашей схеме нет жесткой связки один email один аккаунт. Это особенно актуально для телефонов, когда один и тот же номер телефона может менять разных владельцев и в разные периоды времени принадлежать разным пользователям, что совершенно нормально. Для мейлов возможно это в меньшей степени актуально, но пусть будет.
+
+Так вот, попробуем верифицировать email сразу у обоих пользователей:
+
+```sql
+update email set is_verified=true where email='sterx@rl6.ru'; -- ошибка
+select * from email;
+
+update email set is_verified=true where email='sterx@rl6.ru' and user_id=1;
+select * from email;
+
+update email set is_verified=true where email='sterx@rl6.ru' and user_id=2; -- ошибка
+select * from email;
+```
+
+Видим, что может быть только один пользователь с верифицированным мейлом! Это благодаря нашему индексу. Повторюсь, об индексах подробнее поговорим дальше.
+
+Кстати, выше я упоминал, но подчеркну ещё раз подход с колонкой `is_deleted`. Зачастую мы не удаляем данные их БД, а просто помечаем их удалёнными, проставляя в колонке `is_deleted` значение `TRUE`. И тогда при каждой выборке активных записей надо будет смотреть, что поле `is_deleted` имеет значение `FALSE`. Тогда у нас все исторические данные будут доступны в таблице и мы сможем при необходимости их использовать в будущем.
+
+```sql
+create temp table some_entity(
+    entity_id bigint generated always as identity primary key,
+    value text not null,
+    is_deleted boolean not null default false
+);
+
+insert into some_entity (value) values ('some value');
+insert into some_entity (value) values ('some another value');
+
+select * from some_entity where is_deleted=false;
+
+update some_entity set is_deleted=true where entity_id=2;
+select * from some_entity where is_deleted=false;
+select * from some_entity;
+```
+
+Другим подходом может быть перенос удалённых значений в отдельную таблицу, например, так:
+
+```sql
+drop table if exists some_entity;
+drop table if exists some_entity_deleted;
+
+create temp table some_entity(
+    entity_id bigint generated always as identity primary key,
+    value text not null
+);
+
+create table some_entity_deleted (like some_entity including defaults including constraints including indexes);
+
+insert into some_entity (value) values ('some value');
+insert into some_entity (value) values ('some another value');
+
+select * from some_entity;
+
+begin;
+insert into some_entity_deleted select * from some_entity where entity_id=2;
+delete from some_entity where entity_id=2;
+commit;
+```
+
+Здесь мы создаём таблицу `some_entity` такую же как и раньше, но без поля `is_deleted`, так как в этом случае вся таблица будет хранить в себе только актуальные записи. Затем мы создаём таблицу `some_entity_deleted`, которая копирует структуру, индексы, ограничения и значения колонок по умолчанию такие же как в `some_entity`. И затем мы вставляем в `some_entity` две строки, одну из которых я затем удалю.
+
+Операция удаления должна быть атомарной — то есть мы в 1 операции должны вставить запись в таблицу удаленных записей и удалить запись из основной таблицы. Иначе если операция будет неатомарной, то вставка записи может пройти, а удаление не пройти, например. Поэтому мы оборачиваем это в транзакцию — о транзакциях мы поговорим позже.
+
+Ну и обращаю ваше внимание, что `insert into таблица select` работает. То есть можно вставлять записи в таблицу из какой-то выборки.
+
+Аналогично можно сделать и без явного определения транзакции с помощью CTE, Common Table Expression, который мы изучим дальше:
+
+```sql
+insert into some_entity (value) values ('some another value');
+
+with deleted_rows as (
+    delete from some_entity
+    where entity_id=3
+    returning *
+)
+insert into some_entity_deleted select * from deleted_rows;
+
+select * from some_entity;
+select * from some_entity_deleted;
+```
+
+Преимуществом такого подхода без колонки `is_deleted` может быть то, что не надо помнить, что в каждый запрос надо вставлять это условие where `is_deleted=false`. Таблицы для хранения исторических данных можно при желании вынести в отдельную схему, чтобы они не были видны в основной рабочей схеме.
+
+Отдельно здесь хочу отметить 2 момента, которые здесь промелькнули в этом уроке. Во-первых, как создать таблицу такую же, как уже имеющаяся таблица:
+
+```sql
+create table some_entity_deleted (like some_entity including defaults including constraints including indexes);
+```
+
+Можно также создать таблицу на основе результатов запроса вместе с результатами этого запроса:
+
+```sql
+create table some_tmp_table as select * from book;
+drop table some_tmp_table;
+```
+
+Здесь структура создаваемой новой таблицы, то есть типы данных и их размеры, определяется автоматически на основе структуры и типов данных столбцов, которые возвращаются `SELECT`-запросом.
+
+И можно вставить в существующую таблицу результаты какого-то запроса:
+
+```sql
+insert into some_entity_deleted select * from some_entity where entity_id=2;
+```
+
+Не то чтобы вы будете этим часто пользоваться, но это довольно удобный механизм в процессе изучения баз данных — дублировать таблицу, вставить туда данные из какого-то запроса и так далее.

+ 1 - 1
hardcore web development/11. SQL/Задание.md

@@ -1,4 +1,4 @@
-Создание таблицы с ограничениями
+ Создание таблицы с ограничениями
 
 ```sql
 create table color (name text check (length(name) between 4 and 20));

+ 4 - 0
hardcore web development/11. SQL/Создание таблиц.md

@@ -47,6 +47,10 @@ create temp table author_without_checks (
 );
 ```
 
+~={yellow}Создание таблицы из select запроса=~
+```sql
+create table some_table as select * from base_table;
+```
 ~={cyan}Создание таблиц в базе bird из задания=~
 
 ```sql

+ 4 - 1
hardcore web development/11. SQL/Типы данных.md

@@ -116,4 +116,7 @@ create table color (
 		'подозрительная мурена'
 	))
 );
-```
+```
+
+~={yellow}Boolean=~
+