2010-08-09 31 views
6

Tôi có một bảng mà kết quả đầu ra tương tự như sau (mặc dù trong hàng ngàn):(Oracle) làm thế nào để hàng nhóm cho pagination

 EMPNO ENAME  TRANDATE  AMT 
---------- ---------- --------- ------- 
     100 Alison  21-MAR-96 45000 
     100 Alison  12-DEC-78 23000 
     100 Alison  24-OCT-82 11000 
     101 Linda  15-JAN-84 16000 
     101 Linda  30-JUL-87 17000 
     102 Celia  31-DEC-90 78000 
     102 Celia  17-SEP-96 21000 
     103 James  21-MAR-96 45000 
     103 James  12-DEC-78 23000 
     103 James  24-OCT-82 11000 
     104 Robert  15-JAN-84 16000 
     104 Robert  30-JUL-87 17000 

đầu ra của tôi mong muốn sẽ là tương tự như sau:

 EMPNO ENAME  TRANDATE  AMT PAGE 
---------- ---------- --------- ------- ---- 
     100 Alison  21-MAR-96 45000 1 
     100 Alison  12-DEC-78 23000 1 
     100 Alison  24-OCT-82 11000 1 
     101 Linda  15-JAN-84 16000 2 
     101 Linda  30-JUL-87 17000 2 
     102 Celia  31-DEC-90 78000 2 
     102 Celia  17-SEP-96 21000 2 
     103 James  21-MAR-96 45000 3 
     104 Robert  12-DEC-78 23000 4 
     104 Robert  24-OCT-82 11000 4 
     104 Robert  15-JAN-84 16000 4 
     104 Robert  30-JUL-87 17000 4 

Về cơ bản, nó sẽ chèn một trường mới để xác định trang thuộc về nó. Ngắt trang dựa trên các hàng. Và, như thể "được giữ lại" trong EMPNO, nó thêm 1 đến PAGE khi các hàng không thể thêm hàng loạt EMPNO tiếp theo. Nó dành cho giới hạn Excel vì Excel không cho phép hơn 65000 hàng (hoặc hơn) trong một trang tính. Trong trường hợp của mẫu, nó chỉ có 4 hàng. Số giới hạn là tĩnh.

+0

Vì vậy, bạn có thể đảm bảo rằng không có EMPNO sẽ có> 65000 hồ sơ? – APC

+0

Ngoài ra, bạn đang sử dụng phiên bản Excel nào? Excel 2007 cho phép 1.048.576 hàng trên mỗi trang tính. – APC

+0

