Показаны сообщения с ярлыком Задачи на PL/SQL от потенциальных работодателей. Показать все сообщения
Показаны сообщения с ярлыком Задачи на PL/SQL от потенциальных работодателей. Показать все сообщения

воскресенье, 4 сентября 2022 г.

Отличие подзапросов в NOT EXISTS и NOT IN (...)

В чем есть логическое отличие использования подзапросов в NOT EXISTS и логическом операторе NOT IN (...) в следующих двух SQL-запросах ?  

SELECT *
FROM   facts f
WHERE  f.id NOT IN (SELECT idnum
                    FROM   dict); 

SELECT *
FROM   facts f
WHERE  NOT EXISTS (SELECT 1
                   FROM   dict d
                   WHERE  d.idnum = f.id); 

Ответ:  если подзапрос (select idnum from dict) в запросе с NOT IN возвращает  хотя бы одно значение NULL, то всё логическое выражение становится ложным, т.е. весь запрос ничего не вернёт. При использовании EXISTS, значения NULL обрабатываются корректно, т.е запрос вернёт какие-то строки при их наличии. 

Я бы сказал, что речь идёт о некоем баге, так как при использовании подзапроса в логическом IN знечение NULL не оказывает такого фатального эффекта, т.е. логическое условие выполняется как обычно логично корректно. 

пятница, 16 июля 2021 г.

Рассчитать стоимости возможных маршрутов

На информационной панели представлено несколько прямых маршрутов. У каждого своя стоимость. Есть маршруты, по которым доехать на автобусе с пересадками выгоднее, чем прямой рейс. Задача для программиста:  написать SQL-запрос, который выводит все возможные маршруты начиная с города указанного, как место отправления. Для каждого маршрута необходимо подсчитать стоимость. Если есть несколько маршрутов до одного пункта назначения, вывести самый дешёвый из них. Обратные маршруты не выводить. 

Автобусные маршруты представлены в виде таблицы:

ID ORIGIN DESTINATION COST
1 MSO SPB 500
2 MSO KRD 200
3 MSO SRG 400
4 KRD SRG 100
5 KRD RVD 200
6 RVD VVK 1000
7 VVK OCN 50

Результат работы запроса должен выглядеть аналогично этому:


Один из возможных SQL-запросов для решения данной задачи:

with function getpathsum(sumpath varchar2) return number is v_res number;

  begin

      with liststr as

       (select regexp_substr(sumpath, '[^/]+', 1, level) c1

          from dual

        connect by level <= regexp_count(sumpath, '/'))

      select sum(cost)

        into v_res

        from liststr

            ,busroutes

       where origin || '-' || destination = c1;

      return v_res;

  end;

  busroutes_cost as(

      select cbr

            ,path

            ,cost

            ,getpathsum(path) as routecost

       from (select connect_by_root(f.origin) || '-' || f.destination as cbr

                    ,sys_connect_by_path(f.origin || '-' || f.destination, '/') as path

                    ,f.cost

                from busroutes f

              connect by prior f.destination = f.origin)),

  busroutes_cost_sort as (

      select cbr

             ,path

             ,routecost

             ,row_number() over(partition by cbr order by routecost) as rn

         from busroutes_cost)

select cbr

      ,path

      ,routecost

  from busroutes_cost_sort

where rn = 1



Разбор запроса.

Подзапрос возвращает все возможные маршруты:

select connect_by_root(f.origin) || '-' || f.destination as cbr

      ,sys_connect_by_path(f.origin || '-' || f.destination, '/') as path

      ,f.cost

  from busroutes f

  connect by prior f.destination = f.origin


Выражение  connect_by_root(f.origin) || '-' || f.destination as cbr  представлет начало и окончание маршрута.
Выражение  sys_connect_by_path(f.origin || '-' || f.destination, '/') as path  представлет все прямые рейсы, входящие в маршрут .

Встроенная в запрос функция рассчитывает стоимость каждого маршрута. Функция принимает значение в виде /MSO-KRD/KRD-RVD, ищет стоимость каждого прямого маршрута и суммирует их. 

with function getpathsum(sumpath varchar2) return number is v_res number;

  begin

      with liststr as

       (select regexp_substr(sumpath, '[^/]+', 1, level) c1

          from dual

        connect by level <= regexp_count(sumpath, '/'))

      select sum(cost)

        into v_res

        from liststr

            ,busroutes

       where origin || '-' || destination = c1;

      return v_res;

  end;



Функция проставляет порядковый номер в пределах уникального маршрута, предварительно отсортировав их по стоимости: 1 - самому дешёвому маршруту. Далее, выбираются маршруты, у которых rn = 1 


,row_number() over(partition by cbr order by routecost) as rn