Oracle SQL для непрерывной группировки

Компилятору позволяют сделать одно неявное преобразование для разрешения параметров к функции. То, что это означает, - то, что компилятор может использовать конструкторов, вызываемых с единственный параметр для преобразования от одного типа до другого для получения правильного типа для параметра.

Вот класс в качестве примера с конструктором, который может использоваться для неявных преобразований:

class Foo
{
public:
  // single parameter constructor, can be used as an implicit conversion
  Foo (int foo) : m_foo (foo) 
  {
  }

  int GetFoo () { return m_foo; }

private:
  int m_foo;
};

Вот простая функция, которая берет Foo объект:

void DoBar (Foo foo)
{
  int i = foo.GetFoo ();
}

и вот то, где эти DoBar функция вызвана.

int main ()
{
  DoBar (42);
}

аргумент не Foo объект, а int. Однако там существует конструктор для Foo, который берет int, таким образом, этот конструктор может использоваться для преобразования параметра в корректный тип.

компилятору позволяют сделать это однажды для каждого параметра.

Добавление префикса explicit ключевое слово конструктору препятствует тому, чтобы компилятор использовал того конструктора для неявных преобразований. Добавление его к вышеупомянутому классу создаст ошибку компилятора при вызове функции DoBar (42). Теперь необходимо призвать к преобразованию явно с [1 111]

причина, которую Вы могли бы хотеть сделать, это должно избежать случайной конструкции, которая может скрыть ошибки. Изобретенный пример:

  • Вы имеете MyString(int size) класс с конструктором, который создает строку данного размера. У Вас есть функция print(const MyString&), и Вы звоните print(3) (когда Вы на самом деле намеревались звонить print("3")). Вы ожидаете, что это распечатает "3", но это печатает пустую строку длины 3 вместо этого.
6
задан p.campbell 12 August 2009 в 23:16
поделиться

8 ответов

Допустим, меня вдохновил Эндрю из NZSG. Я также сделал функцию с облицовкой трубы.

create or replace package testpl is

 type outrec_type is record
 ( from_id ticket.id%type
 , to_id   ticket.id%type
 , assigned_to ticket.assigned_to%type);

 type outrec_table is table of outrec_type;

 function pltest return outrec_table pipelined;

end;
/

create or replace package body testpl is

  function pltest return outrec_table pipelined
  is
    l_outrec outrec_type;
    l_first_time boolean := true;
  begin

     for r_tick in (select id, assigned_to from ticket order by id) loop

       if (r_tick.assigned_to != l_outrec.assigned_to or l_first_time) then
          if not l_first_time then
            pipe row (l_outrec);
          else
            l_first_time := false;
          end if;
          l_outrec.assigned_to := r_tick.assigned_to;
          l_outrec.from_id := r_tick.id;
       end if;
       l_outrec.to_id := r_tick.id;
    end loop;

    pipe row (l_outrec);

    return;
  end;

end;
/

Вы можете проверить это с помощью:

select * from table(testpl.pltest);

Это примерно в два раза быстрее, чем решение Роба ван Вейка в моей системе Windows XP Oracle 11.1.0.6.0.

Конструкция

for r_tick in (select ....) loop
  ....
end loop;

имеет очень приличную производительность в Oracle 10 и 11. В большинстве случаев решения только на SQL работают быстрее, но я думаю, что здесь PL / SQL быстрее.

3
ответ дан 8 December 2019 в 12:22
поделиться

Вам необходимо убедиться, что другие пользователи этого репозитория не загружают неправильные изменения или не пытаются строить на основе коммитов, которые вы хотите удалить, потому что вы собираетесь перемотать историю.

Затем вам нужно «принудительно» нажать старую ссылку.

git push -f origin last_known_good_commit:branch_name

или в вашем случае

git push -f origin cc4b63bebb6:alpha-0.3.0

Вы можете установить receive.denyNonFastForwards в удаленном репозитории. В этом случае вы получите сообщение об ошибке, содержащее фразу [удаленный отказ] .

В этом сценарии вам придется удалить и заново создать ветку.

git push origin :alpha-0.3.0
git push origin cc4b63bebb6:refs/heads/alpha-0.3.0