Vâng, tôi đã hy vọng nó sẽ hỗ trợ ngay cả các phiên bản Excel trước năm 2007. (http://office.microsoft.com/en-us/excel-help/excel-specifications-and-limits-HP005199291.aspx) – keiko

Trả lời

2

ThinkJet đúng rằng một số câu trả lời khác không đáp ứng yêu cầu 'giữ lại'. Tuy nhiên tôi nghĩ rằng điều này có thể được thực hiện mà không cần đến một tổng hợp do người dùng xác định.

dữ liệu mẫu

create table test (empno number, ename varchar2(20), trandate date, amt number); 
insert into test values (100, 'Alison' , to_date('21-MAR-1996') , 45000); 
insert into test values (100, 'Alison' , to_date('12-DEC-1978') , 23000); 
insert into test values (100, 'Alison' , to_date('24-OCT-1982') , 11000); 
insert into test values (101, 'Linda' , to_date('15-JAN-1984') , 16000); 
insert into test values (101, 'Linda' , to_date('30-JUL-1987') , 17000); 
insert into test values (102, 'Celia' , to_date('31-DEC-1990') , 78000); 
insert into test values (102, 'Celia' , to_date('17-SEP-1996') , 21000); 
insert into test values (103, 'James' , to_date('21-MAR-1996') , 45000); 
insert into test values (103, 'James' , to_date('12-DEC-1978') , 23000); 
insert into test values (103, 'James' , to_date('24-OCT-1982') , 11000); 
insert into test values (104, 'Robert' , to_date('15-JAN-1984') , 16000); 
insert into test values (104, 'Robert' , to_date('30-JUL-1987') , 17000); 

Bây giờ, xác định dòng cuối của mỗi đoạn empno (sử dụng RANK để tìm điểm bắt đầu và COUNT..PARTITION BY để tìm số trong phân khúc).

Sau đó sử dụng ceil/4 từ giải pháp của APC để nhóm chúng vào 'trang' của chúng. Một lần nữa, như được chỉ ra bởi ThinkJet, có một vấn đề trong đặc điểm kỹ thuật vì nó không phục vụ cho tình huống khi có nhiều hồ sơ trong phân khúc 'giữ cùng nhau' của empno hơn là có thể vừa với một trang.

select empno, ename, 
     ceil((rank() over (order by empno) + 
     count(1) over (partition by empno))/6) as chunk 
from test 
order by 1; 

Như đã chỉ ra bởi ThinkJet, giải pháp này không phải là chống đạn.

drop table test purge; 

create table test (empno number, ename varchar2(20), trandate date, amt number); 
declare 
    cursor csr_name is 
    select rownum emp_id, 
      decode(rownum,1,'Alan',2,'Brian',3,'Clare',4,'David',5,'Edgar', 
      6,'Fred',7,'Greg',8,'Harry',9,'Imran',10,'John', 
      11,'Kevin',12,'Lewis',13,'Morris',14,'Nigel',15,'Oliver', 
      16,'Peter',17,'Quentin',18,'Richard',19,'Simon',20,'Terry', 
      21,'Uther',22,'Victor',23,'Wally',24,'Xander', 
      25,'Yasmin',26,'Zac') emp_name 
    from dual connect by level <= 26; 
begin 
    for c_name in csr_name loop 
    for i in 1..11 loop 
     insert into test values 
      (c_name.emp_id, c_name.emp_name, (date '2010-01-01') + i, 
      to_char(sysdate,'SS') * 1000); 
    end loop; 
    end loop; 
end; 
/

select chunk, count(*) 
from 
    (select empno, ename, 
     ceil((rank() over (order by empno) + 
     count(1) over (partition by empno))/25) as chunk 
    from test) 
group by chunk 
order by chunk 
; 

Vì vậy, với kích thước chunk 25 và kích thước nhóm 11, chúng tôi nhận được bước nhảy phù hợp với 33 người trong đoạn mặc dù giới hạn 25. Kích thước chunk lớn và các nhóm nhỏ nên làm cho điều này không thường xuyên, nhưng bạn muốn cho phép một số mất nhiều thời gian. Vì vậy, có thể thiết lập các khối đến 65.000 thay vì đi tất cả các cách để 65.536.

+0

Cảm ơn rất nhiều! Điều này làm việc cho trường hợp của tôi! Và tôi thực sự nghĩ rằng truy vấn sẽ dài hơn. Tôi hiểu về vấn đề của các bản ghi 65k + với cùng EMPNO, nhưng trong trường hợp của tôi, nó rất có thể sẽ không xảy ra. – keiko

+1

Có lỗi giống như trong các câu trả lời khác: khoảng trống ngẫu nhiên ở cuối trang không được xử lý trong tính toán. Ví dụ. giả sử bạn có 3 nhóm hàng (A, B, C) với 6 hàng trong mỗi nhóm. Cố gắng đặt nó trên các trang, 6 hàng trên mỗi trang: Nhóm A - xếp hạng() = 1 - Ok, trang đầu tiên; Nhóm B - xếp hạng() = 7 - Ok, trang thứ 2 được điền từ trên cùng và 4 hàng để trống trên trang đầu tiên; Nhóm C - rank() = 13 - ERR !, dường như vừa với trang thứ 2 nhưng phải được đặt vào thứ 3, bởi vì giá trị REAL rank() phải bao gồm các hàng trống từ trang 1: 6 + 4 + 6 + 1 = 17. Không có cách nào để dự đoán số hàng trống trước khi xây dựng trang thực. – ThinkJet

+0

Điều này có thể xảy ra ngay cả đối với 65 nghìn hàng +, ngay cả ở cuối trang thứ 2: Ví dụ: 5 hàng miễn phí trên trang đầu tiên + 4 hàng miễn phí trên trang thứ 2 + khối tiếp theo có kích thước từ 5 đến 9 hàng ... – ThinkJet

1

Các câu lệnh SQL sau chia tách hai mươi bản ghi trong bảng EMP của tôi vào năm trang của bốn hàng mỗi:

SQL> select empno 
    2   , ename 
    3   , deptno 
    4   , ceil((row_number() over (order by deptno, empno)/4)) as pageno 
    5 from emp 
    6/

    EMPNO ENAME   DEPTNO  PAGENO 
---------- ---------- ---------- ---------- 
     7782 BOEHMER   10   1 
     7839 SCHNEIDER   10   1 
     7934 KISHORE   10   1 
     7369 CLARKE    20   1 
     7566 ROBERTSON   20   2 
     7788 RIGBY    20   2 
     7876 KULASH    20   2 
     7902 GASPAROTTO   20   2 
     7499 VAN WIJK   30   3 
     7521 PADFIELD   30   3 
     7654 BILLINGTON   30   3 
     7698 SPENCER   30   3 
     7844 CAVE    30   4 
     7900 HALL    30   4 
     8083 KESTELYN   30   4 
     8084 LIRA    30   4 
     8060 VERREYNNE   50   5 
     8061 FEUERSTEIN   50   5 
     8085 TRICHLER   50   5 
     8100 PODER    50   5 

20 rows selected. 

SQL> 
+1

sẽ không hoạt động. Điều kiện quan trọng - empno đó không thể có trong hai nhóm. Trong trường hợp của bạn, empno 101 sẽ nằm trong hai nhóm riêng biệt. –

+0

Cảm ơn! Mã này làm việc cho tôi! (Cập nhật: Ok ... có thể không. Tốt tìm, Michael.) – keiko

+0

Chỉ cần không làm việc - kết quả sai. Ví dụ. nơi được tính các dòng trống bổ sung ở cuối trang cho trang thứ 3 trở lên? – ThinkJet

2

Đó là quá khó khăn hoặc thậm chí không thể làm điều đó trong SQL đơn giản.

Nhưng với một số vấn đề về giới hạn có thể được giải quyết với sự trợ giúp của user-defined aggregate functions.

Đầu tiên, tạo đối tượng với giao diện thực hiện ODCIAggregate:

create or replace type page_num_agg_type as object 
(
    -- Purpose : Pagination with "leave together" option 

    -- Attributes    

    -- Current page number 
    cur_page_number number,         

    -- Cumulative number of rows per page incremented by blocks 
    cur_page_row_count number, 

    -- Row-by-row counter for detect page overflow while placing single block 
    page_row_counter number, 

    -- Member functions and procedures 

    static function ODCIAggregateInitialize(
    sctx in out page_num_agg_type 
) 
    return number, 

    member function ODCIAggregateIterate(
    self  in out page_num_agg_type, 
    value  in  number 
) 
    return number, 

    member function ODCIAggregateTerminate(
    self  in page_num_agg_type, 
    returnValue out number, 
    flags  in number 
) 
    return number, 

    member function ODCIAggregateMerge(
    self in out page_num_agg_type, 
    ctx2 in  page_num_agg_type 
) 
    return number 

); 

Tạo kiểu cơ thể:

create or replace type body PAGE_NUM_AGG_TYPE is 

    -- Member procedures and functions 
    static function ODCIAggregateInitialize(
    sctx in out page_num_agg_type 
) 
    return number 
    is 
    begin 
     sctx := page_num_agg_type(1, 0, 0); 
     return ODCIConst.Success; 
    end; 

    member function ODCIAggregateIterate(
    self  in out page_num_agg_type, 
    value  in  number 
) 
    return number 
is 
    -- !!! WARNING: HARDCODED !!! 
    RowsPerPage number := 4; 
begin 

    self.page_row_counter := self.page_row_counter + 1; 

    -- Main operations: determine number of page 

    if(value > 0) then 
    -- First row of new block 

    if(self.cur_page_row_count + value > RowsPerPage) then 
     -- If we reach next page with new block of records - switch to next page. 
     self.cur_page_number := self.cur_page_number + 1; 
     self.cur_page_row_count := value; 
     self.page_row_counter := 1; 
    else 
     -- Just increment rows and continue to place on current page 
     self.cur_page_row_count := self.cur_page_row_count + value; 
    end if; 

    else      
    -- Row from previous block 

    if(self.page_row_counter > RowsPerPage) then 
     -- Single block of rows exceeds page size - wrap to next page. 
     self.cur_page_number := self.cur_page_number + 1; 
     self.cur_page_row_count := self.cur_page_row_count - RowsPerPage; 
     self.page_row_counter := 1; 
    end if; 

    end if; 

    return ODCIConst.Success; 
end; 

member function ODCIAggregateTerminate(
    self  in page_num_agg_type, 
    returnValue out number, 
    flags  in number 
) 
    return number 
is 
begin 
    -- Returns current page number as result 
    returnValue := self.cur_page_number; 
    return ODCIConst.Success; 
end; 

member function ODCIAggregateMerge(
    self in out page_num_agg_type, 
    ctx2 in  page_num_agg_type 

) 
    return number 
is 
begin 
    -- Can't act in parallel - error on merging attempts 
    raise_application_error(-20202,'PAGE_NUM_AGG_TYPE can''t act in parallel mode'); 
    return ODCIConst.Success; 
end; 

end; 

Tạo chức năng agrreation để sử dụng với loại:

create function page_num_agg (
    input number 
) return number aggregate using page_num_agg_type; 

Tiếp theo chuẩn bị dữ liệu và sử dụng hàm mới để tính số trang:

with data_list as (
    -- Your example data as source 
    select 100 as EmpNo, 'Alison' as EmpName, to_date('21-MAR-96','dd-mon-yy') as TranDate, 45000 as AMT from dual union all 
    select 100 as EmpNo, 'Alison' as EmpName, to_date('12-DEC-78','dd-mon-yy') as TranDate, 23000 as AMT from dual union all 
    select 100 as EmpNo, 'Alison' as EmpName, to_date('24-OCT-82','dd-mon-yy') as TranDate, 11000 as AMT from dual union all 
    select 101 as EmpNo, 'Linda' as EmpName, to_date('15-JAN-84','dd-mon-yy') as TranDate, 16000 as AMT from dual union all 
    select 101 as EmpNo, 'Linda' as EmpName, to_date('30-JUL-87','dd-mon-yy') as TranDate, 17000 as AMT from dual union all 
    select 102 as EmpNo, 'Celia' as EmpName, to_date('31-DEC-90','dd-mon-yy') as TranDate, 78000 as AMT from dual union all 
    select 102 as EmpNo, 'Celia' as EmpName, to_date('17-SEP-96','dd-mon-yy') as TranDate, 21000 as AMT from dual union all 
    select 103 as EmpNo, 'James' as EmpName, to_date('21-MAR-96','dd-mon-yy') as TranDate, 45000 as AMT from dual union all 
    select 103 as EmpNo, 'James' as EmpName, to_date('12-DEC-78','dd-mon-yy') as TranDate, 23000 as AMT from dual union all 
    select 103 as EmpNo, 'James' as EmpName, to_date('24-OCT-82','dd-mon-yy') as TranDate, 11000 as AMT from dual union all 
    select 104 as EmpNo, 'Robert' as EmpName, to_date('15-JAN-84','dd-mon-yy') as TranDate, 16000 as AMT from dual union all 
    select 104 as EmpNo, 'Robert' as EmpName, to_date('30-JUL-87','dd-mon-yy') as TranDate, 17000 as AMT from dual union all 
    select 105 as EmpNo, 'Monica' as EmpName, to_date('30-JUL-88','dd-mon-yy') as TranDate, 31000 as AMT from dual union all 
    select 105 as EmpNo, 'Monica' as EmpName, to_date('01-JUL-87','dd-mon-yy') as TranDate, 19000 as AMT from dual union all 
    select 105 as EmpNo, 'Monica' as EmpName, to_date('31-JAN-97','dd-mon-yy') as TranDate, 11000 as AMT from dual union all 
    select 105 as EmpNo, 'Monica' as EmpName, to_date('17-DEC-93','dd-mon-yy') as TranDate, 33000 as AMT from dual union all 
    select 105 as EmpNo, 'Monica' as EmpName, to_date('11-DEC-91','dd-mon-yy') as TranDate, 65000 as AMT from dual union all 
    select 105 as EmpNo, 'Monica' as EmpName, to_date('22-OCT-89','dd-mon-yy') as TranDate, 19000 as AMT from dual 
), 
ordered_data as (
    select    
    -- Source table fields 
    src_data.EmpNo, src_data.EmpName, src_data.TranDate, src_data.AMT, 
    -- Calculate row count per one employee 
    count(src_data.EmpNo) over(partition by src_data.EmpNo)as emp_row_count, 
    -- Calculate rank of row inside employee data sorted in output order 
    rank() over(partition by src_data.EmpNo order by src_data.EmpName, src_data.TranDate) as emp_rnk 
    from 
    data_list src_data 
) 
-- Final step: calculate page number for rows 
select 
    -- Source table data 
    ordered_data.EmpNo, ordered_data.EmpName, ordered_data.TranDate, ordered_data.AMT, 
    -- Aggregate all data with our new function 
    page_num_agg(
     -- pass count of rows to aggregate function only for first employee's row 
     decode(ordered_data.emp_rnk, 1, ordered_data.emp_row_count, 0) 
    ) 
     over (order by ordered_data.EmpName, ordered_data.TranDate) as page_number 
from  
    ordered_data  
order by 
    ordered_data.EmpName, ordered_data.TranDate 

Và, cuối cùng ...

Nhược của giải pháp này:

  1. hardcoded số trang liên tiếp.
  2. Yêu cầu chuẩn bị dữ liệu cụ thể trong truy vấn để sử dụng chức năng tổng hợp đúng cách.

Ưu của giải pháp này:

  1. Chỉ cần làm việc :)

Cập nhật: cải thiện để xử lý khối ngoại cỡ, ví dụ sửa đổi.

+0

Tôi chưa thực sự thực hiện bất kỳ hàm tổng hợp do người dùng xác định nào trước đây. Tôi được phép làm thủ tục. – keiko

0

Làm thế nào về vấn đề này một: (100 là giới hạn hàng mỗi trang)

select 
    a.*, floor(count(1) over (order by empno, empname)/100)+1 as page 
from source_table a 
order by page, empno, empname; 
Các vấn đề liên quan