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

    nowm
    @nowm
    Вроде, такой подход должен сработать, если я правильно понял, что вы имели ввиду ActiveRecord:

    $subQuery = chat_bot_clients::find()
        ->select('id')
        ->where(['chat_bot_user_id' => 1]);
    
    $query = vk_users::find()
        ->where(['user_id' => 24])
        ->andWhere(['in', 'chat_bot_clients_id', $subQuery]);
    
    $models = $query->all();
    Ответ написан
    2 комментария
  • Правильно ли сформирован запрос на php, mysql?

    nowm
    @nowm
    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)

    nowm
    @nowm
    Если у вас `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 запросов?

    nowm
    @nowm
    Первый способ: сохранить запросы в файл и импортировать этот файл через командную строку. 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 выбрать пользователей, у которых день рождения в ближайшую неделю?

    nowm
    @nowm
    Можно добавить в 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`.
    Ответ написан
    Комментировать
  • Как можно оптимизировать мой код php+mysql?

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

    <?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 для таблиц в двух БД?

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

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

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

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

    nowm
    @nowm
    Насчёт по кускам — не могу сказать, но большой дамп БД можно импортировать из командной строки. Например, если сохранить/закачать дамп в /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 (ну или тот, который вы ей укажете) для заданной базы данных.

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