Ответы пользователя по тегу SQL
  • Есть ли готовые решения, для отмены действий на сайте, как бы SQL-undo?

    @alexalexes
    Проектируйте структуру базы данных так, чтобы сохранялась полная история действий над каждым классом данных, словно сохраняете лог для спец. служб.
    Api пользователя не должно использовать DELETE, только INSERT, и UPDATE только для выставления даты удаления записи, выставления метки последней актуальной записи истории. Операцию DELETE имеет право использовать только служебный метод для зачистки истории, когда устаревшие записи становятся неактуальными ни для одного актора системы.
    Ответ написан
    Комментировать
  • Как выбрать данные без дублей с сортировкой?

    @alexalexes
    select A.*
    from (SELECT id, order_id, updated,
                 row_number() over (partition by order_id order by updated DESC) rn
    FROM orders
    WHERE state = 1
    ) A
    where A.Rn = 1
    ORDER BY A.updated DESC
    LIMIT 5, 5
    Ответ написан
    Комментировать
  • Как можно улучшить запрос?

    @alexalexes
    select A.*
    from (SELECT Pss.session_id,  Pss.peak,
             row_number() over (partition by Ps.id order by Pss.peak desc) rnm -- нумеруем оконной функцией выборку в пределах каждого Ps.id по нужной сортировке
        FROM peak_sessions_sets Pss
        JOIN peak_sessions Ps ON Pss.session_id = Ps.id
        WHERE Ps.user_id = 1
    ) A
    where A.rnm = 1 -- извлекаем по 1 записи
    order by A.Peak Desc -- если надо, то еще как-то сортируем итог
    Ответ написан
    Комментировать
  • Как найти общие поля в таблицах?

    @alexalexes
    Если задача на знание справочника SQL, то есть такая конструкция:
    таблица1 join таблица2 using (общие_имена_полей)
    это эквивалент:
    таблица1 join таблица2 on таблица1.общее_имя_поле = таблица2.общее_имя_поле...

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

    @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
    В реляционной базе - основная таблица - это лог событий.
    event_log:
    event_id - идентификатор события
    event_date - дата и время события
    event_type_id - тип события (пришло или ушло)
    value - сколько (пришло или ушло, положительное значение)
    А чтобы подготовить данные для визуализации, переводите лог в агрегированный вид:
    либо делаете view на основе запроса, либо делаете хранимую процедуру для генерации агрегированных данных и кладете их в отдельную таблицу:
    daily_statistics:
    id - идентификатор агрегированной точки
    stat_date - дата агрегированной точки
    in_value - пришло (кол-во)
    out_value - ушло (кол-во)
    current_value - баланс

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

    @alexalexes
    Еще бы какой-то атрибут добавить, чтобы тип записи различать - тогда жить можно.
    select t1.*, -- тематики
          t2.* -- вопросы
    from table_1 t1
    join table_1 t2 on t2.topic = t1.id
    where t1.topic is null
    and t1.Name like 'Тематика%' -- не хватает атрибута type, чтобы различать раздел от тематики
    Ответ написан
  • Как иерархические данные из таблицы преобразовать в объекты Java?

    @alexalexes
    Составить SQL-запрос на основе синтаксиса WITH RECURSIVE, чтобы выборка происходила от корня к листьям.
    Выполнить запрос и получившуюся выборку прогнать через рекурсивную функцию, которая построит дерево, на основе описанной структуры элемента дерева.
    Ответ написан
    Комментировать
  • В чем разница между функцией и триггером?

    @alexalexes
    Триггер - это функция (точнее процедура), запускаемая (поджигаемая) событиями изменения данных - insert, update, delete (до или после события), на определенной таблице.
    В описании триггера обязательно указано событие, на которое он подписан для начала выполнения.
    Обычная функция может быть вызвана как пользователем, так и из тела триггера.
    Триггеры, функции, процедуры как правило пишутся процедурным языком, встроенным в СУБД, в отличии от декларативного языка SQL.
    Ответ написан
    Комментировать
  • Как оптимальнее всего организовать хранение тяжёлых данных и чтобы потом максимально быстро доставать оттуда данные для отчётов?

    @alexalexes
    Структура таблицы нормальна, только добавьте индекс на поле date_submitted и индекс на поле score и будет летать.
    Запрос для получения рейтинга на каждый день для всех пользователей будет примерно такой:
    with date_list as (/*Любым способом получаете непрерывный список дат интересуемого диапазона */)
    select A.date, u.user_id, nvl(A.max_score, 0) max_score /* nvl зависит от СУБД */
    from (select dl.date, u.user_id, tb_max_score.max_score,
              rank() over (partition by dl.date order by random() /*реализация функции random зависит от СУБД*/ ) rnk
    from (select us.date_submitted, max(us.scope) max_score
               from user_score us) tb_max_score  -- выясняем макс-ные баллы
    left join date_list dl on dl.date = tb_max_score.date_submitted
    left join user_score us on dl.date = us.date_submitted  -- выясняем, у кого макс-ные баллы
                                      and us.score = tb_max_score.max_score
    ) A
    cross join user u u.user_id = A.user_id -- прицепляем тех, кто возможно не участвует в рейтинге
    where A.rnk = 1
    Ответ написан
    Комментировать
  • Как объединить данные товара и фото товара в одном запросе?

    @alexalexes
    Можно, через join этих двух таблиц.
    Результатом запроса будет выборка:
    (product_)id, (product_)name, (photo_)id, (photo_)url
    1, 'Товар 1', 1, 'example.png'
    1, 'Товар 1', 2, 'example2.png'

    На стороне скрипта, который отправлял запрос SQL в СУБД, собираете на основе этой выборки объект с требуемой структурой, и потом перекодируете его в строку JSON.
    Ответ написан
    1 комментарий
  • Можно ли составить такой sql запрос?

    @alexalexes
    Если универсальный запрос составлять, который не зависит от состава свойств, то он будет многострочный, нужно собирать свойства в массив в той среде откуда обращались к СУБД:
    select 
      ar.id, 
      ar.name, 
      ar.date, 
      ar.text,
      ap.property_id,
      p.name as property_name, --поскольку у вас есть в базе реализовано универсальное хранение свойств, то почему бы не вывести имя свойства из справочника?
      ap.value as property_value
    from  articles as ar
    left join articles_property as ap on  ap.article_id = ar.id
                                                            and ap.property_id in (/* перечисляем все необходимые свойства */) 
    left join property as p on  ap.property_id = p.id -- справочник названий свойств

    Другой вариант запроса дает конкретные значения свойств, и довольно плоскую выборку, не требующую постобработки (одна строка - это строго одна статья). Но такой запрос не универсален по набору свойств.
    select 
      ar.id, 
      ar.name, 
      ar.date, 
      ar.text,
     (select value
        from articles_property as ap
      where ap.article_id = ar.id
          and ap.property_id = 10) as author
    from  articles as ar
    Ответ написан
  • Как выбрать много пользователей через чекбоксы и выполнить sql запрос?

    @alexalexes
    Оборачиваете html-таблицу в форму, с кнопочкой сабмита. Когда отметили нужное, пользователь должен засабмитить форму. В форме нужно указать метод передачи GET или POST и адрес, где будет располагаться серверный скрипт обработки формы.
    В скрипте примерно такой код:
    <?php
    // считаем, что данные пришли по POST-у
    if(isset($_POST['submit'])) // проверяем, что приехали данные формы по submit, в кнопке сабмита атрибут name должен иметь значение submit
    {
       $inserted_user_count = 0;
       if(isset($_POST['selected_users'])) // присутствуют отмеченные пользователи
       {
          $pdo = new PDO(параметры подключения к базе);
          foreach($_POST['selected_users'] as $selected_user_id)
          {
            $stmt = $pdo->prepare('insert into ваша таблица (атрибуты таблицы) values (значения атрибутов таблицы, кроме user_id, :user_id)');
            $stmt->bindParam(':user_id', $selected_user_id, PDO::PARAM_INT);
            // еще байндим какие-то параметры у запроса, если есть плейсхолдеры, кроме :user_id.
            $stmt->execute(); // наконец, выполняем запрос
            $pdo->commit(); // фиксируем изменения в базе данных, если у вас соединение открыто не в режиме автокамита
            $inserted_user_count++;
          }
          $pdo = null; // закрываем соединение с базой
       }
       echo 'Обработано пользователей: '.$inserted_user_count;
    }
    ?>
    Ответ написан
    Комментировать
  • Как прибавлять значение в базе данных SQL для бота aiogram?

    @alexalexes
    1. Выбрать из базы запись по известному user_id.
    2. Если получили запись, то выполнить обновление атрибута записи win = win + 1 где user_id этой записи равен известному user_id (то же самое в случае проигрыша loss = loss + 1).
    3. Если не получили запись, то делаете вставку записи пользователя, с win = 0, loss = 0 (с 1 в нужной ситуации).
    Ответ написан
  • Как можно рассортировать элементы базы данных по русскому алфавиту?

    @alexalexes
    У MySQL для сортировки кириллицы нет специфичных рекомендаций к order by.
    Только, если вы не захотите применить естественную сортировку (от кодировки не зависит), то в MySQL нет для этого функций, есть некоторое подобие решения проблемы:
    select '10' Name union
    select '1' Name union
    select '20' Name union
    select '2' Name
    order by length(Name), Name
    Ответ написан
    Комментировать
  • Как составить запрос sql?

    @alexalexes
    Бывает, нужно получить непустой результат при любых обстоятельствах.
    Но так делают, не потому что нет других средств, а просто лень выписывать это за пределами SQL.
    select A.*
      from (select t.col1, t.col2, ..., t.colN from t -- выборка для "не по умолчанию"
            union all 
            select def_col1, def_col2, ... def_colN from dual -- строка значений по умолчанию
           ) A
    where rownum = 1 -- выбираем только первую строку, неважно из чего

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

    @alexalexes
    Если нужно ограничить выборку по вычисляемой колонке case, а разбирать смысл ее лень (или вы в процессе отладки это делаете), то нужно взять этот кейс, перетащить в where и проверить на is not null. Все, что в условия кейса попадает - будет строковым значением (нужно глазками посмотреть, чтобы в кейсе не было исходов с null), а все что не попадает в его условия - обратится в null (если явно не прописан else).
    SELECT id,
    CASE
      WHEN price > 10000 AND price < 11000 THEN 'Economy'
      WHEN price >= 20000 AND price <= 30000 THEN 'PremiumEconomy'
      WHEN price > 100000 THEN 'Business'
    END as service_class, price
    FROM tickets
    where CASE
      WHEN price > 10000 AND price < 11000 THEN 'Economy'
      WHEN price >= 20000 AND price <= 30000 THEN 'PremiumEconomy'
      WHEN price > 100000 THEN 'Business'
    END is not null

    А если делать по уму, конечно, кейс оставлять в where - это крайняя мера. Его быть не должно, это ухудшит показатели плана выполнения запроса (не всегда). Нужно вытаскивать булеву конструкцию, желательно, логически упрощенную.
    SELECT id,
    CASE
      WHEN price > 10000 AND price < 11000 THEN 'Economy'
      WHEN price >= 20000 AND price <= 30000 THEN 'PremiumEconomy'
      WHEN price > 100000 THEN 'Business'
    END as service_class, price
    FROM tickets
    where (price > 10000 AND price < 11000) --  'Economy'
       or (price >= 20000 AND price <= 30000) --  'PremiumEconomy'
       or (price > 100000) -- 'Business'

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

    @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 комментарий
  • Как проверить принадлежат ли массивы из одной таблицы массивам в другой?

    @alexalexes
    С помощью функции json_to_recordset проводите нормализацию таблиц, чтобы на выходе pair получилась вот такая псевдотаблица-выборка:
    pair
    id_group, name
    1, "ананас",
    1, "апельсины",
    2, "ананас",
    2, "арбуз"
    ...
    Дальше тоже самое делаете с order_list.
    Ну, а дальше все приведено в 3-ю нормальную форму, можно, наконец, использовать SQL-ные сравнения/соединения этих псевдовыборок и высчитывать кол-ва того-сего.
    PS: Такое решение годится как разовое для получения результата, например, если вам нужно срочно сделать нестандартный отчет для руководства, а структура таблиц спроектирована из рук вон плохо.
    Нужно изначально планировать структуру базы так (приводить к 3-й нормальной форме), чтобы не прибегать к декомпозиции полей с помощью функций JSON, если компоненты внутри JSON будут использоваться для соединения между таблицами.
    Ответ написан
  • Как передавать один параметр, а не 3?

    @alexalexes
    FROM views v JOIN memes m JOIN comments c
    Вы конечно, не указали, в какой СУБД пишете, но странно видеть JOIN без ON или USING.
    Если это эквивалент синтаксиса с USING, то вы увидите статистику, если по пользователю есть И просмотры, И мемы, И комментарии. Если в одной из таблиц не будет записей, то пользователь вывалится из статистики.
    Если это эквивалент:
    FROM views v, memes m, comments c
    То такое декартово произведение таблиц с использованием distinct будет не производительно.
    Рационально вам написать такое:
    SELECT (select COUNT(v.id) from views v where v.user_id = u.user_id )      AS просмотры,
           (select COUNT(DISTINCT v.meme_id) from views v where v.user_id = u.user_id) AS [уникальные просмотры],
           (select COUNT(m.id) from memes m where m.user_id = u.user_id)      AS [выложено мемов],
           (select COUNT(c.id) from comments c where c.user_id = u.user_id)      AS [написано комментариев]
      FROM users u -- лучше связаться с таблицей users
     WHERE u.user_id = 1

    Тогда каждый подзапрос будет работать по своей агрегации (да, если по какой-то таблице не будет записей, то вы увидите по ней ноль, а не пустую строчку по всем параметрам), дистинктовать вам нужно только по параметру уникальные просмотры - но его использование не связано с размножением записей при объединении таблиц, так что он посчитает его достаточно быстро.
    PS: Если вы не забыли создать индексы для v.user_id, v.meme_id, m.user_id, c.user_id, то должно все летать на космической скорости.
    Ответ написан