Если это не так. не работает - возможно, из-за того, что у вас установлен receive.denyDeletes , тогда у вас должен быть прямой доступ к репозиторию. Затем в удаленном репозитории вам нужно выполнить что-то вроде следующей команды сантехники. Роб.

7
ответ дан 8 December 2019 в 12:22
поделиться

Ладно, это некрасиво, но работает. И никто еще не сделал ничего более красивого, так что, возможно, это способ сделать это.

select min(from_id), to_id, assigned_to from
(
select from_id, max(to_id) as to_id, assigned_to from
(
select t1.id as from_id, t2.id as to_id, t1.assigned_to
from       ticket t1
inner join ticket t2 on t1.assigned_to = t2.assigned_to and t2.id >= t1.id
where not exists
    (
    select * from ticket t3
    where  t3.ID > t1.ID
    and t3.ID < t2.ID
    and t3.assigned_to != t1.assigned_to
    )
) x
group by from_id, assigned_to
) y
group by to_id, assigned_to
;

Я использую mysql; вполне может быть некоторая добродетель оракула, которая делает это лучше - как вполне может быть более элегантный простой sql. Но, по крайней мере, это начало.

2
ответ дан 8 December 2019 в 12:22
поделиться

это должно работать. Решение состоит из нескольких встроенных представлений, каждое из которых что-то вычисляет. Я прокомментировал то, что собирался сделать. Вы, конечно, должны читать комментарии изнутри, когда это выполняется.

--get results by grouping by interval_begin
SELECT MIN(id) from_id,
       MAX(id) to_id,
       MAX(assigned_to) assigned_to
  FROM ( --copy ids of a first row of each interval of ids to the all following rows of that interval
         SELECT id,
                 assigned_to,
                 MAX(change_at) over(ORDER BY id) interval_begin
           FROM ( --find each id where a change of an assignee occurs and "mark" it. Dont forget the first row
                 SELECT id,
                         assigned_to,
                         CASE
                           WHEN (lag(assigned_to) over(ORDER BY id) <> assigned_to OR lag(assigned_to)
                                 over(ORDER BY id) IS NULL) THEN
                            id
                         END change_at
                   FROM ticket))
 GROUP BY interval_begin
 ORDER BY from_id;
    ;
2
ответ дан 8 December 2019 в 12:22
поделиться

Я провел несколько тестов с Oracle express edition 10.2.0.1.0

Я использовал этот сценарий для заполнения билета таблицы 1179648 строками:

create table ticket (id,assigned_to)
as
select 100, 'raju' from dual union all
select 101, 'raju' from dual union all
select 102, 'raju' from dual union all
select 103, 'anil' from dual union all
select 104, 'anil' from dual union all
select 105, 'sam' from dual union all
select 106, 'raju' from dual union all
select 107, 'raju' from dual union all
select 108, 'anil' from dual
/


begin
  for i in 1..17 loop
    insert into ticket 
    select id + (select count(*) from ticket), assigned_to
    from ticket;
  end loop;
end;
/

commit;

SQL> select count(*) from ticket;

  COUNT(*)                                                                      
----------                                                                      
   1179648                                                                      

Оператор select Роба ван Вейка занимает 1,6 секунды. средний, Оператор select от Mesays в среднем 2,8 секунды, оператор select Micheal Pravda - 4,2 секунды и Эндрю из заявления NZSG в среднем 9,6 секунды.

Таким образом, конвейерная функция в Oracle XE работает медленнее. А может кому-то нужно улучшить конвейерную функцию ...?

2
ответ дан 8 December 2019 в 12:22
поделиться

Вы можете наклониться назад, пытаясь достичь этого на чистом SQL, или вы можете создать что-то более длинное, но гораздо более легкое для понимания и более эффективное с точки зрения производительности - используйте конвейерную версию function

По сути, функция будет принимать указатель ref, который должен быть предварительно упорядочен по идентификатору, и затем передавать строки по конвейеру только тогда, когда непрерывный блок записей закончился.

CREATE TABLE assignment
(
  a_id         NUMBER,
  assigned_to  VARCHAR2(4000)
);

