Ответы пользователя по тегу SQL
  • Как сделать добавление в избранное?

    @alexalexes
    20 таблицами в которых рассортированы сериалы по разным жанрам.

    Есть подозрение, что у вас структура таблиц не отвечает условиям нормализации (гуглите 1, 2, 3-я нормальная форма). Чтобы помечать фильмы одним или несколькими жанрами, достаточно 3 таблицы (гуглите как реализовать связь "многим-ко-многим"), в независимости от кол-ва жанров и фильмов.
    Чтобы хранить избранное, достаточно одной таблицы, в независимости от кол-ва пользователей.
    Таблица Fav_movie:
    id - идентификатор записи избранного,
    user_id - идентификатор пользователя
    movie_id - идентификатор фильма
    add_date - дата и время добавления
    position - поле для определения приоритета сортировки в пределах одного пользователя
    Ответ написан
  • Как автоматизировать выполнение sql-запросов?

    @alexalexes
    Зависит от СУБД, а не от IDE. Есть ли в СУБД планировщик запросов по расписанию.
    А универсальный способ - это наличие любой среды выполнения, способной вызывать скрипт по расписанию - хоть shell, хоть cron + php/python, хоть планировщик windows + любой exe со скомпилированным запросом.
    Ответ написан
    7 комментариев
  • Почему не работает группировка SQL?

    @alexalexes
    Вероятно, не кастует год для группировки.
    Вместо:
    group by year ...
    нужно:
    group by EXTRACT(year from CAST(invoice_date as date)) ...

    Если функция extract не пригодна для группировки, то сделайте еще одну обертку подзапроса, чтобы на верхнем уровне год оказался уже вычисленным полем.
    Ответ написан
  • Можно ли пользоваться Ajax с помощью SQL?

    @alexalexes
    В зависимости от требования к скорости доставки изменений для всех пользователей, то эту задачу можно решить разными способами.
    1. Низкие требования к скорости оповещения - "когда пользователь решит сам обновить или перейти на другую страницу, тогда и выведем новое состояние (новый статус, новое сообщение)".
    Классическая форма взаимодействия браузера с сервером.
    Один пользователь набирает сообщение в обычной форме (без ajax). Отправляют его на сервер. Сервер сохраняет его в любой форме. Когда другие пользователи запрашивают у сервера страницу с сообщением или статусом, то они получают новое состояние, если сами сделали какое-то действие, связанное с переходом на другую страницу.
    Так работают старые веб почтовики и форумы 2000-х годов.
    2. Средние требования к скорости оповещения - "пользователь не должен предпринимать никаких действий, чтобы он получил новое состояние, но получать состояние можно раз в несколько минут и более".
    Один пользователь набирает сообщение, отправляет на сервер. А другие пользователи запрашивают новое состояние страницы у сервера по технологии ajax. Проблема в том, что инициатором получения изменений от первого пользователя являются клиентская часть приложения пользователей, которые ждут изменений на сервере, а не приложение сервера. Поэтому когда первый пользователь отправит сообщение, то другие пользователи получат его не сразу, а когда наступит следующий период опроса сервера.
    Это требование подходит для интернет магазинов, чтобы отслеживать статус заказа, новые почтовики.
    3. Высокие требования к скорости оповещения - "пользователь должен незамедлительно получать изменения от сервера, как только другой пользователь сделает действие".
    Вот тут уже на стороне ожидающих пользователей работает технология websocket. Клиентская часть этих пользователей создает соединение с сервером и ждет от него отклика, когда другой пользователь напишет сообщение. Постоянные опросы состояния, как в случае 2 таким пользователям проводить не нужно.
    Но для websocket есть ограничения, что не каждый веб сервер для него годится. Например, с PHP его сложнее подружить, чем с NodeJS. Вся проблема в том, что PHP не приспособлен для обработки множества запросов на одно соединение, в NodeJS и других средах, где такая концепция заложена изначально, проблем не будет с реализацией.
    С такими требованиями работают современные чаты в любых мессенджерах.
    Ответ написан
    Комментировать
  • Как сравнить по времени в таблицах, отбросив минуты и секунды до часов?

    @alexalexes
    Select t1.ID, t1.TIME
    from 
    TABLE_1 t1 
    JOIN TABLE_2 t2 ON t1.ID=t2.ID
                  and to_char(t1.TIME, 'DD.MM.YYYY HH24') = to_char(t2.TIME, 'DD.MM.YYYY HH24')

    Вариант 2 (возможно, пригодится при оптимизации, так как на выходе сравниваются значения в типе дата время, а не строк):
    ... and trunc(t1.TIME, 'hh24') = trunc(t2.TIME, 'hh24')

    PS: Хорошо бы еще индекс создать под этот формат времени для колонок t1.TIME и t2.TIME, тогда запрос будет всегда производителен.
    Ответ написан
    4 комментария
  • В каком виде хранят данные Гугл-документы и похожие сервисы?

    @alexalexes
    Только не пытайтесь в базе хранить буквальное представление ежедневника как двумерной сетки из квадратиков - где пишут заметки на определенный день. Сетка ежедневника - это просто удобная форма вывода данных, но не способ их хранения.
    Хранится это все будет, скорее всего линейно и банально списком заметок с заданными начальными и конечными интервалами:
    Таблица заметок:
    id - идентификатор заметки
    begin_date - начало интервала
    end_date - конец интервала
    comment - заметка
    Вот это минимум, что будет содержаться в базе данных в ее таблице.
    А уже как будете получать данные этой таблицы и строить представление по дням, рисовать календарик - это уже не задача СУБД, а приложения, которое будет использовать эти данные.
    Ответ написан
    2 комментария
  • Как написать команду SQL?

    @alexalexes
    SELECT `user_id`, `username`, `first_name`,`last_name`, `title`
      FROM `userdata`
    WHERE `type` in ('supergroup', 'group')

    или то же самое условие:
    SELECT `user_id`, `username`, `first_name`,`last_name`, `title`
      FROM `userdata`
    WHERE `type` = 'supergroup'
       OR `type` = 'group'

    Если user_id - это первичный ключ, то пользователь может одновременно быть только одним типом. С and что-то писать бессмысленно, если, конечно, в этой таблице не нарушена нормализация данных.
    Ответ написан
    Комментировать
  • Как правильно оформить sql-запрос чтобы он был читаемый?

    @alexalexes
    62d2477b7afb0731475726.png
    У меня сформировалась такая модель оформления запросов sql.
    1. Один уровень запроса я насаживаю на ось. Слева от оси - клаузы синтаксиса sql: select, update, insert, delete, from, set, join-ы всех разновидностей, where, order by, group by и так далее. Справа от оси - атрибуты запроса, скобки следующего уровня запроса.
    2. В выражениях оконных функций делаю ось относительно partition by/order by.
    3. Булевые выражения после on или where выравниваю так:
    - операторы первого приоритета (and) идут в левую часть оси;
    - сравнения и следующий уровень более низкого приоритета (or) - в правую часть оси.
    4. наименования join сокращаем максимально кратко:
    - вместо inner outer join просто join,
    - вместо left outer join - left join и так далее.
    5. В части join...on присоединяемые поля таблицы ставлю в левую часть равенства в булевых выражениях:
    join t1 on t1.a_id = t2.id
               and t1.begin_date < t2.end_date

    но не так:
    join t1 on t2.id = t1.a_id
               and t2.end_date > t1.begin_date

    6. Наименования атрибутов, которые относятся к одной таблице в текущем уровне или прошлом, или имеющие по смыслу более связанное значение, можно писать в одну строчку:
    select a.id, a.name,
             b.position
             ...

    7. Запятые в перечислениях атрибутов удобно иметь в начале строки, когда запрос изменяют каждый день или находится в стадии активной разработки. Но постоянно просматривать такой запрос в режиме изучения, на мой взгляд - неудобно. Поэтому, когда активная фаза исправлений заканчивается, запятые перемещаю в конец строки.
    select a.id
              , a.name
             , b.position

    после:
    select a.id, a.name,
              b.position

    Итог. Эта модель не идеальна. Нужно делать отступы для select и from на первом уровне, чтобы посадить на ось where, group by, order by; также join-ы следующего уровня напирают на ось предыдущего.
    PS: Ваш запрос я бы оформил так:
    62d24e2e5ddec369552006.png
    Ответ написан
    Комментировать
  • Как удалить строку в бд через Read Bean Php?

    @alexalexes
    Без понимания чистого SQL прослойка ORM в виде Read Bean крайне неочевидной вещью становится:
    function claim_message($msg, $msgFh, $msgTh, $uuid)
    {
      $var = R::findOne("messages", "msg_th = ? AND is_read = 0'", [$msgTh]);
      if(!is_null($var))
      {
        $delete = R::load("messages", $var->id);
        R::trash($delete);
        return $msg;
      }
      return "null; $msgTh , $msgFh";
    }

    А где тут цикл?
    Ответ написан
  • Как наисать условие where с множесвом параметров?

    @alexalexes
    SELECT a.*, ai.name_img_file, ai.src, ai.title as img_title 
    FROM adverts as a
    join  advert_imgs as ai on a.id = ai.id_adv 
    WHERE 1 = 1
        AND exists( select 1 from adverts_fields af where a.id = af.id_advert and  af.field_name = 'storey_apartament' AND af.field_value = '58')
        AND  exists( select 1 from adverts_fields af where a.id = af.id_advert and af.field_name = 'condition_house' AND af.field_value = '13')
        AND  exists( select 1 from adverts_fields af where a.id = af.id_advert and af.field_name = 'Sleeping'AND af.field_value = '38')
        AND  exists( select 1 from adverts_fields af where a.id = af.id_advert and af.field_name = 'Availability' AND af.field_value = '48')
        AND  exists( select 1 from adverts_fields af where a.id = af.id_advert and  af.field_name = 'Bathroom' AND af.field_value = '77')
        AND  exists( select 1 from adverts_fields af where a.id = af.id_advert and af.field_name = 'Heating'AND af.field_value = '26')
        AND  exists( select 1 from adverts_fields af where a.id = af.id_advert and af.field_name = 'Furnished' AND af.field_value = '14')
        AND  exists( select 1 from adverts_fields af where a.id = af.id_advert and af.field_name = 'kitchen_area' AND af.field_value = '34')
    ORDER BY `title` DESC

    У вас свойства одного объекта "a" хранятся не в одной записи "af", а в нескольких, которые связаны внешним ключом через af.id_advert. Поэтому, чтобы посмотреть каждое свойство фильтра, нужно подзапросом exists пройтись по таблице свойств столько раз, сколько критериев в фильтре.
    А то, что вы написали - то проверит одну запись свойства и отсечет выборку на следующем AND проверки критериев, поскольку field_name и field_value в одной цепочке and проверит на разные значения, но обратить такую цепочку and в true невозможно.
    Ответ написан
    1 комментарий
  • Как найти пересечение смен?

    @alexalexes
    Попробуйте так:
    select A.point_id, A.schedule_calendar_id, A.date, count(*) cnt
    from (
    select distinct s.user_id, s.point_id, s.schedule_calendar_id, sc.date
    from schedule s
    join schedule_calendar sc on s.schedule_calendar_id = sc.id
    ) A
    group by A.point_id, A.schedule_calendar_id, A.date
    having count(*) > 1
    Ответ написан
    Комментировать
  • Как сделать правильно выборку с двойной сортировкой?

    @alexalexes
    Чтобы решить задачу, нужно иметь ввиду, что order by может не только напрямую работать с колонками таблиц, но с любым вычисляемым выражением в пределах свойств одной результирующей строки.
    В данном случае, вам нужно в приоритете отсортировать результат выборки по ms2.availability > 0 и ms2.availability = 0, но не беря во внимание вес значения ms2.availability.
    Можно использовать выражение ms2.availability = 0, выводящее true или false, которое будет обработано order by.
    SELECT ms2.*, ec.rating
    FROM `rt_ms2_products` AS `ms2`
    LEFT JOIN `rt_ms2_reviews` AS `ec` ON ms2.id=ec.id
    GROUP BY ms2.id
    ORDER BY ms2.availability = 0, ec.rating DESC
    LIMIT 0, 20

    Впрочем, если попадется СУБД, где нельзя работать с булевым типом в order by, то выручит case...end, результат который даст 0 или 1 - это универсальный подход.
    SELECT ms2.*, ec.rating
    FROM `rt_ms2_products` AS `ms2`
    LEFT JOIN `rt_ms2_reviews` AS `ec` ON ms2.id=ec.id
    GROUP BY ms2.id
    ORDER BY case when ms2.availability = 0 then 1 else 0 end, ec.rating DESC
    LIMIT 0, 20
    Ответ написан
    9 комментариев
  • Как отфильтровать таблицу по столбцу значение которого входит/не входит в список значений?

    @alexalexes
    Вариант 1:
    Пришить NOT в текст запроса, в зависимости от состояния p1:
    "SELECT * FROM [dbo].[data] WHERE ([dbo].[data].[intField] " + (p1 ? "" : "NOT") + " IN (0,1,2,8,9))"

    Вариант 2, если MS SQL поддерживает тип bool в SQL, то такое выражение:
    SELECT * FROM [dbo].[data]
          WHERE @p1 = true and [dbo].[data].[intField] IN (0,1,2,8,9)
                  or @p1 = false and [dbo].[data].[intField] NOT IN (0,1,2,8,9)

    Вариант 2.1, если MS SQL не поддерживает тип bool в SQL, то нужно p1 придать значение 0/1 вместо false/true, тогда выражение:
    SELECT * FROM [dbo].[data]
              WHERE @p1 = 0 and [dbo].[data].[intField] IN (0,1,2,8,9)
                      or @p1 = 1 and [dbo].[data].[intField] NOT IN (0,1,2,8,9)
    Ответ написан
    Комментировать
  • Как используя такую конструкцию .* в sql можно дать название подстолбцам, которые будут выведены?

    @alexalexes
    И мне нужно использовать такую конструкцию:
    select food.* from table

    Странное требование, но можно обойти оборачиванием.
    select food.*
    from(select t.Milk as food_Milk, t.Oil as food_Oil
            from table as t) as food
    Ответ написан
  • Как правильно создать таблицу в которой будет храниться инфо о посте?

    @alexalexes
    Если вы хотите просто считать сколько лайков и дизлайков поставили посту, то просто введите две новые колонки в таблице постов.
    Если хотите журналировать выставление лайков и дизлайков и потом заниматься аналитикой, сколько их выставили в час, день, неделю, месяц, то создайте отдельную таблицу для лайков:
    Таблица ЛАЙКИ
    id_лайка,
    id_поста,
    id_типа_лайка, -- лайк/дизлайк
    дата_время_выставления
    Но даже журналируя лайки, можно вести их учет количества в отдельных колонках поста - одно другому не мешает.
    Других оптимальных решений реляционная модель предложить не сможет для вашей задачи.
    Ответ написан
    Комментировать
  • Как правильно организовать ссылку на значение в связанной таблице?

    @alexalexes
    Нет смысла. Вы не для красоты в таблице Book завели свойство author_id, чтобы связывать по нему строку из таблицы Author, тем самым получать доступ ко всем свойствам по идентификатору author_id.
    PS: А вы уверены что у книжки строго один автор? Или это для вас просто учебный пример?
    Ответ написан
  • Как сделать, чтобы неподходящие под условие не забивали лимит?

    @alexalexes
    Вариант А.
    Вы не можете гарантировать, что любой файл изображений, сведения о которых храните в базе, будет доступен физически в любой момент.
    Придется неопределенное число раз постучаться в базу и протестировать каждую запись о файле, существует ли он физически. При тесте формируем список мертвых и живых файлов. После того, как протестировали нужное кол-во живых файлов, можно делать итоговый запрос с оглядкой на список мертвых файлов.
    $need_count = 10;  // сколько требуется файлов для выборки
    $alive_count  = 0;  // сколько живых файлов
    $is_need_repeat = true; // требуется повторить попытку получить живые файлы
    $death_list = []; // сюда накапливаем список id мертвых файлов
    $alive_list = []; // сюда накапливаем список id живых файлов
    while($is_need_repeat) // Если можно делать итерационную попытку и пока не набрали нужное количество живых файлов
    {
    // Этот запрос, чтобы прощупать целостность файлов, достаточно получить только те атрибуты, которые позволяют проверить его путь и запомнить id.
    $database->setQuery("
        SELECT id
        from блаблабла
        WHERE блаблабла 
                   ".(count($depth_list) > 0 ? : ' and id not in ('.join(',',$death_list).') ' : '')." -- отсеиваем мертвые файлы из запроса, они нам не нужны
                   ".(count($alive_list) > 0 ? : ' and id not in ('.join(',',$alive_list).') ' : '')." -- отсеиваем живые файлы из запроса, мы их уже проверяли
        ORDER блаблабла
    limit 0,".($need_count - $alive_count)); // делаем лимит по оптимистичному сценарию, как будто можем получить список файлов, и все они будут живые, но только то кол-во, которое недостает
    while($row = mysql_fetch_assoc($request))
    {
      if(file_exists('/www/ПУТЬ/'.$row['id'].'_100.jpg'))
      {
        $alive_list[] = $row['id']; // файл живой, заносим его id в список
      }
      else
      {
        $death_list[] = $row['id']; // файл мертвый, заносим его id в список
      }
      $curr_alive_count = count($alive_list);
      $is_need_repeat = $curr_alive_count > 0 && $curr_alive_count > $alive_count && $curr_alive_count < $need_count; // необходимо продолжить попытки, если на текущей итерации получили хоть один живой файл, живых файлов на этой итерации оказалось больше, чем на предыдущей, и их кол-во не достаточно до необходимого
      $alive_count = $curr_alive_count; // вписываем кол-во живых файлов на текущей итерации для проверки в будущем цикле (чтобы сравнить результаты двух циклов)
    }
    }
    // теперь можно сделать нормальный запрос, исключив мертвые файлы:
    $database->setQuery("
        SELECT *
        from блаблабла
        WHERE блаблабла 
                   ".(count($depth_list) > 0 ? : ' and id not in ('.join(',',$death_list).') ' : '')." -- отсеиваем мертвые файлы из запроса
        ORDER блаблабла
    limit 0,".$need_count);

    Вариант Б.
    Вы можете гарантировать, что контролируете целостность файлов.
    Тогда в таблице изображений делаете колонку is_del. Когда удаляете файл, вы должны пометить запись о файле в базе как удаленную по этому атрибуту.
    Если вы все таки частично контролируете целостность, то в определенный период времени (например, запускать скрипт по cron раз в час, сутки) вам нужно пройтись по всему списку файлов в базе и проверить целостность каждого файла, и внести актуальную метку is_del.
    Тогда получать живые файлы будет чуть-чуть проще:
    $database->setQuery("
        SELECT *
        from блаблабла
        WHERE блаблабла 
               and is_del is null -- или нулю, в зависимости, что будет по умолчанию
        ORDER блаблабла
    limit 0,".$need_count);
    Ответ написан
    4 комментария
  • Как сделать поиск в двух таблицах?

    @alexalexes
    т.е. если в таблице wp_reviews запись с company_id=2001 не существует, то для таблицы wp_posts для строки с ID=2001 обновляем post_status на 'draft'

    Как бы, у вас уже на 80% запрос сформирован на естественном языке пригодный для прямого транслирования в SQL. Как-то стыдно не записать его прямым текстом:
    update wp_posts p
    set p.post_status = 'draft'
    where not exists (select 1 from wp_reviews r where r.связующий_ключ = p.связующий_ключ)

    Осталось определиться, как называются ключи в той и другой таблице, связывающие данные этих таблиц.
    PS: вероятно это: r.company_id = p.id.
    Ответ написан
  • Как сделать сортировку?

    @alexalexes
    Добавьте еще два параметра в ту часть запроса, которая отвечает за сортировку.
    order_column - по какой колонке сортировать, order_direct - в каком направлении.
    Пример с limit намекает, как это сделать.
    $limit = 'limit 0, 500';
    $order_column = 'added';
    $order_direct = 'desc';
    if(isset($_GET['order_col']))
    {
      if($_GET['order_col'] == 'updated')
        $order_column = 'updated'; // не вздумайте подставлять из GET название колонки, будет sql-инъекция!
    }
    if(isset($_GET['order_dir']))
    {
      if($_GET['order_dir'] == 'asc')
        $order_direct = 'asc'; // не вздумайте подставлять из GET название клаузы, будет sql-инъекция!  
    }
            if (isset($_GET['ajax2'])) $limit = 'limit '.(int)$_GET['offset'].', 30'; // тут застраховано от инъекции при помощи преобразования в int!
    
            $rows = fs::getObjects($sql, "order by c_object.".$order_column." ".$order_direct." ".$limit);
            $rowsCount = count(fs::getObjects($sql));
    
            if (isset($_GET['ajax2']) && !count($rows))
            {
              header("HTTP/1.0 404 Not Found");
              die;
            }

    ПС: Осталось вам дополнить вопрос, "а где мне найти шаблон формы с фильтром списка, чтобы прописать get-параметры order_col и order_dir".
    Ответ написан
    Комментировать
  • Как переделать запрос для получения нужного ответа?

    @alexalexes
    Как-то, так.
    SELECT
    city.id,
    city.name,
    country.name,
    salary,
    country.is_here
    FROM city
    LEFT JOIN country ON city.cid = country.id
    union all
    SELECT
    city.id,
    city.name,
    country.name,
    salary + 2000,
    1
    FROM city
    LEFT JOIN country ON city.cid = country.id
    where (country.id is null or country.is_here = 0)
    Ответ написан
    Комментировать