Оценивать предикаты WHERE для аналитических функций перед другими предикатами (аналитические функции Oracle)

Задний план

Пример набора данных

#Employee
Id | Period | Status 
---------------------
1  |  1 |   L    
1  |  2 |   G    
2  |  3 |   L    

Я хочу, чтобы простой запрос на выборку выдавал последнюю запись о сотрудниках (по периодам), только если статус = 'L'.

Результаты будут выглядеть следующим образом:

#Desired Results
Id | Period | Status | Sequence
-------------------------------
2  |  3     |   L    |   1

Наивная попытка

Очевидно, моя наивная попытка запроса не работает:

#select query
SELECT *, RANK() OVER (PARTITION BY id ORDER BY period ASC) sequence
FROM employees
WHERE   status = 'L' 
 AND    sequence = 1

Что приводит к следующему:

#Naive (incorrect) Results
ID | Period | Status | Sequence
-------------------------------
1  |  1 |   L    |   1
2  |  3 |   L    |   1

Знание порядка, в котором выражения оцениваются в SQL, объясняет, почему это не работает. Вот как оценивается мой запрос:

  • Изолировать строки, где status='L'
  • Ранжируйте строки
  • Изолировать строку верхнего ранга

Я хочу следующее:

  • Ранговые строки
  • Изолировать строки с верхним рейтингом
  • Изолировать, где status='L'

Вопросы

  • Возможно ли — с помощью простой модификации предложений SELECT/WHERE и использования только основных операторов предикатов — гарантировать, что предикаты, основанные на аналитических функциях в предложении WHERE, будут оцениваться раньше, чем неагрегированные предикаты?

  • У кого-нибудь есть другие решения, которые могут быть реализованы конечным пользователем в Oracle Discoverer Plus?

Спасибо!


person J. Christian    schedule 25.03.2011    source источник
comment
Эта часть непонятна without derived tables, sub-queries, joins, or a GROUP BY clause? Итак, вы хотите испечь пирог без муки, молока или яиц?   -  person RichardTheKiwi    schedule 26.03.2011
comment
Почему вы не хотите использовать производную таблицу или подзапрос?   -  person a_horse_with_no_name    schedule 26.03.2011
comment
У меня особые ограничения. Я пытаюсь реализовать этот запрос как конечный пользователь в Oracle Discoverer Plus, построителе запросов с графическим интерфейсом. В Discoverer у меня нет доступа к переопределению SQL или EUL (уровень конечного пользователя), которые контролируют все соединения. Итак, я ищу обходной путь.   -  person J. Christian    schedule 26.03.2011
comment
Столкнувшись с такими инструментами, я считаю, что проще всего просто создать VIEW, которое предоставляет данные, необходимые в виде простого объекта для выбора инструментом.   -  person RichardTheKiwi    schedule 26.03.2011
comment
Я согласен с Ричардом - создайте для этого представление (в основном производную таблицу в моем примере)   -  person a_horse_with_no_name    schedule 26.03.2011
comment
Я абсолютно согласен. Однако я не могу создавать представления, потому что представлениями управляет EUL. Я предполагаю, что еще одним ограничением является то, что я пытаюсь не вовлекать администратора. Однако спасибо за эти решения. Есть ли у нас, по крайней мере, консенсус в отношении того, что это невозможно только (1) модификацией предложений SELECT или WHERE и (2) только самых основных предикатов (=,‹›,IN,LIKE,) в WHERE? пункт?   -  person J. Christian    schedule 26.03.2011
comment
Баго - 100% сертифицирован. Вам не нужно использовать EUL для создания представления. Подключитесь напрямую к Oracle и создайте его.   -  person RichardTheKiwi    schedule 26.03.2011
comment
Разве RANK() не должен быть ORDER BY период DESC? Разве период 2 (со статусом G) не является последней записью для идентификатора сотрудника 1?   -  person beach    schedule 05.05.2011


Ответы (3)


Можно ли это сделать без подзапроса

Технически это не подзапрос, а производная таблица.

