Il est trop difficile, voire impossible de faire une telle chose en SQL.
Mais avec quelques limitations, le problème peut être résolu avec l'aide de user-defined aggregate functions.
Tout d'abord, créer un objet avec la mise en œuvre d'interface 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
);
Créer corps de type:
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;
fonction Créer agrreation utiliser avec le type:
create function page_num_agg (
input number
) return number aggregate using page_num_agg_type;
Suivant préparer les données et utiliser une nouvelle fonction pour calculer les numéros de page:
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
Et, enfin ...
Inconvénients de cette solution:
- la page Hardcoded nombre de lignes.
- Nécessite une préparation de données spécifique dans la requête pour utiliser correctement la fonction d'agrégat.
Avantages de cette solution:
- fonctionne exactement :)
Mise à jour: amélioré pour gérer les blocs surdimensionnés, par exemple modifié.
Alors, pouvez-vous garantir qu'aucun EMPNO aura> 65000 dossiers? – APC
En outre, quelle version d'Excel utilisez-vous? Excel 2007 autorise un nombre fou de 1 048 576 lignes par feuille de calcul. – APC
Eh bien, j'espérais que cela prendrait même en charge les versions d'Excel antérieures à 2007. (http://office.microsoft.com/fr-fr/excel-help/excel-specifications-and-limits-HP005199291.aspx) – keiko