2009-08-12 35 views
6

Tôi cần tạo báo cáo từ một bảng có cấu trúc và dữ liệu như được đưa ra dưới đây.Oracle SQL để nhóm liên tục

Bảng vé có dữ liệu như được nêu bên dưới.

ID   Assigned_To 
100  raju 
101  raju 
102  raju 
103  anil 
104  anil 
105  sam 
106  raju 
107  raju 
108  anil 

Các Oracle SELECT nên tạo ra các báo cáo dưới đây

From_Id   To_Id Assigned_To 
100     102  raju 
103     104  anil 
105     105  sam 
106     107  raju 
108     108  anil 

Ai đó có thể vui lòng giúp tôi với việc xây dựng một truy vấn ..?

Xin cảm ơn trước, Mathew.

+0

Có thể sử dụng min (ID) và tối đa (ID) – Jonathan

+0

Ops, tôi hiểu. Hãy tha thứ cho bình luận trước của tôi ... – Jonathan

Trả lời

2

OK, điều này không đẹp, nhưng nó hoạt động. Và không ai khác đã đóng góp bất cứ điều gì đẹp hơn, vì vậy có lẽ đây là cách để làm điều đó.

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 
; 

Tôi đang sử dụng mysql; cũng có thể có một số tốt đẹp oracle mà làm cho đẹp hơn - như có thể cũng có một số sql đồng bằng thanh lịch hơn. Nhưng ít nhất đó là một sự khởi đầu.

0

Tôi tin rằng bạn muốn có một trong số Oracle Analytic functions. Bạn đang may mắn nếu bạn đang sử dụng Oracle vì các RDBMS khác không có chức năng này. Chúng cho phép bạn viết SQL cho phép bạn truy vấn dữ liệu liên quan đến các hàng liền kề, chẳng hạn như tính toán trung bình di chuyển. Tôi không có một Oracle DB vào đây để chơi với, nhưng tôi nghĩ rằng nó sẽ là một cái gì đó như thế này:

SELECT MIN(ID) AS From_Id, MAX(ID) AS To_Id, Assigned_To 
FROM Ticket 
PARTITION BY Assigned_To 
ORDER BY From_Id 
+1

Đây là cú pháp sai nhưng quan trọng hơn cả về ngữ nghĩa. Mỗi người được chuyển nhượng có thể có mặt nhiều lần trong tập kết quả mong muốn nhưng ở đây anh ta sẽ không ... Tôi đã cố gắng đưa ra một giải pháp trong một câu trả lời riêng biệt. –

2

Bạn có thể uốn cong về phía sau cố gắng để đạt được điều này trong SQL tinh khiết hoặc bạn có thể tạo ra một cái gì đó dài hơn một chút nhưng cả hai dễ hiểu hơn và hiệu quả hơn nhiều - hãy sử dụng một số pipelined function

Thực chất chức năng sẽ chấp nhận con trỏ sẽ được ID đặt hàng trước và sau đó chỉ có các hàng ống khi một khối hồ sơ liền kề đã kết thúc.

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; 
/

và sau đó để thử nó ra, cư bàn và chạy:

SELECT * FROM TABLE(pck_contiguous_groupby.f_cont_groupby (CURSOR (SELECT a_id, assigned_to FROM assignment ORDER BY a_id))); 
+1

Plsql hàng theo giải pháp có thể đôi khi dễ đọc hơn, nhưng hầu như luôn kém hiệu quả hơn SQL thuần túy. –

+0

Tôi cũng rất tò mò tại sao bạn nói "và hiệu suất hiệu quả hơn - sử dụng chức năng pipelined". Quan tâm để giải thích? –

+0

Nó hiệu quả hơn nhiều so với bất kỳ SQL nào khác và tôi sẽ giải thích tại sao. Bước đầu tiên và quan trọng nhất của bất kỳ thiết kế hiệu suất nào không phải là đặt SQL trước PL/SQL nhưng để giảm thiểu I/O logic. Truy vấn trên thực hiện một QUÉT SINGLE của bảng + một đơn đặt hàng theo và đó là nó. Bất kỳ truy vấn nào khác ít nhất phải thực hiện 2 lần quét. Ví dụ. có lẽ là một số cách thông minh để sử dụng một chức năng phân tích để tính toán 'khối tiếp giáp' và sau đó GROUP BY cột tính toán mới này, nhưng điều này sẽ ngụ ý 2 lần quét + một GROUP BY bổ sung. –

1

Dưới đây là gợi ý của tôi, không phải là rất tốt thử nghiệm nhưng trong đầu tôi nó có vẻ đúng, miễn là id là duy nhất và một chuỗi liên tục. Nhìn vào đáy cho truy vấn 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 
2

điều này sẽ hiệu quả. Giải pháp bao gồm một số chế độ xem nội tuyến - mỗi người trong số họ tính toán một cái gì đó. Tôi đã nhận xét những gì tôi dự định làm. Tất nhiên bạn phải đọc những bình luận từ trong cùng nhất khi nó được thi hành.

--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; 
    ; 
+0

Điều này thực sự hữu ích hơn cho tôi, vì tôi có một vấn đề tương tự nhưng hơi phức tạp hơn mà không có lợi ích của một id duy nhất. Nhưng tôi không hiểu, tại sao "ORDER BY id" trong tác vụ truy vấn "sao chép id của hàng đầu tiên" ?? Dường như nó lấy MAX (change_at) cho tập hợp tất cả mọi thứ đang chạy cho đến nay (trong trường hợp của tôi trong mỗi phân vùng), nhưng tôi không nghĩ rằng các hàm phân tích đã hoạt động theo cách đó. – orbfish

