Browse Source

импорт данных MySQL Workbench

Evgeny Kolesnikov 4 years ago
parent
commit
ca80f3cf30
1 changed files with 315 additions and 3 deletions
  1. 315 3
      articles/sql_import.md

+ 315 - 3
articles/sql_import.md

@@ -129,8 +129,320 @@ CSV расшифровывается как comma-separated values — «зна
 * выбираем в контекстном меню «Format Cells»;
 * в открывшемся диалоге выбираем слева тип данных «Text».
 
-<!-- ## Практическое задание
+## Разбор импорта данных на примере прошлогоднего демо-экзамена
 
-В каталоге `data` этого репозитория находится структура БД (`ms.sql`) и файлы для импорта: `products_k_import.csv`, `materials_short_k_import.txt`, `productmaterial_k_import.xlsx`.
+>Файлы, используемые в этом разборе, лежат в каталоге [data](/data) этого репозитория
 
-Необходимо **во-первых**, восстановить структуру БД из скрипта,  **во-вторых**, импортировать исходные данные в **Excel**, **в-третьих** исправить данные (смотрите на структуру таблиц в БД, где-то надо явно указать тип данных, где-то вырезать лишние данные...) и **в-четвёртых** загрузить исправленные данные в БД (сначала просто импорт во временные таблицы, потом разнести SQL-запросами по нужным таблицам) -->
+### Создание таблиц из скрипта
+
+1. Откройте [соединение](#подключение-к-базе-данных) с БД
+
+2. Откройте вкладку SQL-скрипта, если её ещё нет
+
+    ![](../img/01030.png)
+
+3. Скопируйте в буфер обмена содержимое файла `my.sql` (на демо-экзамене в ресурсах будет два файла: `my.sql` для *MySQL* и `ms.sql` для *MSSQL*) и вставьте его во вкладку SQL-скрипта, открытую в предыдущем пункте.
+
+4. Добавьте в начало скрипта команду `USE <название вашей базы данных>;`, для моей БД это будет `USE ekolesnikov;` (обращаю внимание, в MySQL команды должны завершаться точкой с запятой) и выполните скрипт (кликните по значку молнии)
+
+    ![](../img/01031.png)
+
+    После выполнения скрипта в вашей БД должны появиться новые таблицы (пустые)
+
+    ![](../img/01032.png)
+
+### Импорт данных
+
+В ресурсах у нас есть три файла для импорта:
+
+* `materials_short_k_import.txt`
+* `products_k_import.csv`
+* `productmaterial_k_import.xlsx`
+
+>При реальной разработке вам придется самостоятельно разбираться в какую таблицу импортировать данные, но в рамках демо-экзамена имена импортируемых файлов соответсвуют таблицам БД
+
+#### Импорт материалов 
+
+Процесс импорта состоит из нескольких этапов:
+
+- [подготовка данных](#подготовка-данных)
+- [импорт во временную таблицу](#импорт-во-временную-таблицу)
+- перенос данных из временной таблицы в нужную таблицу(ы)
+
+##### Подготовка данных
+
+1. Открываем *Excel*
+
+2. Открываем или импортируем файл с исходными данными
+
+    >Файл материалов у нас в формате *TXT*. Такой формат напрямую из Excel открыть нельзя - загружайте через *импорт данных* (в разных версиях Excel меню может называться по0разному)
+    >
+    >![](../img/01034.png)
+
+    * Выберите файл для импорта
+
+        В мастере импорта на первом шаге выберите правильную кодировку (в предварительном просмотре должен быть читаемый текст) и поставьте, если нужно, *галочку* "мои данные содержат заголовки"
+
+        ![](../img/01035.png)
+
+    * Выберите разделитель (обычно используется запятая или точка с запятой, но могут втретиться и более экзотические символы)
+
+        ![](../img/01036.png)
+
+3. Правка данных
+
+    После импорта данных смотрим всё-ли нормально с данными (Смотрим на структуру БД и просто включаем логику). В этой таблице в колонке "стоимость" явно лишние слова "руб." и "рублей"
+
+    ![](../img/01037.png)
+
+    * выделяем редактируемую колонку 
+
+        ![](../img/01038.png)
+
+    * открываем диалог "Найти и заменить" (`Ctrl+F`)
+
+        и вырезаем всё лишнее
+
+        ![](../img/01039.png)
+
+    * меняем формат ячейки на "числовой"
+
+        Видим, что некоторые строки не распознались как числа. 
+
+        ![](../img/01040.png)
+
+        Дело в том, что в русской локали разделителем разрядов является запятая, а в исходном файле резделитель точка - меняем точки на запятые
+
+4. Экспорт данных
+
+    >**ВАЖНО!!!** Нигде не написано, но названия полей при импорте должны быть латиницей, поэтому переименовываем заголовки столбов.
+
+    MySQL может импортировать данные только в *CSV*-формате. Файл либо "сохраняем как", либо "экспортируем"
+
+    ![](../img/01041.png)
+
+    ![](../img/01042.png)
+
+
+##### Проверка кодировки
+
+>**ВАЖНО!!!** Кодировка импортируемых данных должна быть такой же, как кодировка базы данных (сейчас практически везде используется UTF-8)
+
+**Excel** экспортирует текстовые данные в кодировке **ANSI** (1251)
+
+Для перекодировки файла можно воспользоваться программой **Notepad++** - она будет на демо-экзамене
+
+![](../img/01044.png)
+
+1. Обращаем внимание на кодировку файла. У меня, например, с какого-то перепугу решило что **Macintosh**.
+
+    ![](../img/01044.png)
+
+    Меняем на "Windows-1251"
+
+    ![](../img/01045.png)
+
+2. Кодировка теперь нормальная, но по содержимому видно, что у некоторых столбцов есть лидирующий пробел. Это не есть хорошо. 
+
+    ![](../img/01046.png)
+
+    Сдесь же в **Notepad++** заменим `"; "` на `";"`
+
+    ![](../img/01047.png)
+
+3. Если визуально всё нормально, то меняем кодировку файла на **UTF-8**
+
+    ![](../img/01048.png)
+
+    и сохраняем файл
+
+##### Импорт во временную таблицу
+
+В контекстном меню базы данных запускаем "мастер импорта данных"
+
+![](../img/01049.png)
+
+Импортируем в новую таблицу с уникальным именем
+
+![](../img/01050.png)
+
+Смотрим, всё ли правильно определилось. Судя по тому, что цену как число не распознало MySQL по-умолчанию ждёт точку в качестве разделителя. 
+
+![](../img/01051.png)
+
+Меняем тип поля на *double* и в появившемся поле "Decimal Separator" пишем запятую.
+
+![](../img/01052.png)
+
+После импорта открываем таблицу и смотрим всё-ли нормально
+
+![](../img/01053.png)
+
+##### перенос данных из временной таблицы в нужную таблицу(ы)
+
+По структуре таблицы **Material** видно, что одно поле словарное (*MaterialTypeID*)
+
+![](../img/01033.png)
+
+т.е. нам сначала нужно заполнить таблицу **MaterialType** уникальными значениями типов материалов. 
+
+>Вспомиаем, что для выбора уникальных значений используется ключевое слово **DISTINCT**
+
+Но, кроме названия метериала (*Title*) в таблице **MaterialType** есть ещё какой-то *DefectedPercent*. Смотрим структуру данных:
+
+![](../img/01054.png)
+
+и видим, что поле это обязательное. 
+
+Но в исходных данных у нас никаких "дефектных процентов" нет...
+
+Тут есть два варианта:
+
+* добавить фиксированное значение, взятое "с потолка"
+* отменить обязательность этого поля (NN = Not NULL)
+
+Мы будем использовать первый вариант.
+
+1. Открываем окно с SQL-запросами (возможно оно ещё открыто после импорта структуры базы)
+
+2. Пишем запрос для вставки уникальных значений типов материалов из временной таблицы. 
+
+    Современные клиенты СУБД позволяют облегчить написание этих запросов:
+
+    * в контекстном меню таблицы **MaterialType** выбираем "Send to SQL Editor" -> "Insert Statement"
+
+        ![](../img/01055.png)
+
+        В акивное окно SQL-запросов вставится следующий запрос:
+
+        ```sql
+        INSERT INTO `ekolesnikov`.`MaterialType`
+        (`ID`,
+        `Title`,
+        `DefectedPercent`)
+        VALUES
+        (<{ID: }>,
+        <{Title: }>,
+        <{DefectedPercent: }>);
+        ```
+
+        т.е. у нас уже указана база и таблица, и перечислены все поля этой таблицы.
+
+    * Из запроса удаляем поле **ID** (оно автоинкрементное и само заполнится при вставке) и выражение **VALUES...** (мы будем вставлять не фиксированные значения, а результат выборки из другой таблицы)
+
+        Должен остаться такой текст (курсор оставляем после текста запроса)
+
+        ![](../img/01056.png)
+
+    * теперь аналогично из контекстного меню таблицы **material_import** выбираем "Send to SQL Editor" -> "Select All Statement"
+
+        ```sql
+        INSERT INTO `ekolesnikov`.`MaterialType`
+        (`Title`,
+        `DefectedPercent`)
+        SELECT `material_import`.`name`,
+            `material_import`.`type`,
+            `material_import`.`count`,
+            `material_import`.`edizm`,
+            `material_import`.`quantity`,
+            `material_import`.`min_ost`,
+            `material_import`.`price`
+        FROM `ekolesnikov`.`material_import`;
+        ```
+
+    * Количество и порядок выбираемых полей должны соответствовать вставляемым полям, перечисленным в круглых скобках запроса **INSERT**. Редактируем **SELECT** запрос:
+
+        ```sql
+        INSERT INTO `ekolesnikov`.`MaterialType`
+        (`Title`,
+        `DefectedPercent`)
+        SELECT 
+            DISTINCT `material_import`.`type`, 
+            0
+        FROM `ekolesnikov`.`material_import`;
+        ```
+
+        т.е. мы убрали все поля, кроме *type* (тип материала), поставили перед ним ключевое слово **DISTINCT** (выбирать только уникальные) и для поля *DefectedPercent* записали константу "0"
+
+        После выполнения получившегося запроса смотрим что загрузилось в целевую таблицу (MaterialType):
+
+        ![](../img/01057.png)
+
+        Всё нормально, переходим к следующему этапу.
+
+3. Пишем запрос для вставки данных в таблицу материалов (**Material**) с учётом словарного поля *MaterialTypeID*
+
+    * очищаем окно SQL-запроса, вставляем в него "Insert Statement" для таблицы **Material** и вырезаем лишнее
+
+        ![](../img/01058.png)
+
+        Вырезаем поле *ID* (автоинкермент), поля *Description* и *Image* (в исходных данных их нет и поля не обязательные)
+
+        ```sql
+        INSERT INTO `ekolesnikov`.`Material`
+        (`Title`,
+        `CountInPack`,
+        `Unit`,
+        `CountInStock`,
+        `MinCount`,
+        `Cost`,
+        `MaterialTypeID`)
+        ```
+
+    * Вставляем "Select All Statement" из таблицы **material_import**    
+
+        ```sql
+        INSERT INTO `ekolesnikov`.`Material`
+        (`Title`,
+        `CountInPack`,
+        `Unit`,
+        `CountInStock`,
+        `MinCount`,
+        `Cost`,
+        `MaterialTypeID`)
+        SELECT `material_import`.`name`,
+            `material_import`.`type`,
+            `material_import`.`count`,
+            `material_import`.`edizm`,
+            `material_import`.`quantity`,
+            `material_import`.`min_ost`,
+            `material_import`.`price`
+        FROM `ekolesnikov`.`material_import`;
+        ```
+
+        Вспоминаем, что порядок и количество полей должны совпадать и редактируем запрос:
+
+        ```sql
+        INSERT INTO `ekolesnikov`.`Material`
+            (`Title`,
+            `CountInPack`,
+            `Unit`,
+            `CountInStock`,
+            `MinCount`,
+            `Cost`,
+            `MaterialTypeID`)
+        SELECT `material_import`.`name`,
+            `material_import`.`count`,
+            `material_import`.`edizm`,
+            `material_import`.`quantity`,
+            `material_import`.`min_ost`,
+            `material_import`.`price`,
+            MaterialType.ID
+        FROM 
+            `ekolesnikov`.`material_import`, 
+            `ekolesnikov`.MaterialType
+        WHERE 
+            `material_import`.`type`=MaterialType.Title;
+        ```
+
+        Для получения идентификатора типа материала (`MaterialType.ID`) мы добавили в запрос таблицу **MaterialType** (в секцию FROM) 
+
+        И связали исходные таблицы по названию типа (секция WHERE)
+
+        После выполнения получившегося запроса в окне **Output** должно появиться сообщение:
+
+        ```
+        50 row(s) affected Records: 50  Duplicates: 0  Warnings: 0	0.047 sec
+        ```
+
+Самостоятельно загрузите данные из файлов `products_k_import.csv` и `productmaterial_k_import.xlsx`