Ответы пользователя по тегу MySQL
  • Как лучше реализовать вывод в корзину?

    @alexalexes
    1. Ограничьте количество предметов в корзине каким-то макс. значением, оно должно быть явно задано, но не вызывало дискомфорта у пользователей (оно должно иметь такое значение, чтобы в нормальных человеческих условиях оно едва достижимо).
    $max_cart_item_count = 500;
    $curr_cart_item_count = min(count($_SESSION['cart']), $max_cart_item_count);
    for($i = 0; $i < $curr_cart_item_count; $i++)
    ....

    2. Есть опасность применения SQL инъекций.
    В этом месте, где подставляется переменная в текст запроса, если умело закомментировать кавычку, то можно получить полный доступ к базе.
    WHERE `type`= '$id'
    Избегайте подставлять любые переменные с пользовательскими данными в текст запроса ("с пользовательскими данными" и "в текст запроса" - нужно подчеркнуть и осмыслить как отдельные понятия с точки зрения безопасности). Для этого существует специальные методики подготовки запроса:
    $stmt = $conn->stmt_init();
    $stmt->prepare("SELECT * FROM `items` WHERE `type` = ?"); // подставляем текст запроса, причем на месте входных параметров ставим специальные маркеры - плейсхолдеры.
    $stmt->bind_param("i",  $id); // подставляем на место плейсхолдера значение параметра как целочисленный тип
    $stmt->execute(); // вот теперь можно выполнить запрос
    $result = $stmt->get_result();
    while($row = $result->fetch_assoc())
    {
     // Обработка результатов
    }
    Ответ написан
  • Как сравнить два поля VARCHAR и INT?

    @alexalexes
    Как выглядит ваша проблема:
    Таблица новостей:
    ID, CATEGORIES
    1, '1,13,14'

    Как нужно реализовать:
    Добавить еще таблицу Categories_Of_News
    ID, NEWS_ID, CATEGORY_ID
    1, 1, 1
    2, 1, 13
    3, 1, 14

    Тогда запросы не будут содержать никаких костылей с парсингом строк и у вас будут работать индексы (если их создать).
    Например, получить все категории, в которые входит новость :news_id
    select  C.Id
    from News N
    join Categories_Of_News CoN on CoN.News_Id = N.Id
    join Category C on C.Id = CoN.Category_Id 
    where N.Id = :news_id

    PS: Смотрите, как реализовать связь "многим-ко-многим".
    Ответ написан
    Комментировать
  • Как записать данные из запроса VK в бд?

    @alexalexes
    Отработать навык, как вставить в базу вообще что-то.
    Например, так https://html5css.ru/php/php_mysql_insert.php
    Обратите внимание, что у процесса взаимодействия с БД есть несколько фаз работы:
    подключение к базе, формирование запроса, добавление параметров к запросу, выполнение запроса и получение результата (если нужен действия статус или выборка).
    Ответ написан
    Комментировать
  • Структуризация и импорт базы для интернет магазина?

    @alexalexes
    артикулом/категорией/брендом/названием/ценой

    Редко какое свойство товара останется скалярным значением с развитием структуры базы и не перейдет в таблицы-справочники.
    Что же сразу должно бросится в глаза начинающему проектировщику БД - это то, что нужны таблицы:
    - Категория товара
    - Бренд
    - Цена на товар (одна запись соответствует цене товара за определенный период, может нести сведения о скидке и т.д.)
    Причем, валюта цены тоже может быть отдельным справочником.
    У товара могут быть габариты ДxВxШ.
    Название оси габарита - эта тоже справочник.
    Размерность - см, м, дюймы - тоже справочник.
    Так что, смотрите, любое свойство можно превратить в справочник, и довольно разобранный на составляющие.
    Ответ написан
    Комментировать
  • Сравнение времени mysql?

    @alexalexes
    Минус переставьте в нужное место.
    SELECT * FROM `test` WHERE `time` >= (now() - INTERVAL 10 MINUTE);
    Ответ написан
    3 комментария
  • Как сделать одно действие с несколькими значениями?

    @alexalexes
    Если нужно присвоить одно и то же значение атрибутам у нескольких определенных строк, то это делается элементарно.
    update `table` set атрибут_не_первичный_ключ = 1
    where id in (1, 2, 4, 10);

    Другое дело, если это касается атрибутов с уникальным свойством.
    Если нужно сделать рокировку значений, то поскольку первичный ключ имеет свойство уникального значения, то нужно действовать в два этапа.
    1. Переписать id-шники интересуемых строк так, чтобы новый id был за диапазоном присваиваемых id.
    Можно переписать в большую сторону, если у вас максимальное число записей пару тысяч, то воспользоваться новыми значениями, например, от миллиона.
    Но проблема в том, что если система не остановлена во время этой рокировки, то вероятно, вставка новых строк будет происходить с присвоением макс. последнего значения +1, тогда новые строки окажутся с миллионными id-шками.
    Нужно взять диапазон отрицательных чисел в качестве промежуточного состояния рокировки id.
    update table
    set id = case
       when id = 1 then -1
       when id = 2 then -2
       when id = 4 then -4
       when id = 10 then -10
       else id -- страховка от дурака, если неправильно описан in 
    end
    where id in (1, 2, 4, 10)

    2. Второй шаг заключается в том, чтобы переписать нашим отрицательным id новые положительные значения, чтобы каждая строка получила новые значения 1,2,4,10. Например, можно поменять местами id соседей.
    update table
    set id = case
       when id = -1 then 2
       when id = -2 then 4
       when id = -4 then 10
       when id = -10 then 1
       else id -- страховка от дурака, если неправильно описан in 
    end
    where id in (-1, -2, -4, -10)

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

    @alexalexes
    Если вы понимаете, что делает этот запрос:
    UPDATE article SET visits=(visits+1) WHERE id='".$theme_idss."' LIMIT 1

    То по аналогии можно плюсовать интересуемый столбец:
    UPDATE article
    SET visits=(visits+1),
          today = today + 1 -- суточные посещения
     WHERE id='".$theme_idss."' -- когда разберетесь, что такое sql-инъекция, то перепишите все запросы, использующие такой стиль подстановки параметров (склеиванием строк)
    LIMIT 1 -- после того, как разберетесь, является ли id первичным ключом, можно убрать лимит

    Но при добавлении столбца нужно позаботиться, чтобы у столбца значение по умолчанию было 0.
    ALTER TABLE article
    ADD COLUMN today int default 0 AFTER visits

    В скрипте, который чистит таблицу раз в сутки предусмотрите запрос, обнуляющий суточный счетчик у всех статей:
    UPDATE article
    SET today = 0

    PS: Если хотите анализировать посещаемость, то лучше воспользоваться готовыми счетчиками от поисковых систем - Google Analytics или Яндекс-метрики.
    Если хотите все же использовать свой лог, то лучше его не удалять. Вдруг захотите выявить среднемесячное/суточное/годовое посещение или в каком то другом разрезе по разным разделам сайта. Для пересчета понадобится каждая запись посещения.
    Ответ написан
    2 комментария
  • Как уменьшить время выполнения запроса?

    @alexalexes
    Делаете отдельную таблицу, в единственном поле и записи которой храните значение этого кол-ва.
    Создаете триггеры для таблицы posts на операции вставки, обновления, удаления.
    В триггере используете этот запрос на подсчет кол-ва и вставку/обновление этого значения в таблице количеств.
    Этим триггером вы переложите 30мс из операции select подсчета кол-ва, на операции insert, update, delete, во время вставки нового значения (обновления категории, статуса, удаления записи), причем в триггере можно игнорировать пересчет, если запись вставляется не того вида (не категория 4 и не статус 1).
    Количество особых записей всегда можно получить моментально из той самой таблицы.
    Ответ написан
  • Как вырезать нужную часть предложения с помощью MySQL?

    @alexalexes
    Можно с помощью функции SUBSTRING_INDEX.
    Два раза применяете эту функцию:
    1. Получаете часть строки "Eau Claire - Home 83", указав разделитель " of ", а номер -1.
    2. Получаете часть строки "Eau Claire", указав разделитель " - ", а номер 1.
    Ответ написан
    2 комментария
  • Как отзеркалить сопутствующие товары?

    @alexalexes
    Вы этой таблицей кодируете граф (не важно чего).
    Если принимаете за правило, что каждая запись кодирует ребро графа только в одном направлении (граф является направленным), то, чтобы добавить другое направление, тогда вам нужно вставить еще одну запись.
    Например, существует ребро от вершины 1885 до вершины 4683, чтобы прописать ребро с обратным направлением, нужно вставить запись (4683, 1885).
    Список ребер к присоединенным вершинам от данной вершины достаточно выбирать таким запросом:
    select * from t
    where t.idProduct = :id_product and t.relatedProduct <> :id_product -- сравнение с <> исключает петлю на собственной вершине

    Если у вас граф ненаправленный, то можно обойтись одной записью. При получении выборки (ребер графа) вам нужно выбирать не только ребра от вершины idProduct до relatedProduct, но включать те записи, ребра которые прописаны от relatedProduct до idProduct.
    Скорее всего запрос будет иметь такой вид:
    select * from t
    where (    (t.idProduct = :id_product and t.relatedProduct <> :id_product) -- сравнение с <> исключает петлю на собственной вершине
                or (t.relatedProduct = :id_product and t.idProduct <> :id_product))

    Но обязательно нужно определиться, работает ли совместимость товаров в обе стороны, от этого зависит выбор типа графа и правила его чтения/записи.
    Ответ написан
    6 комментариев
  • Как объединить GROUP BY и JOIN?

    @alexalexes
    Чтобы найти читателей с последними взятыми книгами, нужно при помощи оконной функции row_number пронумеровать строки в пределах каждого читателя (задав партицию по айди читателя) в порядке убывания по дате taken_at. Из полученного результата взять строки, которые получили номера 1.
    select * from
    (select r.id as reader_id, r.first_name, r.last_name, b.id book_id, b.name, b.pub_date, row_number() over (partition by r.id order by l.taken_at desc) as rownum
    from readers r
    left join log_taking l on r.id = l.reader_id
    left join books b on b.id = l.book_id
    ) a
    where a.rownum = 1

    PS: Связка left join выведет всех читателей, в независимости, брали они книги или нет. Просто join даст список читателей, которые брали книги хоть один раз.
    PPS: Оконные функции доступны с MySQL версии 8.
    Если версия 5, то придется городить велосипед с пользовательскими переменными.
    Ответ написан
    4 комментария
  • Как скопировать данные из 2 таблиц в одну?

    @alexalexes
    Правильно построить выборку из нужных таблиц.
    INSERT into archive (title_product, title_category, price_product) 
    SELECT p.title, c.title, p.price
      from product p
       join category c on p.category_id = c.id
    Ответ написан
    1 комментарий
  • Как найти дни рождения в json наборе?

    @alexalexes
    Лучше детей писать в отдельную таблицу.
    А если очень хочется, то только так колхозить:
    -- MySQL
    select u.user_id, child_bdays.bday
      from user u,
            JSON_TABLE(u.children, '$[*]' COLUMNS (
                    bday VARCHAR(10)  PATH '$'
             )) child_bdays
           where STR_TO_DATE(child_bdays.bday, "%Y-%m-%d") >= DATE(now() - INTERVAL 7 DAY)
             and STR_TO_DATE(child_bdays.bday, "%Y-%m-%d") <= DATE(now() + INTERVAL 7 DAY);
    
    -- MariaDB
    select u.user_id, JSON_EXTRACT(u.children, concat('$[', idx_table.idx, ']')) as bday
      from user u,
         (select 0 idx 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 union select 10 union select 11) idx_table /* впомогательная выборка для генерации индексов в диапазоне 0...11 */
      where idx_table.idx < json_length(u.children) /* отсекаем обращения к несуществующим индексам элементов в JSON*/
        and STR_TO_DATE(JSON_EXTRACT(u.children, concat('$[', idx_table.idx, ']')), "%Y-%m-%d") >= DATE(now() - INTERVAL 7 DAY)
        and STR_TO_DATE(JSON_EXTRACT(u.children, concat('$[', idx_table.idx, ']')), "%Y-%m-%d") <= DATE(now() + INTERVAL 7 DAY)
    Ответ написан
  • Как взять значение с определенного поля и изменить другое определенное поле на данные из первого поля?

    @alexalexes
    Как говорите, так и составляйте запрос в SQL.
    update определенная_таблица т1
    set т1.определенное_поле_2 = т1.определенное_поле_1
    where т1.определенное_поле_x = опреленное_значение -- использовать опционально для конкретных записей
    Ответ написан
  • Как заполнить столбец с foreign key?

    @alexalexes
    Это делается апдейтом с подзапросами.
    update table tb
          set tb.type_id = (select tp.id from types tp where tp.name = tb.type_name limit 1) -- выбираем подзапросом id из справочника по совпадению наименования поля в обновляемой таблице
     where tb.type_id is null -- страховка, что будем обновлять не установленные значения
         and exists(select 1 from types tp where tp.name = tb.type_name) -- будем ставить ключ, если есть наименование в справочнике types

    PS: Прежде чем выполнять update, убедитесь при помощи select с тем же where, что вы вставите нужные для вас значения в поля секции set. По принципу поговорки: "Семь раз select-ни - один раз update-ни".
    PPS: Желательно, избегать ситуаций, когда вам периодически нужно нормализовывать структуру данных, в идеале, при insert-е нужно сразу определять ключик, а поле наименования использовать только тогда, когда нужного наименования нет в справочнике.
    Ответ написан
  • Почему Удаляются записи из БД, ООП, php?

    @alexalexes
    ... на глобальном сбой.

    Вероятно, нужно докопаться до текста ошибки, чтобы понять, в чем дело?
    Но можно предположить, что от места выполнения у вас чувствительна вот эта строчка:
    $this->go_to_back_one_stap = explode('.ru', $_SERVER['HTTP_REFERER']);

    Выведите в отладочный файл значение $_SERVER['HTTP_REFERER'] на боевом окружении и посмотрите, то ли вы ожидаете в ней значение, чтобы разделить ее по '.ru' и взять элемент с индексом 1.
    Ответ написан
  • Можно ли работать двумя программами с одной базой данных?

    @alexalexes

    Обе программы получают из базы данные и работают с ними. Так вот я хочу исключить вероятность получения одних и тех же данных обеими програмамми. Сейчас у меня в коде используется using и в нем идет открытие соединения. Далее, получаем данные, в определенном столбце таблицы я помечаю, что данные взяты, что свидетельствует второй программе о том, чтобы она эти данные уже не брала.

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

    @alexalexes
    Укажите перед выводом данных в php, в какой кодировке собираетесь его делать.
    header('Content-Type: text/html; charset=utf8', true);
    Ответ написан
  • Как избежать явного первичного ключа?

    @alexalexes
    Можно сделать локальный id истории, уникальный в пределах одного контакта.
    Получается, будет уникальный id контакта + id истории. Вы емкость одного идентификатора размажете на два.
    Сортировать можно, находить нужную запись истории можно. Какая запись истории появилась раньше у соседних контактов - скорее всего нельзя выяснить, если нет сквозного id.
    На первый взгляд такие возможности и недостатки.
    Ответ написан
  • При выборке двух таблиц с БД одним запросом, на выводе получаются дубликаты. В чем может быть проблема?

    @alexalexes
    Если просто нужен флаг, что товар в корзине, то достаточно этого запроса.
    SELECT p.*,
                case
                   when exists(SELECT 1 from cart c where c.products_id = p.id)
                   then 1
                   else 0
                end is_exists_in_cart -- есть товар к корзине? 0 - нет, 1 - есть
    FROM table_products p

    Если нужны параметры товара, а не только его наличие, то как минимум будет такая конструкция:
    SELECT p.*, c.*
    FROM table_products p
    left join cart c on c.products_id = p.id

    PS: Конструкция запроса может сильно меняться от того, какие свойства вам нужно извлечь, чтобы вывести в итоговую выборку.
    Ответ написан
    Комментировать