dotzero

↑ ↑ ↓ ↓ ← → ← → B A Start

Патчим mysqldump в домашних условиях

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

MySQL позволяет использовать любые имена триггеров, в том числе с использованием точек (например: analitica.cron.indeg.y.run.a_insert). А mysqldump при создании дампа не учитывает это обстоятельство и добавляет конструкцию для их дропа следующего вида:

/*!50032 DROP TRIGGER IF EXISTS analitica.cron.indeg.y.run.a_insert */;

Подвох ожидает при попытке эти дампы накатить на базу где эти триггеры уже созданы. С точки зрения MySQL этот запрос не дропнет триггер, потому что не найдет триггер с таким именем. Для корректной работы имя триггера должно быть заключено в апострофы.

В попытках найти обходное решение зарепортил соответствующий репорт в баг трекер Перконы, а они уже продублировали аналогичный репорт в официальный трекер MySQL.

Поскольку исправлять этот баг будут долго, а дампы мне нужны прямой сейчас. Решить эту задачу я решил самым прямолинейным способом, а именно самостоятельно пропатчив mysqldump. Для этого склонировал официальный репозиторий перконовского дитрибутива MySQL 5.6 с GitHub’а.

git clone --recursive --depth 1 https://github.com/percona/percona-server/

Открыл файл client/mysqldump.c и добавил апострофы к конструкции DROP TRIGGER IF EXISTS в паре мест. Если посмотреть diff, то получается такой патч:

@@ -3517,7 +3517,7 @@ static void dump_trigger_old(FILE *sql_file, MYSQL_RES *show_triggers_rs,
     fprintf(sql_file, "/*!50003 SET @OLD_SQL_MODE=@@SQL_MODE*/;\n");

   if (opt_drop_trigger)
-    fprintf(sql_file, "/*!50032 DROP TRIGGER IF EXISTS %s */;\n", (*show_trigger_row)[0]);
+    fprintf(sql_file, "/*!50032 DROP TRIGGER IF EXISTS `%s` */;\n", (*show_trigger_row)[0]);

   fprintf(sql_file,
           "DELIMITER ;;\n"
@@ -3604,7 +3604,7 @@ static int dump_trigger(FILE *sql_file, MYSQL_RES *show_create_trigger_rs,
     switch_sql_mode(sql_file, ";", row[1]);

     if (opt_drop_trigger)
-      fprintf(sql_file, "/*!50032 DROP TRIGGER IF EXISTS %s */;\n", row[0]);
+      fprintf(sql_file, "/*!50032 DROP TRIGGER IF EXISTS `%s` */;\n", row[0]);

Чтобы собрать пропатченую версию надо поставить пару пакетов и на Ubuntu/Debian, запустить cmake с параметрами из документации к Перконе, а затем make‘ом собрать только mysqldump.

apt-get install build-essential cmake bison libaio-dev libncurses5-dev libreadline-dev
cmake . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_CONFIG=mysql_release -DFEATURE_SET=community -DWITH_EMBEDDED_SERVER=OFF
make mysqldump

После компиляции получаем патченую версию mysqldump, в которой имена триггеров экранируются корректно. Можно дампить новым дампером:

./percona-server/client/mysqldump \
    --socket=/var/run/mysqld/mysqld.sock \
    -uroot -p --routines --events --triggers \
    --add-drop-trigger --quote-names \
    --no-create-info --no-data --no-create-db --skip-opt \
    database_name | sed -r 's/DEFINER[ ]*=[ ]*[^*]*\*/\*/' > dump.sql

Конвертация таблиц MySQL из MyISAM в InnoDB

Вот простой трюк для перевода всех MySQL MyISAM таблиц в таблицы InnoDB.

Сперва необходимо подготовить файл содержащий список SQL запросов на конвертацию каждой таблицы в отдельности. Это просто сделать выполнив следующий запрос (заменив your_database_name на имя базы):

/usr/bin/mysql --defaults-extra-file=/etc/mysql/debian.cnf --batch --skip-column-names -e "SELECT CONCAT('ALTER TABLE ',table_schema,'.',table_name,' engine=InnoDB;') FROM information_schema.tables WHERE TABLE_SCHEMA='your_database_name' AND ENGINE = 'MyISAM';" > myisam.txt

Получив таким образом список SQL запросов в файле myisam.txt, необходимо его выполнить используя команду:

/usr/bin/mysql --defaults-extra-file=/etc/mysql/debian.cnf your_database_name < myisam.txt

Hardcode

Прочитал сегодня две интересные заметки, первая от Ивана Сагалаева о найме инженеров-программистов, а вторая это смежная заметка от Sam‘а в его блоге, где он рассказывает о своем опыте как нанимателя, так и соискателя.

Поскольку я уже некоторое время занимаюсь проведением собеседований php программистов для достаточно простых, по меркам этих двух заметок, проектов, то как следствие сталкиваюсь с потоком намного менее квалифицированных соискателей, которые как правило либо не имеют опыта как такового, либо этот опыт оставляет желать лучшего. Помимо обязательного общения с HR-менеджером каждому из кандидатов предлагается пройти небольшой тест по результатам которого можно сделать первоначальное представление о соискателе, и решить стоит ли отвлекаться от основной работы для проведения беседы.

Тест действительно небольшой и не содержит вопросов с двойным дном, для кандидатов на должность junior программист это всего 10 вопросов, половина из которых это базовые вещи о языке на котором они пишут, а другая половина о MySQL. Для кандидатов на должность senior разработчик, хотя это громко сказано, этих вопросов в два раза больше. Ответы я получаю совершенно разные, но статистика не утешительная, больше половины кандидатов не могут ответить даже на них. Складывается впечатление что люди приходящие в веб студии думают, что можно вообще ничего не знать или знать на уровне пользования одной технологии или cms-системы.

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

hardcode.dotzero.ru

Как поменять местами значения в колонках таблицы

Условия задачи: есть MySQL таблица table1. Используя только язык SQL запросов необходимо поменять местами значения из колонок value1 и value2. Структура таблицы имеет следующий вид:

CREATE TABLE `table1` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `value1` VARCHAR(50) NOT NULL DEFAULT '' ,
  `value2` VARCHAR(50) NOT NULL DEFAULT '' ,
  PRIMARY KEY (`id`) )
ENGINE = MyISAM;

Решение «в лоб»

Самым простым способом является явное переименование колонок таблицы. Данное решение самое простое в реализации и самое быстрое по результатам моих тестов.

ALTER TABLE  `table1`
CHANGE `value1` `value2` VARCHAR(50) NOT NULL DEFAULT '' ,
CHANGE `value2` `value1` VARCHAR(50) NOT NULL DEFAULT '';

Решение с временной переменной

Изящное решение с использованием временной переменной для хранения промежуточного результата. По скорости оно уступает первому решению тем не менее показывает дополнительные скилзы в SQL.

UPDATE `table1` SET `value1`=(@temp:=`value1`), `value1` = `value2`, `value2` = @temp;

Решение с подзапросом

Поскольку MySQL не даст сделать простой UPDATE с выборкой из этой же таблицы, поэтому будем извращаться с ON DUPLICATE KEY UPDATE. Самое медленное из предложенных решений, но данный синтаксис предоставляет определенные преимущества в некоторых ситуациях.

INSERT INTO `table1`
SELECT * FROM `table1` `t2` ON DUPLICATE
KEY UPDATE `value1` = `t2`.`value2`, `value2` = `t2`.`value1`;