7
SQL> create table ticket (id,assigned_to) 
    2 as 
    3 select 100, 'raju' from dual union all 
    4 select 101, 'raju' from dual union all 
    5 select 102, 'raju' from dual union all 
    6 select 103, 'anil' from dual union all 
    7 select 104, 'anil' from dual union all 
    8 select 105, 'sam' from dual union all 
    9 select 106, 'raju' from dual union all 
10 select 107, 'raju' from dual union all 
11 select 108, 'anil' from dual 
12/

Tabel is aangemaakt. 

SQL> select min(id) from_id 
    2  , max(id) to_id 
    3  , assigned_to 
    4 from (select id 
    5    , assigned_to 
    6    , id - row_number() over (partition by assigned_to order by id) grp 
    7    from ticket 
    8  ) 
    9 group by assigned_to 
10  , grp 
11 order by from_id 
12/

    FROM_ID  TO_ID ASSIGNED_TO 
---------- ---------- ----------- 
     100  102 raju 
     103  104 anil 
     105  105 sam 
     106  107 raju 
     108  108 anil 

5 rijen zijn geselecteerd. 

** CẬP NHẬT với kết quả của một sự so sánh hiệu suất với giải pháp tuinstoel của:

On 11.1.0.7:

SQL> exec runstats_pkg.rs_start 

PL/SQL procedure successfully completed. 

SQL> set termout off 
SQL> select min(id) from_id 
    2  , max(id) to_id 
    3  , assigned_to 
    4 from (select id 
    5    , assigned_to 
    6    , id - row_number() over (partition by assigned_to order by id) grp 
    7    from ticket 
    8  ) 
    9 group by assigned_to 
10  , grp 
11 order by from_id 
12/

    FROM_ID  TO_ID ASSI 
---------- ---------- ---- 
     100  102 raju 
     103  104 anil 
     105  105 sam 
     106  107 raju 
     108  108 anil 
     109  111 raju 
<snip> 
    589921  589922 raju 
    589923  589923 anil 

327680 rows selected. 

SQL> set termout on 
SQL> exec runstats_pkg.rs_middle 

PL/SQL procedure successfully completed. 

SQL> set termout off 
SQL> select * from table(testpl.pltest) 
    2/

    FROM_ID  TO_ID ASSI 
---------- ---------- ---- 
     100  102 raju 
     103  104 anil 
     105  105 sam 
     106  107 raju 
     108  108 anil 
     109  111 raju 
<snip> 
    589921  589922 raju 
    589923  589923 anil 

327680 rows selected. 

SQL> set termout on 

Và kết quả:

SQL> exec runstats_pkg.rs_stop(100) 
Run1 draaide in 547 hsecs 
Run2 draaide in 549 hsecs 
Run1 draaide in 99.64% van de tijd 

Naam              Run1  Run2 Verschil 
STAT.recursive cpu usage          2   106   104 
LATCH.row cache objects          91   217   126 
STAT.bytes received via SQL*Net from client    37,496  37,256  -240 
STAT.recursive calls           7  5,914  5,907 
STAT.table scan rows gotten       615,235  589,824  -25,411 
STAT.sorts (rows)          917,504  589,824 -327,680 

Run1 latches totaal versus run2 -- verschil en percentage 
Run1  Run2 Verschil  Pct 
10,255 10,471  216 97.94% 

PL/SQL procedure successfully completed. 

Trân trọng, Rob.

+0

Khá tốt, thật đơn giản để hiểu, giả sử người ta biết cách hoạt động của chức năng phân tích. –

+0

Đẹp và đơn giản. Tôi đã không nhận ra rằng nó là trong trường hợp này có thể phân vùng bằng cách gán cho cũng như grp. Vì vậy, tôi đã sử dụng một cách phức tạp hơn để tính toán "grp": (+1 Messays 'thậm chí còn tốt hơn về hiệu suất (một loại ít hơn), nhưng anh ta giả định rằng id là dày đặc (hai id liên tiếp khác nhau 1) và chúng tôi –

2

tôi đã làm một số điểm chuẩn với Oracle Express Edition 10.2.0.1.0

tôi đã sử dụng kịch bản này để điền vào vé bảng với 1.179.648 hàng:

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                  
lựa chọn công bố

Rob van Wijk của mất 1,6 giây trên trung bình , tuyên bố chọn của Mesays 2,8 giây trung bình, tuyên bố chọn của Micheal Pravda 4,2 giây và Andrew từ tuyên bố của NZSG 9,6 giây tính trung bình.

Vì vậy, chức năng pipelined chậm hơn trong Oracle XE. Hoặc có lẽ ai đó phải cải thiện chức năng pipelined ...?

+0

+1 Cảm ơn bạn đã kiểm tra! –

3

Giả sử Andrew từ NZSG đã truyền cảm hứng cho tôi. Tôi cũng đã tạo ra một hàm lót ống.

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; 
/

Bạn có thể thử nghiệm nó với:

select * from table(testpl.pltest); 

Đó là khoảng hai lần nhanh như giải pháp Rob van Wijk về 11.1.0.6.0 hệ thống Windows XP Oracle của tôi.

Các

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

xây dựng có một buổi biểu diễn rất phong nha trong Oracle 10 và 11. Phần lớn thời gian SQL giải pháp duy nhất là nhanh hơn nhưng tôi nghĩ rằng đây PL/SQL là nhanh hơn.

+0

+1 giải pháp PL/SQL sạch đẹp.Tuy nhiên, tôi thấy thời gian phản hồi tương tự trong bài kiểm tra của tôi. Bạn có điều chỉnh SQL * Plus của bạn thành mảng 100 không? với kết quả của bài kiểm tra đó. –

Các vấn đề liên quan