Ответы пользователя по тегу MySQL
  • Можно ли делать запрос к промежуточной таблице многие-ко-многим для извлечения конкретной информации?

    @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 комментария
  • Как сделать расчет по нескольким группам в 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 не присоединит запись)

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

    @alexalexes
    В любой непонятной ситуации, которая не вызвана синтаксической ошибкой в PHP, поможет try-catch:
    try
    {
      $insert->execute($mediaData);
    }
    catch(Exсeption $e) // см. также типы исключений того модуля, который подключается к СУБД, можно реагировать только на них, если подставить нужный тип
    {
      // тут код, который выполняется в случае ошибки (не обязательно при взаимодействии с СУБД)
    }
    finally
    {
     // тут код, который выполняется при любом исходе
    }
    Ответ написан
    Комментировать
  • Где здесь ошибка?

    @alexalexes
    Палим учетные данные окружения сервера.
    $conn = new mysqli("localhost", "y96360rs_a", "xR&O&&37", "y96360rs_a");

    То, что вы в вопросе показали пример запроса с параметрами - он выполнен с помощью склейки строк - так делать нельзя. Подготовленные запросы пишутся так:
    $sql = "UPDATE `admins` SET `login` = ?, `pass` = ?, `name` = ?, `role` = ? WHERE `id` = ?";
    $stmt = $conn->prepare($sql);
    $stmt->bind_param('ssssi', $login, $pass, $name, $role, $id);

    Почувствуйте разницу.
    $id = $conn->insert_id;
    Зачем оно после запроса обновления, вы же знаете id изначально?
    Ответ написан
    Комментировать
  • Как сортировать позиции в БД по заданным весам?

    @alexalexes
    Ну, если буквально понимать задачу, и у вас нужная характеристика имеет конкретное значение, то сортировать список нужно так:
    select *
      from <таблица/соединенные таблицы для выборки товара> Tab1
    where <условия фильтрации>
    order by
    case
      when Tab1.Приоритетное_свойство = :входная_приоритетная_характеристика
      then 1 -- запись с приоритетным свойством получит повышенное очко для первичной сортировки
      else 0 -- запись с неприоритетным свойством получит пониженное очко для первичной сортировки
    end desc,
    <Другие условия сортировки (по дате добавления, имени и т.д.)>

    Если таких приоритетных характеристик несколько и они равнозначны, то можно сортировать по количеству подходящих под условие характеристик:
    select *
      from <таблица/соединенные таблицы для выборки товара> Tab1
    where <условия фильтрации>
    order by
    case
      when Tab1.Приоритетное_свойство_1 = :входная_приоритетная_характеристика_1
      then 1 -- запись с приоритетным свойством получит повышенное очко для первичной сортировки
      else 0 -- запись с неприоритетным свойством получит пониженное очко для первичной сортировки
    end
    + -- очки приоритетов складываем
    case
      when Tab1.Приоритетное_свойство_2 = :входная_приоритетная_характеристика_2
      then 1 -- запись с приоритетным свойством получит повышенное очко для первичной сортировки
      else 0 -- запись с неприоритетным свойством получит пониженное очко для первичной сортировки
    end
    + 
    -- ... и так далее все характеристики
     desc, -- 
    <Другие условия сортировки (по дате добавления, имени и т.д.)>

    Если приоритеты имеют иерархию, то через запятую в order by раскладываем case от важной характеристики, к менее важной.
    select *
      from <таблица/соединенные таблицы для выборки товара> Tab1
    where <условия фильтрации>
    order by
    case
      when Tab1.Приоритетное_свойство_1 = :входная_приоритетная_характеристика_1
      then 1 -- запись с приоритетным свойством получит повышенное очко для первичной сортировки
      else 0 -- запись с неприоритетным свойством получит пониженное очко для первичной сортировки
    end desc, -- эта более важная характеристика
    case
      when Tab1.Приоритетное_свойство_2 = :входная_приоритетная_характеристика_2
      then 1 -- запись с приоритетным свойством получит повышенное очко для первичной сортировки
      else 0 -- запись с неприоритетным свойством получит пониженное очко для первичной сортировки
    end desc, -- эта менее важная характеристика
    -- ... и так далее все характеристики
    <Другие условия сортировки (по дате добавления, имени и т.д.)>

    Можно комбинировать подходы, если есть куча характеристик на одном уровне иерархии.
    Ответ написан
    1 комментарий
  • Почему в данных таблицы все не по порядку?

    @alexalexes
    О порядке добавления записей не нужно заботиться.
    У СУБД нет задачи хранить записи в строгом порядке, ваша задача понять, удовлетворит ли вас результат сортировки, если при выборке задать сортировку по darg-w:
    select *
      from verbs
    order by darg-w

    Если хотите получать список в порядке добавления, то логично сортировать по id:
    select *
      from verbs
    order by id

    Если хотите особую сортировку, которая не подчиняется ни id, ни естественному весу строк, то можете ввести новую колонку и следить за позиционированием строк при выборки сами, или по каким-то другим правилам вычислять позицию:
    select *
      from verbs
    order by position -- дополнительное поле, в которое будете сами или программно вбивать порядок сортировки

    Или запрос может выдергивать сам определенные строки, давая им высший приоритет (можно написать любое правило, что требует бизнес логика, или что в голову сбредет):
    select *
      from verbs
    order by case when id = 100 then 0 else 1 end -- даем записи id=100 наивысший приоритет, в любом состоянии списка она будет на 1 месте!
            , id -- остальные записи отсортируются в порядке добавления

    PS: Важно, чтобы поля, которые применяются в сортировке, были проиндексированы (в схеме СУБД были созданы индексы по данным полям и они своевременно пересчитаны).
    Ответ написан
    1 комментарий
  • Как удалить определённое словосочетание в значении MYSQL?

    @alexalexes
    1. Нужно знать что такое Update.
    2. Уметь работать с Update, не обнулив все подряд (семь раз select-ть с таким же where - один раз update-ть!)
    3. Некоторые строковые функции, например, replace.
    4. Режим работы транзакций и уровень изоляции таблиц при выполнении транзакций. Чтобы не было сюрпризом, когда работаете с выключенным autocommit-ом (это касается desktop программ - менеджеров СУБД), что данные не сохранились, когда забыли объявить commit после изменения данных, или уметь откатываться rollback-ом, если не объявили commit. Еще доступ к таблице будет заблокирован, пока вы принимаете решение после изменения данных - фиксировать их или нет, если у вас уровень изоляции транзакции на всю таблицу и вы работаете без autocommit.
    update superbase.table1
    set theme = replace(theme, 'комплект', '')
    where theme like '%комплект%'
    Ответ написан
    1 комментарий
  • Как сделать поиск в строке с разделителем в mysql?

    @alexalexes
    Но для это подойдет для поиска одного размера, а мне прилетает несколько. Не ужели скажем если 10 размеров прилетит, писать 10 запросов для проверки каждого размера?

    Да, будете страдать по полной, раз нет нормализованной структуры данных.
    Ни нормального выражения с in не построить, ни возможности создать индекс для ускорения поиска.
    Время поиска будет линейно расти от количества записей и умножаться на кол-во or, так как нельзя воспользоваться индексом.
    select * from table
    where 1 = 1 -- чтобы можно было однотипно подставить выражения с and или or, не заморачиваясь в каких случаях нужен and/or, а в каких нет.
    or find_in_set('L', replace(value, ';', ','))
    or find_in_set('XL', replace(value, ';', ','))
    ...
    or find_in_set('XXXL', replace(value, ';', ','))
    Ответ написан
    Комментировать
  • Как выбрать значения из БД по JSON?

    @alexalexes
    Если СУБД поддерживает тип данных JSON, а вы хотите сравнивать JSON тело как строку, то приведите содержимое из JSON в строку.
    SELECT * FROM tavle
     WHERE JSON_EXTRACT(json, '$') = jsons; -- атрибут json относится к таблице tavle, а jsons - плейсхолдер, куда вы подставляете строку, по которой будете искать тело?
    -- JSON_EXTRACT(атрибут_типа_JSON, '$') - это выражение конвертирует JSON в строку, аналогично как в JS будет JSON.stringify(объект_с_данными).
    Ответ написан
    2 комментария