SELECT * 
FROM (
    SELECT *, 
           RANK() OVER (PARTITION BY id ORDER BY period ASC) sequence
    FROM employees
) t
WHERE status = 'L' 
  AND sequence = 1

Я не могу придумать другого решения вашей проблемы.

person a_horse_with_no_name    schedule 25.03.2011

Классическая группа по

SELECT e.id, e.period, e.status, 1 sequence
FROM
(
    SELECT id, min(period) period
    FROM employees
    GROUP BY id
) X
JOIN employees e on e.period=X.period and e.id=X.id
WHERE e.status = 'L'

Существуют

select e.id, e.period, e.status, 1 sequence
FROM employees e
WHERE e.status = 'L'
  AND NOT EXISTS (select *
                  from employees e2
                  where e2.id=e.id and e2.period>e.period)
person RichardTheKiwi    schedule 25.03.2011
comment
Спасибо, Ричард. К сожалению, я не могу использовать предикаты EXISTS или GROUP BY. - person J. Christian; 26.03.2011

Мне, наверное, придется сделать "Добби" и прихлопнуть ухом дверцей духовки и погладить руки для этого...

Вы можете создать функцию, которая оценивает текущую строку.
Обратите внимание, что она по своей сути не масштабируема. Но я думаю, это лучше, чем ничего.

Создайте образец данных:

--drop table employee purge;

create table employee(
    id     number  not null
   ,period number  not null
   ,status char(1) not null
   ,constraint employee_pk primary key(id, period)
);

insert into employee(id,period, status) values(1, 1, 'L');
insert into employee(id,period, status) values(1, 2, 'G');
insert into employee(id,period, status) values(2, 3, 'L');

commit;

Создайте самую медленную функцию в базе данных:

create or replace function i_am_slow(
    ip_id     employee.id%type
   ,ip_period employee.period%type
)
return varchar2
as
   l_count number := 0;
begin
    select count(*)
      into l_count
      from employee e
     where e.id     = ip_id
       and e.period = ip_period
       and e.status = 'L'
       and not exists(
            select 'x'
              from employee e2
             where e2.id = e.id
               and e2.period > e.period);

    if l_count = 1 then
        return 'Y';
    end if;

    return 'N';
end;
/

Демонстрирует использование функции:

select id, period, status
  from employee
 where i_am_slow(id, period) = 'Y';

        ID     PERIOD STATUS
---------- ---------- ------
         2          3 L

Бросается к печи...

person Ronnis    schedule 25.03.2011
comment
К сожалению, это решение не работает в рамках моих ограничений. Смотрите обновленный вопрос и комментарии к вопросу для получения более подробной информации. Спасибо за нестандартное мышление! - person J. Christian; 26.03.2011
comment
Однако ограничение со сварливым администратором базы данных не является реальным. Если отчет стоит реализовать, представление должно быть создано. Не будьте парнем, который тратит деньги вашей компании :) - person Ronnis; 26.03.2011
comment
Я не думаю, что будет справедливо сказать, что я трачу впустую деньги своей компании. Теперь я понимаю, что то, о чем я прошу, - это очень плохой хак в SQL, но, возможно, изящный обходной путь в Discoverer, который уменьшит нагрузку на техническое обслуживание. Однако я недостаточно знал SQL, чтобы сделать это. Я отредактирую пост соответствующим образом, чтобы он был ориентирован на первооткрывателей. - person J. Christian; 26.03.2011
comment
Однако теперь я также понимаю, что мне следует работать с моим администратором баз данных, чтобы найти передовое решение, вместо того, чтобы просить Stack Overflow провести мозговой штурм для решения моей неясной проблемы. Спасибо. - person J. Christian; 26.03.2011
comment
@Bago, я перечитал свой комментарий и понял, что он звучит грубо, что не входило в мои намерения. Я рад, что вы решили решить проблему вместе со своим администратором баз данных. Вы двое найдете хорошее решение. Я тоже придумываю ограничения, когда их нет, чтобы спасти документы/встречи, а иногда и по незнанию, но, к счастью, люди вокруг меня тыкают мне в глаза, прежде чем я стану парнем, который тратит деньги впустую ;) - person Ronnis; 26.03.2011