|
|
@@ -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-скрипта, если её ещё нет
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+3. Скопируйте в буфер обмена содержимое файла `my.sql` (на демо-экзамене в ресурсах будет два файла: `my.sql` для *MySQL* и `ms.sql` для *MSSQL*) и вставьте его во вкладку SQL-скрипта, открытую в предыдущем пункте.
|
|
|
+
|
|
|
+4. Добавьте в начало скрипта команду `USE <название вашей базы данных>;`, для моей БД это будет `USE ekolesnikov;` (обращаю внимание, в MySQL команды должны завершаться точкой с запятой) и выполните скрипт (кликните по значку молнии)
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+ После выполнения скрипта в вашей БД должны появиться новые таблицы (пустые)
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+### Импорт данных
|
|
|
+
|
|
|
+В ресурсах у нас есть три файла для импорта:
|
|
|
+
|
|
|
+* `materials_short_k_import.txt`
|
|
|
+* `products_k_import.csv`
|
|
|
+* `productmaterial_k_import.xlsx`
|
|
|
+
|
|
|
+>При реальной разработке вам придется самостоятельно разбираться в какую таблицу импортировать данные, но в рамках демо-экзамена имена импортируемых файлов соответсвуют таблицам БД
|
|
|
+
|
|
|
+#### Импорт материалов
|
|
|
+
|
|
|
+Процесс импорта состоит из нескольких этапов:
|
|
|
+
|
|
|
+- [подготовка данных](#подготовка-данных)
|
|
|
+- [импорт во временную таблицу](#импорт-во-временную-таблицу)
|
|
|
+- перенос данных из временной таблицы в нужную таблицу(ы)
|
|
|
+
|
|
|
+##### Подготовка данных
|
|
|
+
|
|
|
+1. Открываем *Excel*
|
|
|
+
|
|
|
+2. Открываем или импортируем файл с исходными данными
|
|
|
+
|
|
|
+ >Файл материалов у нас в формате *TXT*. Такой формат напрямую из Excel открыть нельзя - загружайте через *импорт данных* (в разных версиях Excel меню может называться по0разному)
|
|
|
+ >
|
|
|
+ >
|
|
|
+
|
|
|
+ * Выберите файл для импорта
|
|
|
+
|
|
|
+ В мастере импорта на первом шаге выберите правильную кодировку (в предварительном просмотре должен быть читаемый текст) и поставьте, если нужно, *галочку* "мои данные содержат заголовки"
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+ * Выберите разделитель (обычно используется запятая или точка с запятой, но могут втретиться и более экзотические символы)
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+3. Правка данных
|
|
|
+
|
|
|
+ После импорта данных смотрим всё-ли нормально с данными (Смотрим на структуру БД и просто включаем логику). В этой таблице в колонке "стоимость" явно лишние слова "руб." и "рублей"
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+ * выделяем редактируемую колонку
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+ * открываем диалог "Найти и заменить" (`Ctrl+F`)
|
|
|
+
|
|
|
+ и вырезаем всё лишнее
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+ * меняем формат ячейки на "числовой"
|
|
|
+
|
|
|
+ Видим, что некоторые строки не распознались как числа.
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+ Дело в том, что в русской локали разделителем разрядов является запятая, а в исходном файле резделитель точка - меняем точки на запятые
|
|
|
+
|
|
|
+4. Экспорт данных
|
|
|
+
|
|
|
+ >**ВАЖНО!!!** Нигде не написано, но названия полей при импорте должны быть латиницей, поэтому переименовываем заголовки столбов.
|
|
|
+
|
|
|
+ MySQL может импортировать данные только в *CSV*-формате. Файл либо "сохраняем как", либо "экспортируем"
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+
|
|
|
+##### Проверка кодировки
|
|
|
+
|
|
|
+>**ВАЖНО!!!** Кодировка импортируемых данных должна быть такой же, как кодировка базы данных (сейчас практически везде используется UTF-8)
|
|
|
+
|
|
|
+**Excel** экспортирует текстовые данные в кодировке **ANSI** (1251)
|
|
|
+
|
|
|
+Для перекодировки файла можно воспользоваться программой **Notepad++** - она будет на демо-экзамене
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+1. Обращаем внимание на кодировку файла. У меня, например, с какого-то перепугу решило что **Macintosh**.
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+ Меняем на "Windows-1251"
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+2. Кодировка теперь нормальная, но по содержимому видно, что у некоторых столбцов есть лидирующий пробел. Это не есть хорошо.
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+ Сдесь же в **Notepad++** заменим `"; "` на `";"`
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+3. Если визуально всё нормально, то меняем кодировку файла на **UTF-8**
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+ и сохраняем файл
|
|
|
+
|
|
|
+##### Импорт во временную таблицу
|
|
|
+
|
|
|
+В контекстном меню базы данных запускаем "мастер импорта данных"
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+Импортируем в новую таблицу с уникальным именем
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+Смотрим, всё ли правильно определилось. Судя по тому, что цену как число не распознало MySQL по-умолчанию ждёт точку в качестве разделителя.
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+Меняем тип поля на *double* и в появившемся поле "Decimal Separator" пишем запятую.
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+После импорта открываем таблицу и смотрим всё-ли нормально
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+##### перенос данных из временной таблицы в нужную таблицу(ы)
|
|
|
+
|
|
|
+По структуре таблицы **Material** видно, что одно поле словарное (*MaterialTypeID*)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+т.е. нам сначала нужно заполнить таблицу **MaterialType** уникальными значениями типов материалов.
|
|
|
+
|
|
|
+>Вспомиаем, что для выбора уникальных значений используется ключевое слово **DISTINCT**
|
|
|
+
|
|
|
+Но, кроме названия метериала (*Title*) в таблице **MaterialType** есть ещё какой-то *DefectedPercent*. Смотрим структуру данных:
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+и видим, что поле это обязательное.
|
|
|
+
|
|
|
+Но в исходных данных у нас никаких "дефектных процентов" нет...
|
|
|
+
|
|
|
+Тут есть два варианта:
|
|
|
+
|
|
|
+* добавить фиксированное значение, взятое "с потолка"
|
|
|
+* отменить обязательность этого поля (NN = Not NULL)
|
|
|
+
|
|
|
+Мы будем использовать первый вариант.
|
|
|
+
|
|
|
+1. Открываем окно с SQL-запросами (возможно оно ещё открыто после импорта структуры базы)
|
|
|
+
|
|
|
+2. Пишем запрос для вставки уникальных значений типов материалов из временной таблицы.
|
|
|
+
|
|
|
+ Современные клиенты СУБД позволяют облегчить написание этих запросов:
|
|
|
+
|
|
|
+ * в контекстном меню таблицы **MaterialType** выбираем "Send to SQL Editor" -> "Insert Statement"
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+ В акивное окно SQL-запросов вставится следующий запрос:
|
|
|
+
|
|
|
+ ```sql
|
|
|
+ INSERT INTO `ekolesnikov`.`MaterialType`
|
|
|
+ (`ID`,
|
|
|
+ `Title`,
|
|
|
+ `DefectedPercent`)
|
|
|
+ VALUES
|
|
|
+ (<{ID: }>,
|
|
|
+ <{Title: }>,
|
|
|
+ <{DefectedPercent: }>);
|
|
|
+ ```
|
|
|
+
|
|
|
+ т.е. у нас уже указана база и таблица, и перечислены все поля этой таблицы.
|
|
|
+
|
|
|
+ * Из запроса удаляем поле **ID** (оно автоинкрементное и само заполнится при вставке) и выражение **VALUES...** (мы будем вставлять не фиксированные значения, а результат выборки из другой таблицы)
|
|
|
+
|
|
|
+ Должен остаться такой текст (курсор оставляем после текста запроса)
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+ * теперь аналогично из контекстного меню таблицы **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):
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+ Всё нормально, переходим к следующему этапу.
|
|
|
+
|
|
|
+3. Пишем запрос для вставки данных в таблицу материалов (**Material**) с учётом словарного поля *MaterialTypeID*
|
|
|
+
|
|
|
+ * очищаем окно SQL-запроса, вставляем в него "Insert Statement" для таблицы **Material** и вырезаем лишнее
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+ Вырезаем поле *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`
|