Ответы пользователя по тегу MySQL
  • Как отобразить различный текст в зависимости от времени добавления данных в БД?

    erge
    @erge
    Примус починяю
    для MySQL смотрите функции работы с датами и CASE WHEN
    DATETIME приводите к DATE и через CASE WHEN определяете

    SELECT
        id,
        dt,
        CASE DATE(dt)
          WHEN CURDATE() THEN 'Сегодня'
          WHEN CURDATE() + INTERVAL -1 DAY THEN 'Вчера'
          ELSE 'Ранее'
        END AS when_day
      FROM items
    ;


    см. пример

    для PHP аналогично, смотрите switch case и DateTime для работы объектом, который представляет дату и / или время и методы для манипуляции с ними.

    $now = new DateTime();  // текущая дата
      switch ($DATEPOSTED) { // в формате Y m d
        case $now->format("Y m d"):
            echo "сегодня";
            break;
        case ((clone $now)->modify("yesterday"))->format('Y m d'):
            echo "вчера";
            break;
        default:
            echo "ранее";
      }


    PS: здесь (clone $now) я клонирую $now, чтобы не изменять основную переменную, если обернуть в функцию то это не обязательно, см. пример ниже.

    см. полный пример

    UPDATE:
    касательно кода в стилистике автора
    $DATEPOSTED = strtotime("-1 day");
    здесь вчерашний день задается неверно!
    во-первых, strtotime - Преобразует текстовое представление даты на английском языке в метку времени Unix
    во-вторых, время Unix (timestamp) не совпадает с форматом заданным выше и в $now - Y m d, поэтому сравнивать их некорректно, необходимо приводить дату к одному формату Y m d
    $DATEPOSTED = date('Y m d', strtotime(' -1 day'));
    далее по условию, если необходимо просто сравнивать с сегодня и что не сегодня то это вчера, то условие вообще банальное:
    if ($now == $DATEPOSTED) {
    Ответ написан
    Комментировать
  • Можно ли написать циклический запрос, получающий последнюю запись из древа, не делая несколько запросов в цикле?

    erge
    @erge
    Примус починяю
    -- вывод ветки дерева
    WITH RECURSIVE
    cte (id, title, parent_id) AS (
      SELECT     id,
                 title,
                 parent_id
      FROM       test
      WHERE      id = 1 -- < id узла от которого выводить
      UNION ALL
      SELECT     t.id,
                 t.title,
                 t.parent_id
      FROM       test t
      INNER JOIN cte
              ON t.parent_id = cte.id
    )
    SELECT * FROM cte;


    для того чтобы идти от потомка к родителю, необходимо "развернуть" связь в ON t.parent_id = cte.id т.е. ON t.id = cte.parent_id и из полученного выбрать запись с parent_id is null

    т.е. так:
    -- вывод самого верхнего родителя по дочернему узлу
    WITH RECURSIVE
    cte (id, title, parent_id) AS (
      SELECT     id,
                 title,
                 parent_id
      FROM       test
      WHERE      id = 4 -- < id узла
      UNION ALL
      SELECT     t.id,
                 t.title,
                 t.parent_id
      FROM       test t
      INNER JOIN cte
              ON t.id = cte.parent_id
    )
    SELECT * FROM cte
      WHERE parent_id IS NULL;


    см. пример

    для MySQL 5+ можно так:

    SELECT * FROM (
      SELECT  id,
              title,
              parent_id 
        FROM (SELECT * FROM test ORDER BY id DESC) test_sorted
        JOIN (select @pv := 4) initialisation -- < id узла
        WHERE find_in_set(id, @pv)
          AND length(@pv := concat(@pv, ',', COALESCE(parent_id, '')))
    ) t
      WHERE parent_id is null
    Ответ написан
    Комментировать
  • Как написать SQL запрос для выборки записей, который учитывает кол-во существующих в таблице записей с таким же ключом?

    erge
    @erge
    Примус починяю
    т.к. набор характеристик может быть совершенно разнообразным, а известен нам только товар (его id), то сопоставлять необходимо по набору характеристик и их кол-ву, т.е. объединяем таблицу саму с собой, из первой выбираем наш продукт и по его набору характеристик соединяем со второй, при этом исключаем исходный товар из второй, как-то так:

    WITH param AS (
      SELECT 5 AS prod_id -- ИД продукта которому ищем сопутствующие
    )
    SELECT p2.prod_id
      FROM param, prod_ch_val p1, prod_ch_val p2
      WHERE p1.prod_id = param.prod_id   -- ИД продукта
        AND p1.ch_id=p2.ch_id
        AND p1.val = p2.val
        AND p1.prod_id != p2.prod_id
        AND (SELECT count(1) FROM prod_ch_val t WHERE t.prod_id=p2.prod_id) = (SELECT count(1) FROM prod_ch_val t WHERE t.prod_id=p1.prod_id)
    GROUP BY p2.prod_id


    пример
    идентичные товары:
    1 и 3
    2 и 5
    4 - не идентичен ни с кем.

    PS: выражение WITH можно убрать и подставлять ID товара непосредственно в запрос вместо param.prod_id
    сделал для удобства
    Ответ написан
    Комментировать
  • Как заполнить таблицу в цикле Mysql?

    erge
    @erge
    Примус починяю
    т.к. вы не указали версию БД, то предлагаемое ниже решение написано в рамках 8+ версии
    предполагаю что product_attr_id - это автоинкрементный PRIMARY KEY
    В итого, все делается одним запросом и без циклов.
    делаем две рекурсивные CTE (см. Recursive Common Table Expressions), реализуем в них "счетчики", объединяем их и получаем желаемое, далее дело техники, в итого получаем

    INSERT INTO product_attr (attr_7, attr_29)
    WITH RECURSIVE
      param AS
    (
      SELECT
        461  AS attr_7_start,  -- начальное значение для attr_7
        10   AS attr_7_num,    -- количество итераций в attr_7
        1097 AS attr_29_start, -- начальное значение для attr_29
        3    AS attr_29_num    -- количество итераций в attr_29
    ),
      attr29_iter (n) AS
    (
      SELECT 0
      UNION ALL
      SELECT n + 1 FROM attr29_iter, param WHERE n < attr_29_num -1
    ),
     attr_7_iter (n) AS
    (
      SELECT 0
      UNION ALL
      SELECT n + 1 FROM attr_7_iter, param WHERE n < attr_7_num -1
    ),
      gen_inc AS
    (
      SELECT i7.n as inc7, i29.n as inc29
        FROM attr_7_iter i7, attr29_iter i29
    )
    SELECT attr_7_start + inc7, attr_29_start + inc29
      FROM gen_inc, param
      ORDER BY 1,2
    ;


    PS: для удобства, вынес так называемые параметры в отдельную CTEшку param

    см. пример на dbfiddle

    UPDATE:
    для "прохода" по нескольким продуктам по их категориям, просто включаете таблицы product и category в общее объединение во внешнем запросе SELECT

    INSERT INTO product_attr (product_id, product_price, product_old_price, attr_7, attr_29)
    WITH RECURSIVE
      param AS
    (
      SELECT
        461  AS attr_7_start,  -- начальное значение для attr_7
        14   AS attr_7_num,    -- количество итераций в attr_7
        1097 AS attr_29_start, -- начальное значение для attr_29
        3    AS attr_29_num    -- количество итераций в attr_29
    ),
      attr29_iter (n) AS
    (
      SELECT 0
      UNION ALL
      SELECT n + 1 FROM attr29_iter, param WHERE n < attr_29_num -1
    ),
     attr_7_iter (n) AS
    (
      SELECT 0
      UNION ALL
      SELECT n + 1 FROM attr_7_iter, param WHERE n < attr_7_num -1
    ),
      gen_inc AS
    (
      SELECT i7.n as inc7, i29.n as inc29
        FROM attr_7_iter i7, attr29_iter i29
    )
    SELECT p.product_id, p.product_price, p.product_old_price, attr_7_start + inc7 AS attr_7, attr_29_start + inc29 AS attr_29
      FROM gen_inc, param, product p, category c
      WHERE p.product_id = c.product_id
        AND c.category_id NOT IN (214, 221, 220, 217, 216, 215, 48, 42, 40, 45, 46, 44, 50, 41, 43, 31, 178, 89, 47, 179, 177, 120, 121, 59, 58, 32, 37, 56, 53, 54, 55, 29, 28, 30, 176, 237, 113, 116, 175, 114, 196, 195, 197, 218, 219, 33, 93, 92, 91, 81, 90)
      ORDER BY 1, 4, 5
    ;


    см. пример на dbfiddle
    Ответ написан
  • Как сделать выборку из одной таблицы и сгруппировать по колонке?

    erge
    @erge
    Примус починяю
    Просто джойнишь таблицу саму с собой и для каждой "таблицы" в where пишешь field_name = 'longitude'и field_name = 'latitude'

    select
        lng.object_id,
        ltd.value as latitude,
        lng.value as longitude
      from obj_table lng, obj_table ltd
      where lng.field_name = 'longitude'
        and ltd.field_name = 'latitude'
        and lng.object_id = ltd.object_id
    ;


    см. пример на dbfiddle

    UPDATE:

    еще можно так:
    select
        object_id,
        max(case field_name when 'latitude' then value else null end) as latitude,
        max(case field_name when 'longitude' then value else null end) as latitude,
        max(case field_name when 'category' then value else null end) as category
      from obj_table
      group by object_id;


    или еще...
    собрать строки в json, а затем извлечь обратно нужные поля...

    -- собираем в json:
    select
        object_id,
    	cast(replace(group_concat(json_object(field_name,value)),'},{',',') as json) json_obj
      from obj_table
      group by object_id
    ;
    
    -- или так:
    select
        object_id,
        cast(concat('{',group_concat( concat('"',field_name,'":"',value,'"') separator ',' ), '}') as json) as json_obj
      from obj_table
      group by object_id
    ;
    
    -- извлекаем поля из json:
    select
        object_id,
        json_extract(json_obj,'$.latitude') as latitude,
        json_extract(json_obj,'$.longitude') as latitude,
        json_extract(json_obj,'$.category') as category
      from (
    select
        object_id,
        cast(replace(group_concat(json_object(field_name,value)),'},{',',') as json) json_obj
      from obj_table
      group by object_id
      ) t
    ;


    см. пример на dbfiddle

    PS: JSON работает начиная с версии 5.7
    Ответ написан
    Комментировать
  • Как получить название и 'картинку' из БД?

    erge
    @erge
    Примус починяю
    Нет связи между таблицами file_managed или field_revision_field_image с таблицей node, поэтому у вас получается декартово произведение.
    состоящее в перечислении через запятую табличных выражений в предложении FROM (таблицы, представления, подзапросы) при отсутствии предложения WHERE, связывающего столбцы из перечисленных источников строк
    (см.)
    в настоящее время у вас только одна связь по типу 'article' и этого очевидно недостаточно.
    Ответ написан
    Комментировать
  • Как запрос к mysql переделать по sqlite?

    erge
    @erge
    Примус починяю
    И почему вы думаете что это должно работать в SQLite? Разные БД, разный синтаксис, разные функции в том числе работы с датами...

    в ошибке явное указание в чем проблема:

    Error: near "DAY": syntax error

    нет такого в принципе в SQLite

    смотрите функции работы с датами в SQLite!
    date функция SQLite

    т.е. в итого вместо
    DATE(DATE_ADD(NOW(), INTERVAL -3 DAY))

    надо:
    date ( 'now', '-3 day')

    PS: между - и 3 пробелов быть не должно, SQLite этого не понимает и возвращает результатом date - null !? но почему-то во многих примерах пишут с пробелом!??
    Ответ написан
    Комментировать
  • Возможно ли сделать нулевой count при использовании GROUP BY?

    erge
    @erge
    Примус починяю
    1. нужна таблица - календарь, либо сгенерированный набор дат
    2. либо к календарю надо сделать left join вашей таблицы, либо к вашей таблице надо сделать right join календаря.

    SQL join в примерах с описанием

    в результате получится:

    select cal.cal_date, count(person)
      from some_table st
      right join (
      -- генерируем календарь - набор дат
        select *
          from (
            select adddate('1970-01-01',t4*10000 + t3*1000 + t2*100 + t1*10 + t0) cal_date
              from
                (select 0 t0 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) t0,
                (select 0 t1 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,
                (select 0 t2 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,
                (select 0 t3 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,
                (select 0 t4 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
          ) v
          where cal_date between '2021-11-01' and '2021-11-30'
      ) cal on cal.cal_date = st.date_field
      group by cal.cal_date


    в MariaDB есть Sequence Storage Engine, но до MariaDB 10.0 , он поставлялся в виде динамической плагина, начиная с MariaDB 10.1 , движок Sequence устанавливается по умолчанию. Используя его можно генерировать список дат (календарь) следующим образом:

    SELECT '2021-11-01' + INTERVAL seq DAY FROM seq_0_to_29;


    а запрос будет соответственно:

    select cal.cal_date, count(person)
      from some_table st
      right join (
      -- генерируем календарь - набор дат
        SELECT '2021-11-01' + INTERVAL seq DAY as cal_date FROM seq_0_to_29
      ) cal on cal.cal_date = st.date_field
      group by cal.cal_date
    ;


    см. пример работы на dbfiddle.uk

    и если person добавить в группировку - пример
    Ответ написан
    Комментировать
  • Выбрать только одну саму свежую строку для поля?

    erge
    @erge
    Примус починяю
    для MySQL 8.0 испльзовать row_number()

    для более ранних версий использовать подзапросы:

    -- For MySQL 8.0
    select FLOW, OTPERIOD, LOG_DATE, LOG_TIME, ENTITY
      from (
        select
          FLOW, OTPERIOD, LOG_DATE, LOG_TIME, ENTITY,
          row_number() over (partition by ENTITY order by LOG_DATE desc, LOG_TIME desc) num
        from test
      ) tt
      where num = 1
    ;
    
    
    -- For MySQL <=8.0
    select t.*
      from test t
      inner join (
        select max(LOG_DATE) LOG_DATE, MAX(LOG_TIME) LOG_TIME, ENTITY
          from test
          group by ENTITY
          having concat(LOG_DATE,' ', LOG_TIME) = max(concat(LOG_DATE,' ', LOG_TIME))
    ) tt on tt.ENTITY = t.ENTITY and tt.LOG_DATE = t.LOG_DATE and tt.LOG_TIME = t.LOG_TIME
    ;
    
    
    -- For MySQL <=8.0
    select *
      from test t
      where concat(LOG_DATE,' ', LOG_TIME) = (
        select max(concat(LOG_DATE,' ', LOG_TIME))
          from test tt
            where tt.ENTITY = t.ENTITY
          group by ENTITY
      )
    ;


    как-то так, см. пример на dbfiddle.uk

    PS: но по последним двум вариантам будет выдаваться несколько записей если в одну секунду по одному и тому же ENTITY пишется несколько записей, надо дальше "колхозить".
    Ответ написан
    3 комментария
  • Как экспортировать базу JSON в MYSQL?

    erge
    @erge
    Примус починяю
    1. не экспорт, а импорт
    2. гуглите (яндексите) import json to mysql (есть море информации)
    3. если импортировать записи как в примере в простую таблицу в новую БД, то проще и быстрее как уже говорили выше - загружать напрямую в базу из CSV файла, а json конвертнуть в CSV при помощи sed например (одной строчкой)
    Ответ написан
    Комментировать
  • Как сделать выборку постов из разных категорий в нужном количестве?

    erge
    @erge
    Примус починяю
    select name, test from (
      select name, test from (select name, test from post where category_id=2 limit 0,4) t2
      union
      select name, test from (select name, test from post where category_id=3 limit 0,5) t3
      union
      select name, test from (select name, test from post where category_id=1 limit 0,18) t1
    ) t
    limit 0,18


    см. пример на dbfiddle.uk
    Ответ написан
    Комментировать
  • Как из БД выбрать товар рандомно?

    erge
    @erge
    Примус починяю
    По мотивам New_Horizons

    SELECT p.* FROM product p
      JOIN ( SELECT rand() * (SELECT max(id) FROM product WHERE p.name LIKE 'Горо%') AS max_id ) AS m
      WHERE p.id >= m.max_id
        AND p.name LIKE 'Горо%'
    ORDER BY p.id ASC LIMIT 1;


    про p.name LIKE 'Горо%'

    1. в подзапросе выбирается максимальный ID для товаров с соответствующим названием, т.к. если не ограничивать то выберется в принципе максимальный ID, а он может быть больше максимального ID нужного товара и если он выпадет в рандоме, то не выберется ничего.
    2. при выборе по ID >= рандомного max_id, ограничиваем выборку по нужному товару условием.
    Ответ написан
    Комментировать
  • Как сделать запрос в запросе в mysql?

    erge
    @erge
    Примус починяю

    надо что бы при запросе вывел где status= true, и count_id = количество id за 30 дней.
    |id|id_2|name|status|date| count_id |


    • количество записей считается COUNT при группировке по полям (см. GROUP BY), в данном случае по интервалу дат

    • но... в таком виде
      |id|id_2|name|status|date| count_id |
      вывести не получится! т.к. группировать по полям |id|id_2|name| не имеет смысла, они могут быть уникальны и никакого COUNT за интервал не получится, например как считать количество в интервале для id=1,2,3 ... ??? это группироваться никак не будет вообще, будет выводиться построчно для каждого id и количество будет Всегда равно =1 и так для всех id. определитесь что вам надо?! конкретно

    • можно выбрать записи со статусом true и посчитать количество в интервале, для этого поле date привести в некий интервальный формат, либо если надо за ПОСЛЕДНИЕ 30 дней, то просто:
      SELECT count(id) count_id
        FROM tbl
        WHERE status = 'true'
          AND date >= now() - interval 30 day
      ;




    см. пример на dbfiddle
    Ответ написан
    Комментировать
  • Как настроить выгрузку excel на PHPEexcel чтобы выгрузка из mysql начиналась с первого элемента, а не со второго?

    erge
    @erge
    Примус починяю
    $query1 = mysqli_query($link, "SELECT * FROM goods ORDER BY id");


    $s = 1;

    UPD:

    да, заметил...
    зачем зафетчили первую строку до вывода в цикле??
    $myrow = mysqli_fetch_array($query1);
    удалите ее.
    Ответ написан
    3 комментария
  • Как построить sql запрос?

    erge
    @erge
    Примус починяю
    как-то так...

    SELECT
        c.title,
        ou.name,
        m.text,
        FROM_UNIXTIME(m.date) AS dt
      FROM chat_users cu
      INNER JOIN chat_users cuu ON cuu.chat_id = cu.chat_id AND cuu.user_id != cu.user_id
      INNER JOIN users ou ON ou.id = cuu.user_id
      INNER JOIN chat c ON c.id = cu.chat_id
      INNER JOIN messages m ON m.user_id = cuu.user_id AND m.chat_id = cu.chat_id
      WHERE cu.user_id = :ID_USER  -- ID пользователя по которому выбирать чаты.
      ORDER BY c.title ASC, m.date DESC
    Ответ написан
    Комментировать
  • Хитрая SQL группировка, как сделать?

    erge
    @erge
    Примус починяю
    SELECT
        position, count(1) AS n
      FROM (
        SELECT
            position, row_number() OVER (PARTITION BY position) rn
          FROM tbl
      ) t
      GROUP BY position, (rn+1) div 2
    ;

    см. пример на https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=50eda5...
    Ответ написан
    Комментировать
  • Как правильно преобразовать text в дату?

    erge
    @erge
    Примус починяю
    как писал @alexalexes
    Необходимо строку doc_time функцией STR_TO_DATE преобразовать в дату, а уже ее сравнивать с интервалом дат в BETWEEN

    SELECT *
      FROM order_kassa
      WHERE str_to_date(doc_time, '%d.%m.%Y') BETWEEN '2019-11-01' AND '2019-11-20';


    см. пример - https://dbfiddle.uk/?rdbms=mysql_5.6&fiddle=cde3b3...
    Ответ написан
    Комментировать
  • Группировка по дням из двух таблиц?

    erge
    @erge
    Примус починяю
    Вынести подзапросы по click в один подзапрос с группировкой (аналогично как по event)
    и сджойнить два подзапроса по дате, как-то так:

    SELECT
        e.*, c.`click`, c.`unique_click`
      FROM (
        SELECT
            DATE(FROM_UNIXTIME(`inserted_at`)) as `day`,
            SUM(IF(`type` = 2, `total_cost`, 0)) as `f_sum`,
            SUM(IF(`type` = 3, `total_cost`, 0)) as `d_sum`,
            SUM(IF(`type` = 4, `total_cost`, 0)) as `w_sum`,
          FROM `event`
          GROUP BY `day`
      ) e
      INNER JOIN (
        SELECT
            DATE(FROM_UNIXTIME(`inserted_at`)) as `day`
            SUM(IF(`type` = 1, 1, 0)) as `click`,
            SUM(IF(`type` = 2, 1, 0)) as `unique_click`,
          FROM `click`
          GROUP BY `day`
      ) c ON e.`day` = c.`day`
      ORDER BY e.`day` DESC
      LIMIT 10
    ;
    Ответ написан
  • Поиск в БД, как найти данные по используя like?

    erge
    @erge
    Примус починяю
    'UT%409%'

    $arr = [
        0 => "UT",
        1 => "409"
    ];
    
    $s = join("%", $arr)."%";
    
    $db->where('model', 'like', $s);
    Ответ написан
    Комментировать
  • Как вытащить данные при вложенном запросе?

    erge
    @erge
    Примус починяю
    У вас таблица oc_product внутри подзапроса, поэтому сначала нужно вывести там, а уже потом "сверху":

    SELECT
        name,
        sku,
        CONCAT('https://домен/', IF(ua2.keyword IS NULL,'',CONCAT(ua2.keyword, '/')), IF(ua3.keyword IS NULL,'',CONCAT(ua3.keyword, '/')), IF(ua4.keyword IS NULL,'',CONCAT(ua4.keyword, '/')), ua1.keyword) AS url
      FROM (
        SELECT
            name,
            p.sku,
            CONCAT( 'product_id=', p.product_id ) AS product_query,
            CONCAT( 'category_id=', pc.category_id ) AS category_query,
            CONCAT( 'series_id=', ps.series_id ) AS series_query,
            CONCAT( 'subcategory_id=', psc.subcategory_id ) AS subcategory_query
          FROM `oc_product_description` pd
          LEFT JOIN oc_product p ON (p.product_id=pd.product_id)
          LEFT JOIN oc_product_to_category pc ON (pc.product_id=p.product_id)
          LEFT JOIN oc_product_to_series ps ON (ps.product_id=p.product_id)
          LEFT JOIN oc_product_to_subcategory psc ON (psc.product_id=p.product_id)
          WHERE p.date_available <= NOW()
            AND p.status = '1'
      ) pd
      LEFT JOIN oc_url_alias ua1 ON ( pd.`product_query` = ua1.`query` )
      LEFT JOIN oc_url_alias ua2 ON ( pd.`category_query` = ua2.`query` )
      LEFT JOIN oc_url_alias ua3 ON ( pd.`series_query` = ua3.`query` )
      LEFT JOIN oc_url_alias ua4 ON ( pd.`subcategory_query` = ua4.`query` )
    ;


    PS: и форматируйте запросы нормально, читать будет удобнее.
    Ответ написан
    Комментировать