CREATE OR REPLACE package PCK_CONTIGUOUS_GROUPBY as

 TYPE refcur_t IS REF CURSOR RETURN assignment%ROWTYPE;

 TYPE outrec_typ IS RECORD ( 
    from_id    NUMBER,
    to_id      NUMBER,
    assigned_to  VARCHAR2(4000));

  TYPE outrecset IS TABLE OF outrec_typ;

 FUNCTION f_cont_groupby(p refcur_t) 
      RETURN outrecset PIPELINED;

end;
/

CREATE OR REPLACE package body pck_contiguous_groupby as

 FUNCTION f_cont_groupby(p refcur_t) RETURN outrecset PIPELINED IS

  out_rec             outrec_typ;
  in_rec              p%ROWTYPE;
  first_id            assignment.a_id%type;
  last_id             assignment.a_id%type;
  last_assigned_to    assignment.assigned_to%type;

  BEGIN

   LOOP
     FETCH p INTO in_rec;
     EXIT WHEN p%NOTFOUND;


       IF last_id IS NULL THEN
       -- First record: don't pipe
         first_id := in_rec.a_id;

       ELSIF last_id = in_rec.a_id - 1 AND last_assigned_to = in_rec.assigned_to THEN
       -- Contiguous block: don't pipe
         NULL;

       ELSE
       -- Block not contiguous: pipe 
         out_rec.from_id := first_id;
         out_rec.to_id := last_id;
         out_rec.assigned_to := last_assigned_to;

         PIPE ROW(out_rec);

         first_id := in_rec.a_id;
       END IF;

     last_id := in_rec.a_id;
     last_assigned_to := in_rec.assigned_to;

   END LOOP;
   CLOSE p;

   -- Pipe remaining row 
   out_rec.from_id := first_id;
   out_rec.to_id := last_id;
   out_rec.assigned_to := last_assigned_to;

   PIPE ROW(out_rec);

   RETURN;
 END;

END pck_contiguous_groupby;
/

а затем, чтобы попробовать это, заполнить таблицу и запустите:

SELECT * FROM TABLE(pck_contiguous_groupby.f_cont_groupby (CURSOR (SELECT a_id, assigned_to FROM assignment ORDER BY a_id)));
2
ответ дан 8 December 2019 в 12:22
поделиться

Я полагаю, вам нужна одна из аналитических функций Oracle . Вам повезло, если вы используете Oracle, потому что другие СУБД не имеют этой функции. Они позволяют писать SQL, который позволяет запрашивать данные относительно соседних строк, например, вычислять скользящие средние. У меня нет базы данных Oracle, с которой можно было бы поиграть, но я думаю, что это будет примерно так:

SELECT MIN(ID) AS From_Id, MAX(ID) AS To_Id, Assigned_To
FROM Ticket
PARTITION BY Assigned_To
ORDER BY From_Id
0
ответ дан 8 December 2019 в 12:22
поделиться

Вот мое предложение, не очень хорошо протестированное, но в моей голове оно звучит правильно, если id уникален и является непрерывной последовательностью. Посмотрите внизу на sql-запрос.

SQL> create table ticket (id number, assigned_to varchar2(30));

Table created.

SQL> insert into ticket values (100, 'raju');

1 row created.

SQL> insert into ticket values (101, 'raju');

1 row created.

SQL> insert into ticket values (102, 'raju');

1 row created.

SQL> insert into ticket values (103, 'anil');

1 row created.

SQL> insert into ticket values (104, 'anil');

1 row created.

SQL> insert into ticket values (105, 'sam');

1 row created.

SQL> insert into ticket values (106, 'raju');

1 row created.

SQL> insert into ticket values (107, 'raju');

1 row created.

SQL> insert into ticket values (108, 'anil');

1 row created.

SQL> select a.id from_id
  2  ,lead(a.id -1, 1, a.id) over (order by a.id) to_id
  3  ,a.assigned_to
  4  from (
  5  select
  6  id, assigned_to
  7  ,lag(assigned_to, 1) over (order by id) prev_assigned_to
  8  from ticket
  9  ) a
 10  where a.assigned_to != nvl(a.prev_assigned_to, a.assigned_to||'unique')
 11  order by id
 12  ;

   FROM_ID      TO_ID ASSIGNED_TO
---------- ---------- ------------------------------
       100        102 raju
       103        104 anil
       105        105 sam
       106        107 raju
       108        108 anil
1
ответ дан 8 December 2019 в 12:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: