Ответы пользователя по тегу MySQL
  • Как в Drupal 10 массово проставить noindex для >1000 страниц?

    @alexalexes
    писать скрипт, который будет вытаскивать по запросу необходимые записи, далее для каждой записи разбирать сер.массив, находить нужное значение, изменять его и затем записывать новый массив с изменённым значением в БД

    Именно так. Но лучше найти вариант вытаскивать список статей с помощью объектной модели движка самой CMS, и каммитить сохранение статьи тоже методом из объекта CMS (и не забыть еще дернуть метод очистки кэша страницы, или всего сайта, или части его контекста).
    Если у вас сайт высоконагруженный, то лучше разобраться с объектами CMS. Если нет, или не охота углубляться под капот CMS, то можно самописным скриптом поправить записи в базе, и не забыть сбросить кэш уже в панели управления CMS.
    PS: С drupal не знаком, но в любой CMS проблема будет решаться именно так.
    Ответ написан
    Комментировать
  • Как связать таблицы по одному столбцу и посчитать сумму?

    @alexalexes
    /* создание подготавливаемого запроса */
    $stmt = $mysql->prepare("select sum(c.`Price`) as price_sum
                                 from `dogovor` d
                                 join `catalog` c on c.`Id_Product` = d.`Id_Product`
                              where d.`id_client` = ?");
    /* связывание параметров с метками */
    $stmt->bind_param("i", $id_client);
    /* выполнение запроса */
    $result = $stmt->execute();
    /* выборка результатов */
    $row = $result->fetch_accos();
    if($row)
    {
      echo 'Общая стоимость всех заказов клиента: '.$row['price_sum'];
    }
    else
    {
      echo 'Запрос не дал результата!';
    }

    Ошибки в вашем решении:
    1) Небезопасная подстановка параметров в запросах. Неумение использовать в этой ситуации подготовленные запросы.
    2) Использование id_client как строкового параметра, если он состоит из цифр, то целесообразно использовать целочисленный тип в колонке таблицы.
    3) Неиспользование агрегированных функций среды sql. Зачем на php плодить лишние циклы, когда сумму можно посчитать "из коробки" средствами самой СУБД? Она под это заточена.
    4) Связывание таблиц по наименованию сущности, а не по его идентификатору. Рационально связывать по id_product вместо name_product.
    Ответ написан
    Комментировать
  • Как кучу файлов с дампами таблиц соединить в один?

    @alexalexes
    Каждая табличка - это отдельный файл SQL.

    Это не таблица, это "чертеж" таблицы.
    Соберете все файлики в несколько больших с помощью какого-нибудь файлового менеджера, то можете phpMyAdmin скармливать чертеж на постройку сразу нескольких объектов СУБД, а не только одной таблички.
    Ответ написан
    4 комментария
  • Как осуществить проверку по дате в MYSQL?

    @alexalexes
    не больше двух раз в неделю

    Формулировка требует некоторых уточнений.
    Если лимит определен на календарную неделю, и счет обращений сбрасывается в понедельник, то нужен такой запрос проверки:
    select count(*) as cnt -- кол-во записей на этой неделе
    from record as r
    where r.user_id = ? -- пользователь
      -- дата текущего понедельника (от curdate()) <= r.add_date < дата следующего понедельника (от curdate())   
      and r.add_date >= subdate(curdate(), interval weekday(curdate()) day) -- текущ. понедельник с точки зрения curdate()
      and r.add_date < subdate(curdate(), interval weekday(curdate()) - 7 day) -- следующий понедельник с точки зрения curdate()

    Если лимит определен просто на интервал 7 дней, то такой запрос:
    select count(*) as cnt -- кол-во записей за последние 7 дней
    from record as r
    where r.user_id = ? -- пользователь
      and r.add_date > subdate(curdate(), interval 7 day)
    Ответ написан
    5 комментариев
  • Как подсчитать и вывести количество совпадений значения в строке при запросе в MySQL?

    @alexalexes
    Если у вас MySQL 8, то дубликаты телефонов можно найти тривиально:
    select A.*
      from (SELECT `date`, `name`, phone, count() over (partition by phone) phone_count
            FROM `myusers`
    ) A
    where A.phone_count > 1
    ORDER BY date ASC

    Если MySQL 5, то придется немного по сложнее:
    select `date`, `name`, phone, p.phone_count
    from `myusers` as mu
    join (SELECT phone, count(*) phone_count
             FROM `myusers`
             group by phone) p on p.phone = mu.phone
    where p.phone_count > 1
    ORDER BY mu.date ASC
    Ответ написан
    4 комментария
  • Как сгруппировать по убыванию?

    @alexalexes
    Если у вас MySQL 8+ версия, то список чатов получается тривиально - используя оконную функцию:
    SELECT A.*
    from (
    SELECT class_messages.id as class_messages_id,
          `class_messages_chat`.`to_user_id`,
           `class_messages_chat`.`from_user_id`,
           `class_users`.`id`,
           `class_users`.`user_status`,
           `class_users`.`online`,
           `class_messages_chat`.`chat_id`,
           `class_messages_chat`.`ad_id`,
           `class_ads`.user_id,
           `class_ads`.title,
           row_number() over (partition by `class_messages_chat`.`chat_id` order by class_messages.id desc) rn -- этой функцией нумеруем в пределах чата сообщения
        FROM `class_messages_chat`
        inner join `class_ads` on `class_messages_chat`.ad_id = `class_ads`.id
        INNER JOIN `class_users` ON `class_users`.`id`=`class_ads`.`user_id`
        inner join `class_messages` on `class_messages_chat`.`chat_id` = `class_messages`.`chat_id`
    WHERE (   `class_messages_chat`.`to_user_id` = 133337
           or `class_messages_chat`.`from_user_id` = 133337)
    and class_messages_chat.chat_id is not null
    ) A
    where A.Rn = 1 -- оставляем, все что с первым номером счетчика
    order by A.class_messages_id desc

    В mySQL 5.7
    SELECT A.*
    from (
    SELECT
          `class_messages_chat`.`to_user_id`,
           `class_messages_chat`.`from_user_id`,
           `class_users`.`id`,
           `class_users`.`user_status`,
           `class_users`.`online`,
           `class_messages_chat`.`chat_id`,
           `class_messages_chat`.`ad_id`,
           `class_ads`.user_id,
           `class_ads`.title,
      IF(@prev <>  `class_messages_chat`.`chat_id`, @rn:=1,@rn), @prev:= `class_messages_chat`.`chat_id`, @rn:=@rn+1 AS rn -- этой функцией нумеруем в пределах чата сообщения
        FROM `class_messages_chat`
        inner join `class_ads` on `class_messages_chat`.ad_id = `class_ads`.id
        INNER JOIN `class_users` ON `class_users`.`id`=`class_ads`.`user_id`
        inner join `class_messages` on `class_messages_chat`.`chat_id` = `class_messages`.`chat_id`,
         (SELECT @rn:=1)rn,
         (SELECT @prev:=-1)prev
    WHERE (   `class_messages_chat`.`to_user_id` = 133337
           or `class_messages_chat`.`from_user_id` = 133337)
    and class_messages_chat.chat_id is not null
    order by class_messages.id desc
    ) A
    where A.Rn = 1 -- оставляем, все что с первым номером счетчика

    Второй вариант для 5.7, если некорректно будет работать сортировка.
    select B.*
    from (SELECT A.*,
               IF(@prev <> A.chat_id, @rn:=1,@rn), @prev:= A.chat_id, @rn:=@rn+1 AS rn -- этой функцией нумеруем в пределах чата сообщения
    from (
    SELECT
          `class_messages_chat`.`to_user_id`,
           `class_messages_chat`.`from_user_id`,
           `class_users`.`id`,
           `class_users`.`user_status`,
           `class_users`.`online`,
           `class_messages_chat`.`chat_id`,
           `class_messages_chat`.`ad_id`,
           `class_ads`.user_id,
           `class_ads`.title
        FROM `class_messages_chat`
        inner join `class_ads` on `class_messages_chat`.ad_id = `class_ads`.id
        INNER JOIN `class_users` ON `class_users`.`id`=`class_ads`.`user_id`
        inner join `class_messages` on `class_messages_chat`.`chat_id` = `class_messages`.`chat_id`
    WHERE (   `class_messages_chat`.`to_user_id` = 133337
           or `class_messages_chat`.`from_user_id` = 133337)
    and class_messages_chat.chat_id is not null
    order by class_messages.id desc
    ) A,
    (SELECT @rn:=1)rn,
         (SELECT @prev:=-1)prev
    ) B
    where B.Rn = 1 -- оставляем, все что с первым номером счетчика
    Ответ написан
  • Можно ли делать запрос к промежуточной таблице многие-ко-многим для извлечения конкретной информации?

    @alexalexes
    Что-то много философии про можно/нельзя. Нужно влоб погуглить "join-ы в sql" и набросать что-то такое:
    select b.*, -- все колонки по книге
            a.* -- все колонки по сведениям об авторе
    from books as b
    join book_autor as ba on b.book_id = ba.book_id  -- если у книги может не быть авторов, то нужен left join
    join autors as a on ba.autor_id = a.autor_id -- если у книги может не быть авторов, то нужен left join
    where b.book_id = :book_id -- сюда подставляем параметр нужной книги.
    Ответ написан
  • Запрос на удаление, что нём не так?

    @alexalexes
    DELETE FROM joom_user_usergroup_map -- m - забудьте про алиасы в делетах, пока работаете в MariaDB !
    WHERE NOT EXISTS (SELECT * FROM joom_users u WHERE u.id = joom_user_usergroup_map.user_id -- будьте добры указывать имя таблицы полностью, которую обрабатывает delete
    );
    Ответ написан
    3 комментария
  • Как вывести по 5 максимальных значений в строках из каждой категории?

    @alexalexes
    select A.*
           from
           (
           select B.*,
                  row_number() over (partition by B.nomin_id order by B.countUser desc) rwnm -- партишином задаем область счета строк, а сортировкой - порядок счета
                  from
                  (
                  SELECT count(user_id) countUser, nomin_id,  film_id
                  FROM votes
                  GROUP BY  film_id , nomin_id
                  ) B
           ) A
    where rwnm <= 5 -- берем по этому количеству от каждого локального счета

    См. оконную функцию row_number для MySQL 8+ версия, либо MariaDB. Но учтите, что MySQL 5 не поддерживает оконки, в ней свои костыли есть в виде использования переменных с @.

    Но всего в таблице 25 000 строк запрос выполняется 30 секунд, а потом крах.

    Индексы на каждый из атрибутов user_id, nomin_id, film_id нужно создать, тогда будет пару сотен миллисекунд выполняться.
    Ответ написан
  • Можно ли составить mysql запрос с выборкой не определенного множества полей?

    @alexalexes
    Можете, но свойства будут не в пределах строки (горизонтально), а в пределах колонки (вертикально).
    С помощью SQL вы можете, не забивая микроскопом гвозди, извлечь вот такой результат:
    Ручка; Цвет; Синий;
    Ручка; Цена; 10
    Нож; Материал; Дерево;
    Нож; Цена; 200;
    Нож; Рукоятка; Дерево; 
    Нож; Фото; Есть;

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

    @alexalexes
    если в Mysql какие-то строки смогут загрузиться, а какие-то - нет,

    Не бывает тут вариативности, даже случайной.
    Можете внести одну или несколько строк запросом insert (или несколькими insert-ами по одной строке, а может еще где-то update-ом или delete-ом пробежитесь). Но фиксация этих действий будет происходить, если после запросов на вставку/изменение/удаление явно будет отправлена команда commit или rollback (а может и не явно, см. что такое autocommit).
    До поступления этих транзакционных команд ваши изменения будут копиться во временном буфере СУБД и будет блокировать объекты СУБД до того момента, пока не поступит транзакционная команда (или не наступит ситуация для autocommit). Для других пользователей СУБД изменения заблокированных объектов будет недоступно, только чтение той версии объекта, которая была до наложения блокировки (не всегда таков принцип, см. что такое уровни изоляции транзакций).
    Ответ написан
    Комментировать
  • Как сделать DISTINCT для двух полей?

    @alexalexes
    Нужно воспользоваться какой-нибудь агрегирующей функцией (min, max, avg, sum, count и т.д.) и по ней группировать.
    Зависит от того, какой результат хотите получить.
    SELECT brand_name, any_function(indeks_shvidkosti) as func_indeks_shvidkosti
      FROM tires
    group by brand_name
    ORDER BY brand_name ASC

    ПС: Если вы используете Distinct, значит где-то что-то не учли в требованиях к структуре данных, что вам приходится избавляться от дублей.
    Ответ написан
  • Как продублировать значения из одной строки в другую внутри таблицы?

    @alexalexes
    Измените запрос update, который делает обновление в этой таблице, чтобы в where подставлялся не конкретный id строки, а было условие, которое соответствует всем строкам, которым нужно сделать обновление.
    Ответ написан
    Комментировать
  • Как составить запрос выборки баллов?

    @alexalexes
    Вас просят написать что-то с клаузой Having.
    Если читать вопрос влоб, то вас просят сгруппировать выборку по атрибуту point_lifetime, и проанализировать сумму баллов в having:
    select point_lifetime, sum(point)
    from t
    group by point_lifetime
    having sum(point) <= N -- N нужно рассматривать как входной параметр запроса

    Тут бы логично по user_id что-то интересное искать, но в задачке point_lifetime. В общем, нужно видеть с какими исходными данными работаете, чтобы понять, что вы группируете.
    Дополнено:
    -- тестовые данные
    with t as (select  1 id, 1 user_id, 10 point, from_unixtime(1701533439) point_lifetime
    union
    select  2 id, 1 user_id, 10 point, from_unixtime(1701533439) point_lifetime
    union
    select  3 id, 2 user_id, 10 point, from_unixtime(1001433439) point_lifetime
    union
    select  4 id, 1 user_id, -10 point, from_unixtime(1901533439) point_lifetime
    union
    select  5 id, 1 user_id, 100 point, from_unixtime(1801733439) point_lifetime)
    
    -- экспериментальный запрос
    select a.*
    from (select t.id,
           t.user_id,
           t.point,
           t.point_lifetime, 
           sum(t.point) over (partition by t.user_id) sum_point,
           row_number() over (partition by t.user_id order by case when t.point > 0 then 0 else 1 end, -- продавливаем положительные баллы вверх
                                                                    t.point_lifetime desc) rn -- локальный счетчик записей по пользователю, чтобы понять, какую запись хотите выдернуть из подзапроса, нужно сделать так, чтобы интересуемые записи имели rn = 1
    from t
      where point_lifetime > str_to_date('02.12.2023 10:00:00', '%d.%m.%Y %H:%i:%s') -- считаем, что отметка "сейчас" - это 02.12.2023 10:00:00
    ) a
    where a.sum_point >= 30 -- берем все то, что больше минимальной суммы
      and a.rn = 1 -- берем только интересуемую запись по каждому пользователю
     order by id
    Ответ написан
  • Какой из запросов более оптимальный?

    @alexalexes
    Использование in нужно избегать, если вы в нем не прописываете статичные значения, или известное кол-во переменных с альтернативными вариантами.
    Более живенько будет работать с обычными join:
    SELECT u.* FROM users u 
    JOIN pp1 ON u.id = pp1.id and pp1.organization = 1 
    union
    SELECT u.* FROM users u 
    JOIN pp2 ON u.id = pp2.id and pp2.organization = 1
    ORDER BY name
    LIMIT 15

    Или использовать exists в where - так вы планировщику даете понять, что можно не искать все варианты в подзапросе, а найти один, подходящий под условие.
    SELECT u.* FROM users u 
    where exists (select 1 from pp1 where u.id = pp1.id and pp1.organization = 1 )
         or exists (select 1 from pp2 where u.id = pp2.id and pp2.organization = 1 )
    ORDER BY u.name
    LIMIT 15

    PS: Наличие однотипных таблиц pp1 и pp2 говорит о наличии ошибки проектирования базы данных. От видов сущностей pp у вас не должно плодится таблицы с pp. У таблицы pp должно быть свойство вида сущности, например pp_type_id и таблица-справочник pp_type - куда вы будете добавлять новый вид сущности, а не создавать новую таблицу.
    Тогда запрос у вас будет выглядеть так:
    SELECT u.*
       FROM users u 
    where exists (select 1 
                  from pp
                 where u.id = pp.id
                   and pp.organization = 1 
                   and pp.pp_type_id in (1, 2) -- собственно, в таблице содержится все, что раньше находилось в pp1 и pp2, появилось поле, которое отличает разновидности записей
              )
    ORDER BY u.name
    LIMIT 15
    Ответ написан
    3 комментария
  • Некоторые бинарные строки не определяются по оператору LIKE, проблема не дебажится, в чём может быть причина?

    @alexalexes
    Вероятно, проблема в состоянии индекса на поле info_hash_v2 (вы же используете индексы в базе для ускорения поиска?). Запустите перерасчет индекса для устранения глюка.

    PS:
    $info_hash_where = "WHERE tor.info_hash = '$info_hash' OR tor.info_hash_v2 LIKE '$info_hash%'";

    Ай-ай-ай. Склейка параметров запроса с текстом запроса, ой не хорошо. Не хотите познакомиться с подготовленными запросами?
    Или нормально исследуйте класс объекта DB, вдруг там есть нормальный метод bind_param, а то вдруг используете инструмент не до конца по назначению.
    Ответ написан
  • Как сделать расчет по нескольким группам в SQL?

    @alexalexes
    Примерно так, но без понимания матрицы переходов статусов написать кейсы в каунтах не получится корректно с моей стороны.
    select t.user_id,
              count(*) as calls,
              count(case when t.old_status = 'Open' then 1 end) as opened,
              count(case when t.new_status = 'Assigned' then 1 end) as assigned
              ....
     from log_ticket_statuses t
    group by  t.user_id

    PS: Хорошо бы статусы вынести в отдельный справочник, не комильфо использовать его как строку.
    PPS: Почему выбрали модель использования колонок old_stasus/new_status, почему бы не делать трек истории через связанные строки id_next? Туда неограниченное количество переходов статусов можно запихнуть на одного пользователя и по маркеру id_next is null удобно составлять запросы все ли треки завершены, и какие текущие статусы у пользователей с задачами.
    history_id, ticket_id, user_id, status_id, next_history_id
    1, 1, 1, 1, 2
    2, 1, 1, 2, null
    3, 1, 2, 1, null
    4, 2, 2, 1, 5
    5, 2, 2, 2, null
    Ответ написан
    Комментировать
  • Как сделать изменение поля из одной таблицы в другую с сопоставлением по другому полю?

    @alexalexes
    Так-то подзапрос "знает" о таблице b_uts_crm_company из основного запроса в секции where (но не наоборот).
    UPDATE b_uts_crm_company 
    SET UF_CRM_1696186645 = (SELECT b_crm_company.REVENUE
                            FROM b_crm_company
                            where b_crm_company.ID = b_uts_crm_company.VALUE_ID)

    Есть еще момент, что подзапрос не должен возвращать больше 1 значения, тогда он выполнится корректно.
    Если что-то нужно вставить из связанной таблицы любой ценой, то используйте max( b_crm_company.REVENUE) или min( b_crm_company.REVENUE).
    Ответ написан
  • Как сохранить порядок номеров в запросе?

    @alexalexes
    Припаркую свой велик.
    Из in нельзя вытащить сведения о порядке, он просто дает ответ "включает" или "не включает" в себя значение.
    Если СУБД позволяет, то для сеанса создаете временную таблицу, где добавляете все необходимые атрибуты (в данном случае порядок записей), и пользуетесь ей. Если не позволяет, то всегда можно написать подзапрос и он тоже будет работать почти как временная таблица:
    SELECT *
      FROM table
      join (select 1 id, 7 val union
              select 2, 8 union
              select 3, 5 union
              select 4, 4 union
              select 5, 1)  tmp on table.id = tmp.val
    order by tmp.id
    Ответ написан
    Комментировать
  • Как выбрать последнюю запись с group by?

    @alexalexes
    Как-то так:
    select b.*
    from (select a.*, -- атрибуты по агрегированной статистике
                -- атрибуты записей-кандидатов на последние записи
                 s.id id_2,
                 s.product_id product_id_2,
                 s.warehouse_id warehouse_id_2, 
                -- ......
                 row_number() over (partition by s.product_id, s.warehouse_id, s.date order by s.id desc) r_num -- нумеруем кандидатов, по порядку id в статистике
    from (select
             product_id,
             size,
             warehouse_id,
             max(date) later_date, -- поздняя дата
             sum(orders) as orders
        from stats
      where date(`date`) >= '2023-09-01' and date(`date`) <= '2023-09-04'
       group by product_id, warehouse_id, size
    )  a
    left join stats s on s.date = a.later_date
                     and s.product_id = a.product_id
                     and s.warehouse_id = a.warehouse_id) b
    where b.r_num = 1 -- берем первого кандидата (нужно проверить, будет ли null, если left join не присоединит запись)

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