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

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

    Бред сивой кобылы (извиняюсь). Структура (точные имена полей, и в какой таблице какое поле) обязана быть заранее известна. Даже если используется псевдодинамическая структура на базе EAV или иным способом организованная.

    у меня есть 2 таблицы или допустим будет 3 ... Я хочу при выполнении insert into или update использовать эти таблицы

    MySQL не реализует одновременную вставку в более чем одну таблицу. А также не реализует использование запроса на вставку в CTE. Напрямую задача не решается.

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

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

    @Akina
    Сетевой и системный админ, SQL-программист.
    Рабочий ли вариант если просто через rsync передать всю папку /var/lib/mysql?

    Просто перенос файлов рабочей БД окончится тем, что файлы есть, а базы нет, потому что неоткуда взяться метаданным в системной базе данных. Вернее, всё ещё забавнее - базы вроде бы и нет (USE приводит к ошибке "БД не существует"), но её нельзя создать, потому что она есть. И то же самое с таблицами (SELECT/DROP/прочее нельзя, потому что таблицы нет, CREATE нельзя, потому что она есть).

    Метод сработает, если переносить все БД сразу, включая служебные. Но требуется точное соответствие исходной и конечной конфигураций - начиная с версии и сборки MySQL и кончая именами каталогов и именами/паролями учётных записей в операционной системе. Плюс перегенерация и переподключение сертификатов.

    Еще вариант с репликацией, но насколько я понял всё равно базу нужно блокировать на момент дампа, а это тоже какое-то время

    Решений без блокирования практически не существует. Нет, можно наколхозить реальное решение, которое обойдётся вообще без остановки, я даже представляю как именно (FEDERATED ENGINE + triggers), но подробно рассказывать не буду - для её реализации нужны достаточная квалификация и опыт, на одних советах шанс на удачу невелик, скорее всё поломается.

    Есть вполне себе документированные способы: Copying MySQL Databases to Another Machine. Особенно последний из описанных. Он предполагает минимальное время простоя при использовании штатных средств.
    Ответ написан
    Комментировать
  • Как вывести записи по ближайшей дате сгруппированные по категориям в MySQL 5.7?

    @Akina
    Сетевой и системный админ, SQL-программист.
    На версии 8+ как-то так:
    WITH cte AS (
        SELECT `events`.`id`,
               `events`.`date_public`,
    	   `event_categories`.`id` as `cat_id`,
    	   `event_categories`.`title` as `cat_title`
    	   ROW_NUMBER() OVER (PARTITION BY `event_categories`.`id` ORDER BY `events`.`date_public` ASC) rn
        FROM `events`
        INNER JOIN `event_category_joins` ON `events`.`id` = `event_category_joins`.`event_id` 
        INNER JOIN `event_categories` ON `event_categories`.`id` = `event_category_joins`.`event_category_id` 
        WHERE `events`.`date_public` >= '2024-04-05 19:00:00'
    --    WHERE `events`.`date_public` >= CURRENT_DATE
        )
    SELECT id, date_public, cat_id, cat_title
    FROM cte
    WHERE rn = 1;
    Ответ написан
    1 комментарий
  • Как кучу файлов с дампами таблиц соединить в один?

    @Akina
    Сетевой и системный админ, SQL-программист.
    type X:\sql_files_folder\*.sql | Z:\mysql_bin_folder\mysql -D database_name -u account_name -p

    И молись, чтобы в файлах не было синтаксических ошибок...
    Ответ написан
    Комментировать
  • Что такое кластерный индекс в mysql?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Кластерный индекс... это на самом деле понятие крайне виртуальное.

    Что такое обычный некластерный индекс? берём выражение индекса, считаем его значение для каждой записи, сортируем и пишем на диск. Получаем отдельную структуру, в которой выражение индекса сортировано. Когда потребуется искать заданное значение этого выражения, мы вместо просмотра от записи к записи сразу половинным делением быстренько найдём нужное значение, возьмём из него уникальный идентификатор записи, и обратимся за записью. Если в таблице 1000 записей, то для поиска заданного значения без индекса нам в среднем пришлось бы просмотреть 500 записей, а с индексом - всего 10.

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

    В MySQL (точнее, в используемом по умолчанию движке InnoDB) первичный индекс, во-первых, существует ВСЕГДА, во-вторых, определяется так (в статье, на которую дали ссылку, имеются неточности в пункте 2):
    1. Если первичный ключ задан явно, то его выражение является также и выражением кластерного индекса. Или иначе - первичный ключ и есть кластерный индекс.
    2. Если первичный ключ явно не задан, но в таблице имеется индекс, отвечающий всем следующим требованиям:
      • является уникальным
      • не является функциональным, в т.ч. не использует в выражении вычисляемые поля
      • не использует в выражении поля, которые определены как допускающие значение NULL

      то именно такой индекс используется в качестве первичного. А если таких индексов несколько, то используется первый по тексту запроса на создание таблицы
    3. Если не имеется ни того, ни другого - генерируется синтетический скрытый 6-байтовый номер записи, который и используется как первичный ключ. Следует отметить, что штатных способов доступа к этому значению не существует.


    Выглядит так, как будто это просто физическая сортировка данных по индексируемому полю.

    Фактически - именно так.

    Создаётся ли отдельная таблица или просто упорядочивается хранение существующих данных?

    Не создаётся. Но при изменении первичного индекса таблица полностью пересоздаётся с новым физическим порядком записей.

    Если данные упорядочиваются этим индексом, допустим по ID, то почему при select без сортировки данные могут возвращаться в произвольном порядке, а не отсортированные по ID по-умолчанию?

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

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

    ===

    PS. Кстати, правило выбора индекса, который будет использоваться в качестве кластерного, имеет неприятный побочный эффект. Если у некоторых полей, входящих в какие-то индексы, изменяется свойство NULLability, то это может привести к изменению того, какой из имеющихся индексов станет использоваться в качестве первичного по пункту 2. В результате мы получим невозможность использования INSTANT / INPLACE методов, и будет использован длинный COPY. Впрочем, ситуация такая крайне редка.
    Ответ написан
    2 комментария
  • Как оптимизировать запрос через Sqlalchemy?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Если отформатировать запрос, чтобы было видно, что происходит, получим следующее:

    SELECT coalesce(A.id, :coalesce_1) AS card_id, 
           coalesce(A.serial_number, :coalesce_2) AS card_number, 
           coalesce(B.name, :coalesce_3) AS prj_name, 
           coalesce(C.name, :coalesce_4) AS client_name, 
           coalesce(A.user_name, :coalesce_5) AS card_owner_name, 
           coalesce(A.user_phone_number, :coalesce_6) AS card_owner_phone_number, 
           coalesce(A.balance_current, :coalesce_7) AS current_balance, 
           coalesce(A.balance_bonus, :coalesce_8) AS bonus_balance, 
           coalesce(A.active, :coalesce_9) AS card_status, 
           coalesce(D.name, :coalesce_10) AS tariff_plan_name, 
           NULL AS anon_1 
    FROM cashless_card 
    JOIN company ON cashless_card.company_id = company.id 
    JOIN voc ON cashless_card.voc_id = voc.id 
    LEFT OUTER JOIN tariff_plan ON cashless_card.tariff_plan = tariff_plan.id 
    WHERE cashless_card.company_id IN (__[POSTCOMPILE_company_id_1])

    Почти очевидно, что в таблицах company, voc и tariff_plan поле по имени id - это первичный ключ... но хотелось бы подтверждения.

    Впрочем, видно, что для оптимизации запроса в таблице cashless_card крайне желательно наличие индекса (company_id). Следует также проверить эффективность индекса (company_id, voc_id, tariff_plan), и аналогичного с переставленными местами последними 2 полями (первым ставить то, которое более селективно).

    Кроме того, эффективность запроса сильно зависит от количества идентификаторов в списке, который передаётся в условие WHERE cashless_card.company_id IN (__[POSTCOMPILE_company_id_1]). Если их более десятка, есть смысл преобразовать этот список в набор данных (синтетическая UNION-таблица) и использовать как ещё один источник данных запроса.

    Ну и надо понимать, что наличие LIMIT/OFFSET только замедляет выполнение запроса. Причём чем больше OFFSET, тем сильнее это замедление.
    Ответ написан
    6 комментариев
  • Как правильно создать триггер?

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

    Задача не решается вычисляемым полем - в его выражении не допускается недетерминированных конструкций.

    Для решения задачи потребуется:
    1) trigger, который обработает начальные условия
    2) event procedure, которая обработает изменение значения по наступлении заданного момента времени

    Однако условия на формирование значения поля:

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

    Ну и, как правильно предлагает Константин Цветков, разумнее не изменять данные в таблице, а рассчитывать актуальный статус в момент, когда он требуется.
    Ответ написан
  • Зависит ли скорость записи в БД от количества в ней записей?

    @Akina
    Сетевой и системный админ, SQL-программист.
    1. Меняется/Зависит ли как-то скорость записи в таблицу БД от количества записей в таблице?

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

    2. Меняется/Зависит ли как-то скорость записи в таблицу БД от количества таблиц в БД?

    В MySQL - нет.

    3. Меняется/Зависит ли как-то скорость записи в таблицу БД от количества записей в соседних таблицах БД?

    В MySQL - нет. За исключением случая, когда пополняемая таблица имеет внешний ключ на другую таблицу. Но и в этом случае есть факторы, которые влияют на скорость записи гораздо сильнее.

    Да и разница в значении параметра "количество записей" должна быть на несколько порядков, чтобы разница по скорости записи была хотя бы не меньше точности измерения.
    Ответ написан
    Комментировать
  • Как составить сложную выборку из двух таблиц?

    @Akina
    Сетевой и системный админ, SQL-программист.
    SELECT address, 
           office, 
           t1.fio, 
           t2.counter, 
           CASE t2.counter
               WHEN 'counter1' THEN t1.counter1
               WHEN 'counter2' THEN t1.counter2
               WHEN 'counter3' THEN t1.counter3
               END value,
           t2.num,
           t1.date       
    FROM table1 t1
    JOIN table2 t2 USING (address, office)
    Ответ написан
    2 комментария
  • Почему не работает функция CURRENT_DATE() в триггере?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Почему не работает функция CURRENT_DATE() в триггере?

    Где-то что-то криво делаешь. У меня - работает.

    Хотя с моей точки зрения - у тебя кривая таблица. Нафига нужен триггер, если существует такая штука как "значение по умолчанию"?

    CREATE TABLE Sotrudnik (
      .....
      Date_Prinyatiya DATE DEFAULT (CURRENT_TIMESTAMP),
      .....
    );

    FIDDLE

    PS. Обращаю внимание - скобки в выражении значения по умолчанию обязательны!
    Ответ написан
    Комментировать
  • Почему при выборке первых трех строк, MySQL просматривает все записи таблицы?

    @Akina
    Сетевой и системный админ, SQL-программист.
    почему этот запрос просматривает так много записей?


    Таков принцип работы у этой СУБД. MySQL сначала выбирает все соответствующие условиям отбора записи, и только потом применяет к ним ограничение количества. Правда, обычно в запросе ещё присутствует и сортировка.

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

    Мне возвращается первые 3 строки.

    Хочу огорчить. Не три первые, а три случайным образом полученные. Но, поскольку в запросе отсутствуют выражения отбора и сортировки, то записи обычно выдаются в том порядке, в каком они располагаются в кластерном (первичном) индексе. Впрочем, кэширование данных и вымывание их из кэша данными конкурентных процессов могут это изменить.

    Вот, полюбопытствуйте: FIDDLE. Кстати, забавно, что без хинта оптимизатор выбирает вовсе даже не первичный индекс... Более того, в данном конкретном случае этому есть совершенно точное и строгое объяснение - попробуйте его найти.
    Ответ написан
    Комментировать
  • Можно ли такое реализовать с помощью MySQL?

    @Akina
    Сетевой и системный админ, SQL-программист.
    В рамках реляционной СУБД для описанной схемы есть несколько принципиально разных подходов.

    Первый - одна таблица и NULL в полях, отсутствующих у конкретного типа, её описывает Сергей Соловьев, вариант 2.
    Второй - использование EAV. Удобно, динамично, но проблемы с производительностью. Хотя из всех паттернов для реляционных СУБД он походу наиболее применим для фасетного поиска.
    Третий - использование сериализованного формата хранения. Например, хранение свойств объекта в формате JSON. Но, поскольку требуется поиск по атрибутам, в этом случае необходимо будет использовать внешний поисковый движок, возможно, даже ориентированный на фасетный поиск, или будет ужас как медленно.

    Использование MongoDB на описанном материале (буквально пара типов объектов) мне кажется не очень соответствующим решением. Хотя зависит от планируемого объёма данных.
    Ответ написан
    Комментировать
  • Как сделать запись в mysql с проверкой на уникальность с учётом времени из поля?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Легко решается триггером. Пример:

    - исходная таблица:
    CREATE TABLE users (
      id INT AUTO_INCREMENT PRIMARY KEY,
      username VARCHAR(100),
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP
    );


    - триггер:

    CREATE TRIGGER tr_users_bi
    BEFORE INSERT ON users
    FOR EACH ROW
    BEGIN
      DECLARE allowed_created_at DATETIME;
      DECLARE error_message_text VARCHAR(64);
      SELECT MAX(created_at) + INTERVAL 12 HOUR INTO allowed_created_at
      FROM users
      WHERE username = NEW.username;
      IF NEW.created_at < allowed_created_at THEN
        SET error_message_text = CONCAT('Регистрация возможна не ранее ', allowed_created_at);
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = error_message_text; 
      END IF;
    END


    DEMO fiddle

    PS. Надеюсь, получение сгенерированного сообщения об ошибке и показ его юзеру не составит проблемы..
    Ответ написан
    Комментировать
  • Как защитить данные от повреждения при INSERT?

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

    НЕТ.

    Возможно рассогласование данных, нарушение внутренней логики системы - но никак не повреждение.

    Повреждение БД - это физическое либо логическое разрушение базы данных. Физически - например, битый сектор в теле файла таблицы. Логический (с точки зрения БД, потому как с точки зрения аппаратуры это очень даже физическое) - например, неправильное считывание номера блока данных в файле индекса вследствие наличия на диске слабого бита.

    При отсутствии аппаратных ошибок, на чистой логике повреждение БД возможно либо из-за ошибочного/злонамеренного действия, либо из-за бага/сбоя программного обеспечения. В обоих случаях - как внутри SQL-сервера, так и снаружи (ОС, третий софт, ...).
    Ответ написан
    Комментировать
  • Можно ли составить mysql запрос с выборкой не определенного множества полей?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Можно ли одним запросом вывести все товары и свойства которые соответствуют данному товару
    id товара, название, свойство, значение......все свойства, значения.

    1; Ручка; Цвет; Синий; Цена; 10
    2; Нож; Материал; Цена; 200; Рукоятка; Дерево; Фото; Есть;

    Приведённый хреново форматированный текст не позволяет понять, где какое поле, и какой в нём тип данных.

    Полное ощущение, что автору требуется куча колонок (в смысле полей в структуре выходного набора). ТО есть если свойство одно, полей 4, если свойств 2, полей 6, и так далее...

    Если так, то требуемый результат называется "сводная таблица" (pivot table). Это совсем даже не реляционная структура, у которой нет и тени шанса попасть в стандарт, а потому практически ни одной СУБД штатно не поддерживается. Из популярных оно есть исключительно в SQL Server, да и там скорее исторически сложилось, чем нужно было.

    В MySQL / MariaDB ничего подходящего нет. И если кому оно реально необходимо, приходится возиться руками - хранимая процедура, динамический код... хотя на порядок разумнее оставить данные в EAV, а сводную таблицу строить на клиенте на его сервисе отчётов.

    Если же устроит выдача всех свойств, собранных в одно сериализованное поле (например, объект JSON) - то это обычная группировка и агрегация.
    Ответ написан
    Комментировать
  • Почему paragonie/easydb не пропускает слэши?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Первым делом хорошо посчитайте количество шагов экранирования. Один - сам MySQL, второй - видимо, используемый язык, но там ещё может вмешаться фреймворк или что ещё...

    Рекомендую включить временно General Log, и посмотреть, какой именно текст запроса получает MySQL. После чего отрихтовать текст запроса.

    В крайнем случае
    WHERE `value` LIKE CONCAT('%', CHAR(92), CHAR(92), '%')
    Ответ написан
    Комментировать
  • Как можно ускорить выполнение SQL запроса?

    @Akina
    Сетевой и системный админ, SQL-программист.
    если `prop_id` in (1,2,3,...) штук 20, может быть и больше, запрос выполняется 18 секунд, что прям совсем много.

    Критичное количество значений в таком списке - порядка 7..10, дальше действительно запрос резко замедляется.

    Workaround: создаём индексированную временную таблицу в памяти, записываем в неё набор значений, используем в запросе:

    CREATE TEMPORARY TABLE criteria (prop_id INT PRIMARY KEY) ENGINE = Memory
    SELECT prop_id FROM (VALUES ROW(1), ROW(2), ROW(3), ...) criteria (prop_id);

    и потом

    select `product_id`, count(*) as `cnt`
    from `product_prop`
    NATURAL JOIN criteria 
    group by `product_id`
    order by `cnt` desc


    Ну а ускорить ORDER BY по вычисляемому полю - вообще без шансов.
    Ответ написан
    4 комментария
  • Как отслеживать и выводить обновления базы данных?

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

    Да цельный воз и маленькая тележка.

    Обычно у каждой записи в БД имеется поле, в котором регистрируется штамп времени создания и/или последнего обновления записи. Так что всего и делов - запомнить, когда последний раз обращались к БД, и в следующий раз запросить те записи, что изменены позже. MySQL позволяет получать штамп времени с точностью до микросекунды.

    Или уж совсем по-простому. В каждой таблице держим синтетический первичный ключ-автоинкремент, и по аналогичной схеме запоминаем, каким было максимальное значение при предыдущем обращении.
    Ответ написан
    Комментировать
  • Как написать относительный путь в конфиге MySQL my.ini?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Как сделать в главном конфиге сервера MySQL относительные пути?

    Никак. Относительный путь требует наличия и, главное, детерминированности базового пути, от которого будет выполнен относительный отсчёт. А вот этой детерминированности гарантировать, увы, невозможно.

    Хотелось бы примерно такое (добавить переменную %BASEDIR%)

    Ну сделайте отдельный маленький конфигурационный файл с развёрнутыми путями, подключаемый инструкцией !include, и скриптик, который переформирует этот файл по введённой базовой директории.
    Ответ написан
  • Как составить запрос выборки баллов?

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

    Мне нужно найти запись на который у пользователя где у пользователя 1 накопится допустим 30 баллов, это запись id=5.


    WITH cte AS (
        SELECT *, SUM(point) OVER (ORDER BY point_lifetime) cum_sum
        FROM tablename
        WHERE user_id = 1 --   у пользователя 1 
        )
    SELECT *
    FROM cte
    WHERE cum_sum >= 30   --   накопится 30 баллов
    ORDER BY point_lifetime LIMIT 1;


    PS. Поскольку значения в point_lifetime неуникальны, то однозначного решения может и не быть. Во всяком случае до тех пор, пока не будет точно определён порядок записей при равенстве значений этого поля.
    Ответ написан
    Комментировать