Ответы пользователя по тегу MySQL
  • Как сделать дамп mysql с сохранением связей таблиц?

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

    /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;


    А в конце — такая:

    /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;


    Здесь «40014» — это номер версии сервера; в данном случае проверка будет отключаться для всех версий выше 4.0.14.

    Если проверку не отключать, то связи могут не образоваться во время импорта.

    PhpMyAdmin, кстати, может и сам добавить эти инструкции. Для этого на странице экспорта нужно выбрать способ экспорта «Обычный - отображать все возможные настройки», а в разделе «параметры формата», чуть ниже, поставить галочку напротив пункта «Отключить проверку внешних ключей».

    По-умолчанию PhpMyAdmin этого не делает. Поэтому связи и не импортируются потом нормально.
    Ответ написан
  • Правильно ли сформирован запрос на php, mysql?

    1. Точка с запятой необязательна, если выполняется только одна инструкция. В вашем случае её можно не писать, так как выполняется только одна инструкция «INSERT». Если инструкций несколько, то каждая инструкция обязательно должна заканчиваться точкой с запятой (кроме последней инструкции, кажется — там не обязательно).

    2. Да, запрос правильный.

    Тот запрос, который у вас был «как вариант», не будет работать, потому что вы пытаетесь нормальный PHP-шный NULL скопировать в строку. Там не появится слово «NULL» в итоге, так как вместо него подставится пустая строка. Итоговый запрос будет выглядеть примерно так:

    INSERT INTO users (user_id, user_name, user_adddate) VALUES ('', 'user', '1404721764')


    У вас здесь сразу две ошибки. Первая — вы передаёте значение NULL, а нужно передавать строку со словом «NULL». Вторая — вы пытаетесь сгененрировать строку так, что у вас слово «NULL» будет в SQL-запросе стоять в кавычках. В таком случае это для SQL-сервера будет считаться не значением «NULL», а строкой. Будет выдана ошибка, потому что у вас user_id — целочисленное, а вы пытаетесь в него писать строку. Правильнее будет так (разобью на строки для читаемости):

    <?php
    $insert_sql = sprintf("
        INSERT INTO users (user_id, user_name, user_adddate) 
        VALUES (%s, '%s', %d)
    ", 'NULL', $user_name, time());
    ?>


    Со sprintf вы движетесь в правильном направлении. После этого вам будет легко понять принцип работы подготовленных запросов PDO, которые я вам советую использовать вместо mysql_*-функций.

    <?php
    /* 
     * Инициализация. Эту строку нужно писать там, 
     * где вы делаете mysql_connect. А сам вызов mysql_connect 
     * и прочего сопутствующего можно удалить.
     */
    $db = new PDO('mysql:dbname=testdb;host=127.0.0.1', 'dbuser', 'dbpass');
    
    // . . .
    
    /* 
     * Вставляемые переменные заменяются знаками вопроса. Никаких кавычек
     * тут ставить не нужно — PDO сам поставит их в нужном месте.
     */
    $sth = $db->prepare('INSERT INTO `users` (user_id, user_name, user_adddate) VALUES (NULL, ?, ?)');
    
    /* 
     * Передача значений в запрос происходит в виде массива;
     * порядок элементов в массиве имеет значение. 
     * $user_name — первый элемент, значит его значение подставится 
     * на месте первого знака вопроса. time() — второй элемент, значит
     * его результат подставится вместо второго «?»
     */
    
    $sth->execute( array($user_name, time()) );
    ?>


    В запросе выше я добавил NULL прямо в строку, чтобы сильно не загоняться, так как для его добавления нужна дополнительная работа. С дополнительной работой это выглядит примерно так:

    <?php 
    // . . . 
    
    $sth = $db->prepare('INSERT INTO `users` (user_id, user_name, user_adddate) VALUES (?, ?, ?)');
    
    /*
     * Важный момент: нельзя писать
     * $sth->bindParam(1, NULL, PDO::PARAM_NULL);
     * Перед тем, как биндить NULL, его нужно присвоить переменной 
     * и биндить уже эту переменную. Иначе будут выскакивать ошибки.
     */
    $null_value = NULL;
    $sth->bindParam(1, $null_value, PDO::PARAM_NULL);
    
    $sth->bindParam(2, $user_name, PDO::PARAM_STR);
    $sth->bindParam(3, time(), PDO::PARAM_INT);
    
    /*
     * Так как параметры уже переданы с помощью bindParam-ов,
     * в вызов execute не нужно ничего добавлять.
     */
    $sth->execute();
    ?>


    По поводу NOW() или CURDATE(). У вас выскакивает ошибка потому, что это не функции PHP. Это функции MySQL и их нужно писать в запросе как есть — они выполняются не в интерпретаторе PHP.

    Как я понял, вы пытались что-то вроде этого делать:

    <?php
    $insert_sql = sprintf("
        INSERT INTO users (user_id, user_name, user_adddate) 
        VALUES (%s, '%s', %d)
    ", 'NULL', $user_name, NOW());
    ?>


    А нужно так:
    <?php
    $insert_sql = sprintf("
        INSERT INTO users (user_id, user_name, user_adddate) 
        VALUES (NULL, '%s', NOW())
    ", $user_name);
    ?>


    Так же, если вы добавляете user_adddate с помощью NOW() или CURDATE(), лучше для этого столбца изменить тип с bigint(20) на datetime.
    Ответ написан
  • Проблема с sql запросом (NOT IN)

    Если у вас `usr-ex4-ids` — это строка, в которой через запятую перечислены идентификаторы, то неработающий запрос, после выполнения подзапроса, выглядит так:

    # . . .
    AND `ex4-id` NOT IN ("17,18,19,20")

    То есть, как вы видите, в скобках получается не несколько элементов, разделённых запятыми, а один элемент — строка, внутри которой есть цифры и запятые.

    Есть несколько вариантов.

    1. Можно сделать дополнительную таблицу, в которую записывается соответствие ex4-id и user-id.

    CREATE TABLE `usr-ex4-ids` (
     `ex4-id` int(11) NOT NULL,
     `usr-id` int(11) NOT NULL,
     PRIMARY KEY (`ex4-id`,`usr-id`)
    )


    Далее эта таблица заполняется соответствующими значениями (один ex4-id на строку, а не перечисление ex4-id в одной строке через запятую). Итоговый запрос, который сейчас у вас не работает, будет выглядеть так:

    # . . .
    AND `ex4-id` NOT IN (SELECT `ex4-id` FROM `usr-ex4-ids` WHERE `usr-id` = 51)


    2. Можно попытаться сделать конвертацию строки в массив. Встроенных функций в MySQL для таких задач нет. Советую посмотреть комментарии пользователей к списку строковых функций MySQL — там есть несколько интересных решений (комментарии, как и положено комментариям, находятся в конце страницы).

    Но вообще, я советую первый вариант, так как он логичнее, проще и быстрее. По второму варианту вам скорее всего придётся собственные функции MySQL писать. И они будут работать гораздо медленнее, чем вариант с нормализацией БД.
    Ответ написан
  • Как на php выполнить сразу несколько SQL запросов?

    Первый способ: сохранить запросы в файл и импортировать этот файл через командную строку. mysql -u user database < /home/username/mysqldump.sql

    Второй способ: можно сократить количество запросов немного поменяв логику построения запроса. Вместо

    UPDATE `products` SET `status` = 1 WHERE `product_id` = 1;
    UPDATE `products` SET `status` = 1 WHERE `product_id` = 2;
    -- 
    UPDATE `products` SET `status` = 1 WHERE `product_id` = 1000;


    можно писать так:

    UPDATE `products` SET `status` = 1 WHERE `product_id` IN (1, 2, 3, 4, 5);
    UPDATE `products` SET `status` = 2 WHERE `product_id` IN (6, 7, 8, 9);


    Update: оу... я так медленно пишу. Когда начинал писать, ни одного ответа ещё не было. ))
    Ответ написан
  • Как в SQL выбрать пользователей, у которых день рождения в ближайшую неделю?

    Можно добавить в SQL-запрос условие:

    WHERE TIMESTAMPDIFF(DAY, `birthday`, NOW()) BETWEEN 0 AND 7

    WHERE TIMESTAMPDIFF(
        DAY, 
        DATE_ADD(`birthday`, INTERVAL YEAR(NOW())-YEAR(`birthday`) YEAR), 
        NOW()
    ) BETWEEN 0 AND 7

    (На несколько строк разбил только для того, чтобы было удобнее читать.)

    Спасибо @Kerman за подсказку — совсем не подумал про то, что нужно обратить внимание на год в `birthday`.
    Ответ написан
  • Mysql. Почему поля с содержанием кириллицы возвращают NULL в функции при работе с Cursor?

    Можно вместо concat(res, title) написать concat(IFNULL(res, ''), IFNULL(title, ''))

    В этом случае, если title или res будут NULL, вместо NULL подставится пустая строка, и concat нормально отработает.

    А по поводу кодировки я ничего наверное не подскажу.
    Ответ написан
  • Как можно оптимизировать мой код php+mysql?

    Можно не только сократить количество запросов, но и сделать код немного аккуратнее и безопаснее.

    <?php
    // Эта строчка — где-то в другом файле — где в данный момент инициализируется соединение с БД
    $db = new PDO('mysql:dbname=testdb;host=127.0.0.1', 'username', 'password');
    
    $sth = $db->prepare("
        SELECT 
            t.`id`, 
            DATE_FORMAT(rt.`data`, '%d.%m.%Y') remont_date,
            ma.`name` marka,
            mo.`name` model,
            t.`serial`,
            rt.`work_id`,
            rt.`sum`
        FROM `tech` t 
        LEFT JOIN `remont_tech` rt ON rt.`tech_id` = t.`id` AND rt.`status` <> 'delete'
        LEFT JOIN `marka` ma ON ma.`marka` = t.`marka`
        LEFT JOIN `model` mo ON mo.`id` = t.`model`
        WHERE 
            t.`user` = ? AND
            TIMESTAMPDIFF(DAY, rt.`data`, NOW()) BETWEEN 0 AND 30
        ORDER BY rt.`data` DESC
    ");
    $sth->execute(array($myrow['id']));
    $result = $sth->fetchAll();
    ?>
    <table>
        <tr>
            <th>№</th>
            <th>Дата</th>
            <th>Оборудование</th>
            <th>Проделанная работа</th>
            <th>Сумма</th>
        </tr>
        <?php if (!count($result)) : ?> 
            <tr>
                <td>0</td>
                <td colspan="4">Ничего не найдено.</td>
            </tr>
        <?php else : ?> 
            <?php $summ = 0; ?>
            <?php foreach ($result as $key => $row) : ?> 
                <tr>
                    <td><?= $key+1 ?></td>
                    <td><?= $row['remont_date'] ?></td>
                    <td><?= sprintf('%s %s(%s)', $row['marka'], $row['model'], $row['serial']) ?></td>
                    <td><?= $row['work_id'] ?></td>
                    <td><?= number_format($row['sum'], 0, ',', ' ') ?> руб.</td>
                </tr>
                <?php $summ += $row['sum']; ?>
            <?php endforeach; ?> 
            <tr>
                <td colspan="3"></td>
                <td><b>ИТОГО:</b></td>
                <td><b><?= number_format($summ, 0, ',', ' ') ?> руб.</b></td>
            </tr>
        <?php endif; ?> 
    </table>


    А всякие: рамки таблицы, ширины ячеек и прочее — лучше в файле со стилями прописывать, а не инлайном. Их там гораздо проще обслуживать.
    Ответ написан
  • Как реализовать JOIN для таблиц в двух БД?

    но, по моему, это будет слишком тяжело для баз

    Чтобы для баз было легче, просто сделайте индексы на тех полях, по которым происходит объединение. Один джоин это ещё не «слишком тяжело». Но если вы думаете, что, после запроса с одним джоином, SQL-сервер выйдет из строя на полгода раньше, надорвётся и начнёт глючить, или во время этого запроса будет потреблять электроэнергию, как 100 сварочных аппаратов, то, конечно, можно сделать два запроса, на которые потратится время, а потом потратить время ещё и на цикл в PHP.

    Вообще, лучший способ: замерьте время, которое вам понадобится на то, чтобы получить данные запросом с джоинами, а так же время, которое понадобится для того, чтобы получить данные двумя запросами и с помощью циклов в PHP их объединить. Для чистоты эксперимента, можно вычислять среднее время на 1000 или больше повторений.

    Если у вас базы данных находятся на разных серверах, как вы написали, то самый простой способ — выбрать из каждой таблицы данные отдельно, и уже в PHP сделать объединение. Либо можно использовать FEDERATED таблицы.
    Ответ написан
  • Чем лучше перенести большую базу данных на виртуальный сервер хостинга?

    Насчёт по кускам — не могу сказать, но большой дамп БД можно импортировать из командной строки. Например, если сохранить/закачать дамп в /home/username/mysqldump.sql, то импортировать можно так:

    mysql -u user -p database < /home/username/mysqldump.sql


    Здесь user после ключа -u — пользователь БД, который имеет право импортировать, database — название БД, в которую будут импортироваться данные; -p — ключ, который указывает, что нужно спросить пароль перед импортом. Если хочется ввести пароль сразу в командной строке, он задаётся после знака равенства в ключе p, вот так: -p=P455w0rd — без пробелов.

    Эта команда, если не ошибаюсь, после запуска спросит у вас пароль для пользователя user, а после ввода пароля выполнит файл /home/username/mysqldump.sql (ну или тот, который вы ей укажете) для заданной базы данных.

    Так импортировать гораздо проще, чем оперировать кусками файлов.
    Ответ написан