Ответы пользователя по тегу MySQL
  • Проблема с regexp на mysql 8.0.26?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Согласно MySQL 5.7 Reference Manual / ... / Regular Exp...

    [[:<:]], [[:>:]]

    These markers stand for word boundaries.


    Согласно MySQL 8.0 Reference Manual / ... / Regular Exp...

    The Spencer library supports word-beginning and word-end boundary markers ([[:<:]] and [[:>:]] notation). ICU does not. For ICU, you can use \b to match word boundaries; double the backslash because MySQL interprets it as the escape character within strings.


    Так что подход правильный.

    Но Вы не учитываете, что слэш надо квотить как для PHP, так и для MySQL. Т.е. их должно быть не два, а четыре.
    Ответ написан
    Комментировать
  • SQL запрос как создать 9999 записей с итерацией чисел?

    @Akina
    Сетевой и системный админ, SQL-программист.
    1. Вот оно надо, каждый раз заботиться об установлении значений полей DATE_CREATE/DATE_UPDATE?

    ALTER TABLE tablename
    MODIFY COLUMN date_create TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    MODIFY COLUMN date_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;

    Всё, теперь они сами будут заботиться о правильном значении. Точнее, сервер будет об этом заботиться. Если, конечно, коду не придёт в голову устанавливать значения этих полей явно (нафига бы только - лишняя же работа).

    2. Для выполнения операции используем рекурсивный CTE (помним, что поля DATE_CREATE/DATE_UPDATE сами заботятся о правильном значении, как и автоинкрементный первичный ключ):

    INSERT INTO tablename (active, user_id, value)
    WITH RECURSIVE
    cte AS ( SELECT 1 num
             UNION ALL
             SELECT num + 1 FROM cte WHERE num < 9999 )
    SELECT 1, 1, num
    FROM cte;


    Возможно, предварительно надо установить достаточное значение для сессионной переменной:SET SESSION cte_max_recursion_depth = 10000;

    Если версия MySQL старая и не понимает CTE, то

    INSERT INTO tablename (active, user_id, value)
    SELECT 1, 1, 1 + t1.num + t2.num * 10 + t3.num * 100 + t4.num * 1000 AS value
    FROM       (SELECT 0 num UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t1
    CROSS JOIN (SELECT 0 num UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t2
    CROSS JOIN (SELECT 0 num UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t3
    CROSS JOIN (SELECT 0 num UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t4;
    HAVING value <= 9999;
    Ответ написан
    1 комментарий
  • Как правильно спроектировать связь БД между двумя товарами?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Вопрос: как мне правильно спроектировать таблицу для связей между этими товарами. Чтобы каждый из этих товаров ссылался друг на друга?

    CREATE TABLE groups_of_goods (
        group_id BIGINT UNSIGNED NOT NULL,
        product_id BIGINT UNSIGNED NOT NULL,
        PRIMARY KEY (group_id, product_id),
        FOREIGN KEY fk_product (product_id) REFERENCES product (product_id)
    );


    Соответственно если твои труселя входят в одну группу и ссылаются друг на друга, то в таблице будут 2 записи - (123, 10) и (123, 15).

    Кстати, такая схема обеспечивает и принцип "вассал моего вассала ...". Т.е. "пиджак малиновый" может ссылаться на "брюки малиновые" (через группу 456) и "пиджак в полоску" (через группу 789), но при этом последние два друг на друга ссылаться не будут, ибо разные группы.
    Ответ написан
    2 комментария
  • Какой должен быть тип строки в БД для номера телефона, состоящего из одних цифр?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Насколько я понимаю, надо не только чистить для сохранения и последующего удобного использования, но и возвращать их на место при передаче данных обратно на сайт для отображения или корректировки, верно?

    Если так, то предлагаю с самим полем ничего не делать, пусть себе будет текстовое и со всякой фигнёй. А для использования создать ещё одно, вычисляемое, поле, в которое класть очищенное и приведённое к числовому значение.

    Т.е. если сейчас имеется
    CREATE TABLE users (
        ...
        phone VARCHAR(255),
        ....
    );

    то выполняем
    ALTER TABLE users ALGORITHM = INPLACE
        ADD COLUMN phone_num BIGINT UNSIGNED AS (REGEXP_REPLACE(phone, '[^0-9], ''')) VIRTUAL,
        INDEX idx_phone_num (phone_num);


    Что это даёт?

    Выражение вычисляемого поля чистит строковое значение от всего, что не цифра, после чего значение преобразуется в тип данных поля, т.е. BIGINT UNSIGNED. Отлично, число получено.

    Поле объявлено как VIRTUAL. Это означает, что его значение не хранится в таблице, а вычисляется каждый раз, когда оно потребуется. Но мы создаём по этому полю индекс. Соответственно сервер может не вычислять значение, а извлечь его из индекса. Ещё лучше, если это поле будет включено в композитные индексы вместо поля phone - тогда вероятность, что сервер будет извлекать значение из индекса, увеличится.

    Конечно, можно было бы сделать поле STORED и не морочиться с индексом. Но тогда процесс изменения структуры таблицы будет достаточно длительным, потому что такая операция не может быть INPLACE и требует COPY. Впрочем, это может оказаться более подходящим в определённых условиях.

    Да, следует помнить, что в это поле нельзя записывать значения ни при INSERT, ни при UPDATE - такая операция приведёт к ошибке. Так что про запросы без указания списка полей придётся забыть... ну и SELECT * - тоже забыть.
    Ответ написан
    Комментировать
  • Как правильно сделать Mysql "точный" like запрос?

    @Akina
    Сетевой и системный админ, SQL-программист.
    1. Использовать не LIKE, а функции поиска подстроки (INSTR или LOCATE)
    2. Задать требуемый collation (вплоть до BINARY)
    Ответ написан
    Комментировать
  • Можно ли доверять коду ошибки Mysql?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Server Error Message Reference

    Error number: 1452; Symbol: ER_NO_REFERENCED_ROW_2; SQLSTATE: 23000

    Message: Cannot add or update a child row: a foreign key constraint fails (%s)

    InnoDB reports this error when you try to add a row but there is no parent row, and a foreign key constraint fails. Add the parent row first.

    Данная ошибка всегда порождается событием неудачной проверки ограничения ссылочной целостности. Так что можете смело доверять причине.

    Или перед вставкой нужно создать запрос на проверку существования такой записи в связующей таблице?

    А вот задайте себе вопрос - откуда возьмётся то самое значение user_id = 3, которое Вы собираетесь вставить? я не вижу иного способа получить данное значение кроме как сделать запрос в родительскую таблицу. А коли из неё вернётся нужное значение - то запись, из которой взято это значение, совершенно очевидно в таблице есть.
    Ответ написан
    5 комментариев
  • Как лучше реализовать динамические поля?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Принципиально есть два варианта.

    Первый, уже указанный в комментарии от Ерлан Ибраев, EAV.

    Второй - сериализация динамических параметров. JSON, XML и т.п.

    В обоих вариантах по мере доработки параметры могут легко переноситься из динамического хранилища в постоянное. Также оба варианта допускают и множественные значения - в том числе в постоянном и временном хранилищах одновременно (что к тому же даёт потенцию одноступенчатой приоритезации).

    Явный недостаток обоих способов - использование унифицированного типа данных в таблице (как минимум строковый, а данные запросто могут потребовать ещё и binary collation) вне зависимости от реального типа сохраняемых данных. Ещё один явный недостаток - необходимость выноса контроля целостности на клиентский уровень.

    Что выбрать? а вот это уже зависит от того, что именно делается с данными.

    В любом случае - следует сразу отбросить идею динамической корректировки структуры хранения (изменения структуры таблиц, добавления таблиц) из клиентского кода.
    Ответ написан
  • Как правильно делать JOIN таблиц?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Есть ли разница джоинов этих двух строк, с точки зрения нагрузки на БД?

    Оба запроса - INNER JOIN. Если не используется STRAIGHT_JOIN, то сервер гарантированно построит для обоих запросов один и тот же план выполнения.

    Более того, если Вы почитаете внимательно, как именно сервер выполняет и оптимизирует запросы, то узнаете, что сервер фактически оба этих запроса приводит к картезианскому произведению с отбором. Вернее, препроцессинг выглядит так, словно сервер выполняет такое приведение.
    Ответ написан
    Комментировать
  • Как удалить содержимое БД Mysql?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Самое простое - сделать бэкап только структуры, без данных (mysqldump --no-data, https://dev.mysql.com/doc/refman/8.0/en/mysqldump....), удалить БД, восстановить. Само собой, не забыть забэкапить процедуры/триггеры/прочее.
    Ответ написан
    Комментировать
  • Как правильно указать значение SET?

    @Akina
    Сетевой и системный админ, SQL-программист.
    пробовал делать это через "?" и "*" , но не получается

    Для того. чтобы вставляемое значение не квотировалось, следует использовать плейсхолдер из 2 знаков вопроса:
    PreparedStatement ps = plugin.SQL.getConnection().prepareStatement("UPDATE PlayerBlocks SET ?? = ? WHERE UUID=?");

    Тогда первый передаваемый параметр - имя поля для обновления. 2 и 3 соответственно значения (новое и для условия отбора).
    Ответ написан
    4 комментария
  • Какой необходимо составить запрос mysql для генератора случайного поста по рейтингу?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Ну типа так.
    WITH cte AS (
        SELECT *, SUM(rating) OVER (ORDER BY id) cum_rating
        FROM table
    )
    SELECT
    FROM cte t1
    JOIN (SELECT RAND() * MAX(cum_rating) rnd_rating
          FROM cte) t2 ON t2.rnd_rating BETWEEN t1.cum_rating - t1.rating AND t1.cum_rating

    Реально, конечно, не BETWEEN, а два неравенства, по верхней границе - строгое.
    Ответ написан
  • Помощь с запросом MySQL?

    @Akina
    Сетевой и системный админ, SQL-программист.
    SELECT car_model.* 
    FROM car_model 
    JOIN car_mark ON car_model USING (id_car_mark)
    WHERE car_mark.name='AC'
    Ответ написан
    Комментировать
  • Почему mysql запрос не выводит запись где кол-во схождений 0?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Список требуемых id должен быть источником данных запроса, причём базовым. И при возможном отсутствии связанных записей связывание должно быть внешнее.
    SELECT service.id AS service_id, COUNT(subs.id) AS c
    FROM ( SELECT 13 id UNION 
           SELECT 74    UNION
           SELECT 71    UNION
           SELECT 72 ) AS service
    LEFT JOIN Subscriptions subs ON subs.service_id = service.id AND subs.msisdn=992777757031
    GROUP BY service.id
    ORDER BY c DESC;
    Ответ написан
  • Как убрать лишние значения при JOIN по неуникальным полям?

    @Akina
    Сетевой и системный админ, SQL-программист.
    WITH 
    cte1 AS ( SELECT id, name, ROW_NUMBER() OVER (ORDER BY name) rn
              FROM t1 ),
    cte2 AS ( SELECT id, name, ROW_NUMBER() OVER (ORDER BY name) rn
              FROM t2 )
    SELECT cte1.id id1, cte1.name name1, cte2.id id2, cte2.name name2
    FROM cte1
    JOIN cte2 USING (rn, id);


    https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=b5a064...

    Хотя, конечно, бред рафинированный. С точки зрения логики.
    Ответ написан
  • Как правильно указать WHERE если делаем LEFT JOIN?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Порядок кляуз в запросе чётко определён в документации, и никаких изменений и перестановок не предполагает (исключение - кляуза INTO при помещении результата в переменную, для которой описано 3 разных местоположения).
    SELECT u.*, t.type_name, t.description 
    FROM users AS u 
    LEFT JOIN type_user AS tu ON tu.id_user = u.id 
    LEFT JOIN types AS t ON tu.id_type = t.id
    WHERE u.id = 1;
    Ответ написан
    Комментировать
  • Можно ли и как составить запрос в БД, чтобы получить статистические данные?

    @Akina
    Сетевой и системный админ, SQL-программист.
    WITH RECURSIVE
    cte1 AS ( SELECT MIN(DATE(create_date)) mindate, MAX(DATE(create_date)) maxdate
              FROM shops
              UNION ALL
              SELECT MIN(DATE(create_date)) mindate, MAX(DATE(create_date)) maxdate
              FROM users ),
    cte2 AS ( SELECT MIN(mindate) mindate, MAX(maxdate) maxdate
              FROM cte1 ),
    dates AS ( SELECT mindate thedate, maxdate
               FROM cte2
               UNION ALL
               SELECT thedate + INTERVAL 1 DAY, maxdate
               FROM dates
               WHERE thedate < maxdate ),
    shopstat AS ( SELECT DATE(create_date) thedate, COUNT(*) cnt
                  FROM shops
                  GROUP BY thedate ),
    userstat AS ( SELECT DATE(create_date) thedate, COUNT(*) cnt
                  FROM users
                  GROUP BY thedate )
    SELECT thedate `date`, 
           COALESCE(shopstat.cnt, 0) shops, 
           COALESCE(userstat.cnt, 0) users
    FROM dates
    LEFT JOIN shopstat USING (thedate)
    LEFT JOIN userstat USING (thedate)


    Если нужны данные за определённый период - убрать cte1 и cte2, использовать границы диапазона в dates (поле maxdate - не нужно, убрать), добавить соотв. WHERE в остальные CTE.
    Ответ написан
    Комментировать
  • Как использовать CONVERT_TZ и AS?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Алиас выходного поля нельзя использовать во WHERE.

    Правильно - использовать копию вычисляющего значения выражения:
    SELECT event, 
           CONVERT_TZ(time, '+00:00', '-04:00') AS time1 
    FROM events 
    WHERE CONVERT_TZ(time, '+00:00', '-04:00') between '2021-06-24 00:00:00' AND '2021-06-24 23:59:59' 
      AND lang = 'ru'

    Расширение MySQL также допускает использовать алиас выходного поля в HAVING (при условии что в выражении не используются оконные функции):
    SELECT event, 
           CONVERT_TZ(time, '+00:00', '-04:00') AS time1 
    FROM events 
    WHERE lang = 'ru'
    HAVING time1 between '2021-06-24 00:00:00' AND '2021-06-24 23:59:59'
    Ответ написан
    Комментировать
  • Как выводить записи не раннее получаса и не позднее часа в sql?

    @Akina
    Сетевой и системный админ, SQL-программист.
    WHERE created_at BETWEEN CURRENT_TIMESTAMP - INTERVAL 1 HOUR
                         AND CURRENT_TIMESTAMP - INTERVAL 30 MINUTE
    Ответ написан
    Комментировать
  • Как вычесть у одного пользователя число и добавить его другому?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Лучше делать всё в одном запросе:
    UPDATE users u1
    CROSS JOIN users u2
    SET u1.amount = u1.amount - $summa,
        u2.amount = u2.amount + $summa
    WHERE u1.id = $client
      AND u2.id = $shop;


    Во-первых, один запрос - проще. Пусть даже сам запрос и сложнее.

    Во-вторых, если, например, в структуре таблицы имеется ограничение CHECK (amount >= 0), а сумма такова, что у клиента баланс уйдёт в минус - в таком случае ни одна из записей не будет изменена.
    Ответ написан
    Комментировать
  • Что не так с моим запросом?

    @Akina
    Сетевой и системный админ, SQL-программист.
    SELECT main.id,
           ( SELECT COUNT(*) 
             FROM news 
             WHERE main.id = news.main_id ) news,
           ( SELECT COUNT(*) 
             FROM articles
             WHERE main.id = articles.main_id ) articles
    FROM main
    ORDER BY news DESC;


    Предполагается, что main(id) есть уникальное поле (скорее всего первичный ключ).
    Ответ написан
    2 комментария