Home>Question Details



sai -- Thanks for the question regarding "Query to obtain pagination links", version 10g

Submitted on 6-Feb-2008 11:26 Central time zone
Last updated 31-Mar-2008 16:48

You Asked

Hi Tom,
I am trying to formulate a SQL query to obtain a pagination links to display "By Name" in alphabetically. The page limit being 200 records.

E.g

A - L | M - S | T - Z

Authors starting char "A" to "L" are less than( ~ 200 like 190 + ) or equal to 200. More complex situation In case a specific character span more than that limit then the links should be displayed as A - La | Le - Su | T - Z

So A - La has records less than( ~ 200 like 190 + ) or equal to 200. But there may not be any records for Lb so display the next link as Le where the record exists.

I am able to get the counts on the columns -

SELECT  UPPER(SUBSTR(LN,1,1)) STARTS_WITH_CHAR,
       COUNT(UPPER(SUBSTR(LN,1,1))) COUNT
FROM 
(SELECT DISTINCT CI.LAST_NAME LN
FROM 
     PRODUCT_CON_ROLE PC,
     CON_INFO CI,
     PRODUCT_C_ENTITY_ID_MAPPING PCE
WHERE 
             CI.LAST_NAME IS NOT NULL 
AND   CI.CON_ID = PC.CON_ID
AND   PC.ROLE_ID IN ('AU', 'MA')
AND   PC.PRODU_ID = PCE.PRODU_ID
AND   PCE.C_ID = 'XXX'
AND   PCE.M_TYPE = 'YY'
ORDER BY CI.LAST_NAME)
GROUP BY UPPER(SUBSTR(LN,1,1))
ORDER BY UPPER(SUBSTR(LN,1,1))


Result : 26 rows

A 101
B 275
C 213
D 161
E 54
F 115
.
.
.


Also when inputed as SUBSTR(LN,1,2) in the above query, I get the first 2 characters of name -

Result : 224 rows

AA 1
AB 6
AC 5
AD 5
.
.
BA 63
BE 56
.
.
.


I am trying to formulate query to get the following result set but not knowing how to do it.


Page No Start_Char End_Char Offset
1 A Ba 0
2 Be Br 163
3 Bu Cr 260
.
.
.

In above "Page No" is pseduo colum numbering and column "offset" is to provide the index of start and end is obtained from the next record. Obtaining the result set as above with readily help in displaying the links as

A - Ba | Be - Br | Bu - Cr ........

and also the corresponding displayed below on the page using the offset.


I am trying to use SUM of the count and condition but i dont know how to break when it reaches the max also the complexity with 2nd character level.

Can you let me know what is the possible query that can help to obtain the above result set.

Thanks,
Sai.

and we said...

ops$tkyte%ORA10GR2> select substr( min(object_name), 1, 2) || ' - ' || substr( 
max(object_name), 1, 2 ), count(*), trunc( rn / 200 )
  2    from (
  3  select object_name, row_number() over (order by object_name) rn
  4    from all_objects
  5   where rownum <= 1234
  6         )
  7   group by trunc( rn / 200 )
  8   order by 1
  9  /

SUBSTR(   COUNT(*) TRUNC(RN/200)
------- ---------- -------------
AC - I_        199             0
I_ - I_        200             1
I_ - PL        200             2
PL - US        200             3
US - V$        200             4
V$ - V_        200             5
V_ - WA         35             6

7 rows selected.

Reviews    
5 stars Very Slick   February 7, 2008 - 1pm Central time zone
Reviewer: Michael O from TX
This is the slickest query I've seen in an age.

2 stars No Proper results   February 7, 2008 - 3pm Central time zone
Reviewer: sai from NY, USA
Thanks for your response. But when i translated my query as below to the you sent my results are not correct, they are 3 times.

======================================
SELECT  trunc( rn / 200 ) Page_No,
        UPPER(substr( min(LN), 1, 2)) 
        || ' - ' || 
        UPPER(substr(max(LN), 1, 2 )) Page_Links,
        COUNT(*) COUNT
FROM 
(
SELECT DISTINCT UPPER(CI.LAST_NAME) LN,row_number() over (order by CI.LAST_NAME) rn
FROM 
     PRODUCT_CON_ROLE PC,
     CON_INFO CI,
     PRODUCT_C_ENTITY_ID_MAPPING PCE
WHERE 
             CI.LAST_NAME IS NOT NULL 
AND   CI.CON_ID = PC.CON_ID
AND   PC.ROLE_ID IN ('AU', 'MA')
AND   PC.PRODU_ID = PCE.PRODU_ID
AND   PCE.C_ID = 'XXX'
AND   PCE.M_TYPE = 'YY'
AND   rownum <= 12345 
ORDER BY UPPER(CI.LAST_NAME)
)
GROUP BY trunc( rn / 200 )
ORDER BY 1


====================================

I observe that when i use "row_number() over (order by CI.LAST_NAME)" in subquery the results are 3 times. Can you clarify why it could be and

My changes - I have rownum<=12345 because my results are about 2900. If i put rownum<=1234 it fetches about 1200 records only. Is this correct?

Also We dont always want 2 characters in the page links in case the alphabet has only less records. For e,g A can have only 101 records since page accepts 200 records, the first link should be "A - Be" and so on. Can you let me know what logic should be incorporated to obtain the same.

One more question - "Order by 1" at the end of query is ordering by first column ??


Thanks for your inputs,
Sai.





Followup   February 7, 2008 - 3pm Central time zone:

SELECT DISTINCT UPPER(CI.LAST_NAME) LN,row_number() over (order by 
CI.LAST_NAME) rn
FROM 
     PRODUCT_CON_ROLE PC,
     CON_INFO CI,
     PRODUCT_C_ENTITY_ID_MAPPING PCE
WHERE 


think about it.... row_number() is *distinct*

so the distinct - does NOTHING...


take your query:

(SELECT DISTINCT CI.LAST_NAME LN
FROM 
     PRODUCT_CON_ROLE PC,
     CON_INFO CI,
     PRODUCT_C_ENTITY_ID_MAPPING PCE
WHERE 
             CI.LAST_NAME IS NOT NULL 
AND   CI.CON_ID = PC.CON_ID
AND   PC.ROLE_ID IN ('AU', 'MA')
AND   PC.PRODU_ID = PCE.PRODU_ID
AND   PCE.C_ID = 'XXX'
AND   PCE.M_TYPE = 'YY'
ORDER BY CI.LAST_NAME)


and lets call is "q"

ops$tkyte%ORA10GR2> select substr( min(object_name), 1, 2) || ' - ' || substr( 
max(object_name), 1, 2 ), count(*), trunc( rn / 200 )
  2    from (
  3  select ln, row_number() over (order by object_name) rn
  4    from (Q)
  6         )
  7   group by trunc( rn / 200 )
  8   order by 1
  9  /



and yes, order by 1 orders by the first selected column


as for the shorter substr logic - come on - you see all of the information you have here right - understand what it does and you'll be able to come up with something...
5 stars Reverse Order   February 8, 2008 - 1pm Central time zone
Reviewer: Jack from Raleigh, NC USA
Tom,
This little query is very powerful and will aid greatly in the application I am developing.  
I do have one comment/question though.  When I reverse the results by adding 'desc' in the order by 
clause, I notice my first range contains one less than my expected range of 10.

select min(batch_job_id) || ' - ' || max(batch_job_id) range
      ,count(*)
      ,trunc( rn / 10 )
  from (select batch_job_id, row_number() over (order by batch_job_id desc) rn
          from refresh_history
         where rownum <= 10000 )
 group by trunc( rn / 10 )
 order by 1 desc;

RANGE             COUNT(*) TRUNC(RN/10)
--------------- ---------- ------------
1280 - 1360              9            0
1180 - 1270             10            1
1170 - 1170              1            2



Followup   February 8, 2008 - 3pm Central time zone:

well, frankly, if you reverse engineer the query (that should be the FIRST thing you do, to understand it), you could fix this.

My first range ALWAYS has one less:
ops$tkyte%ORA10GR2> select substr( min(object_name), 1, 2) || ' - ' || substr( 
max(object_name), 1, 2 ), count(*), trunc( rn / 200 )
  2    from (
  3  select object_name, row_number() over (order by object_name) rn
  4    from all_objects
  5   where rownum <= 1234
  6         )
  7   group by trunc( rn / 200 )
  8   order by 1
  9  /

SUBSTR(   COUNT(*) TRUNC(RN/200)
------- ---------- -------------
AC - I_        199             0
I_ - I_        200             1
I_ - PL        200             2
PL - US        200             3


because row_number() starts at 1, not zero...

so, make it start at zero :)

ops$tkyte%ORA10GR2> select substr( min(object_name), 1, 2) || ' - ' || substr( max(object_name), 1, 
2 ), count(*), trunc( rn / 200 )
  2    from (
  3  select object_name, row_number() over (order by object_name)-1 rn
  4    from all_objects
  5   where rownum <= 1234
  6         )
  7   group by trunc( rn / 200 )
  8   order by 1
  9  /

SUBSTR(   COUNT(*) TRUNC(RN/200)
------- ---------- -------------
AC - I_        200             0
I_ - I_        200             1
I_ - PL        200             2
PL - US        200             3
US - V$        200             4
V$ - V_        200             5
V_ - WA         34             6

7 rows selected.


5 stars Reverse Order   February 8, 2008 - 4pm Central time zone
Reviewer: Jack from Raleigh, NC USA
Outstanding!!!  I totally missed that one.

Many Thanks for your help.


4 stars   March 19, 2008 - 4pm Central time zone
Reviewer: sai from NJ
Hi Tom,
I am further working on obtaining alphabetical pagination links with regards to my changes in the 
implementation to the earlier one.

Here is the query i am working on - 
===========================================================
select trunc( rn / 200 ),substr( min(LN), 1, 2) || ' - ' || substr(max(LN), 1, 2 ) PAGE_LINKS
      ,count(*)
  from (select C_ID, row_number() over (order by C_ID) rn,LAST_NAME LN
          from TAB_NAMES
         where rownum <= 10000 
     order by C_ID)
 group by trunc( rn / 200)
 order by 1;
===========================================================

Where the inner query as below -  

=======================
  select C_ID, row_number() over (order by C_ID) rn,LAST_NAME LN
    from TAB_NAMES
  where rownum <= 10000 
  order by C_ID
=======================

Which results as below which is expected and fine.

C_ID Rn    Ln
==============
0001  0     AAXXX
0001  1     AEXXX
0001  2     AYXXX 
.
.
.
0001   6010  ZUXXX
0002   6011  AAXXX
0002   6012  AEXXX
0002   6013  AYXXX


From these results When executed with the outer query 

pNo C_ID  P_Links Count 
=======================
 0  0001 AA - AL 199
 1  0001 AL - BE 200
 2  0001 BE - SA 200
 .
 .
 .
 .
 29 0001 ZA - ZU 200
 30 0001 AA - ZW 200 --->> This record gets incorrect
 31 0002  

record with pNo as 30 gets incorrect as it clubs the page limit of 200 with other C_Id i.e., 0002. 
But We dont want to include the count of C_ID 0002 in C_ID 0001. And want to keep the count 
seperate.

Ideally the record on pNo 30 should be -
30 0001 ZU - ZW 11

For C_ID 0001 only 11 names are left before it switches to 0002 (C_ID)

In other words, We want to build the page links for all c_id order by the c_id's with a page limit 
of 200 ( able to achieve till this point) and dont want to mix the count/page links of c_ids (This 
is what our issue is how to break or switch when the C_ID changes).


Can you please suggest how to approach the above.

 


Followup   March 24, 2008 - 9am Central time zone:

you'd need to start from scratch here, I do not go back up and re-read stuff from weeks/months ago. (includes creating data - at least a very clear link to create the data, back into this page - every review has a link associated with it)

sounds like you need to PARTITION by c_id if you want things to reset - do you understand fully how analytics work?

Please play around with partition - to break the data up by C_id if you want things broken up by c_id. Given that you

a) know analytics
b) know what you want to do

this becomes just an exercise it applying A to B - so dig a little more into analytics, get the partition clause well understood and go from there.
3 stars Partition along with analytic functions   March 27, 2008 - 4pm Central time zone
Reviewer: Sai from NJ
Hi Tom,

Thanks for your partition. I am getting the partition rows in the inner query but not able to come 
through the outer query which involves analaytic functions.

I am trying to group such that maximum of 200 records can be displayed for each of C_ID

===========================================================
select trunc( rn / 200 ),substr( min(LN), 1, 2) || ' - ' || substr(max(LN), 1, 2 ) PAGE_LINKS
      ,count(*)
  from (select C_ID, row_number() over (PARTITION by C_ID order by C_ID) rn,LAST_NAME LN
          from TAB_NAMES
         where rownum <= 10000 
     order by C_ID)
 group by trunc( rn / 200)
 order by 1;
===========================================================

Where the inner query as below -  

=======================
  select C_ID, row_number() over (PARTITION BY C_ID order by C_ID) rn,LAST_NAME LN
    from TAB_NAMES
  where rownum <= 10000 
  order by C_ID
=======================

Which results as below which is expected and fine.

C_ID Rn    Ln
==============
0001  0     AAXXX
0001  1     AEXXX
0001  2     AYXXX 
.
.
.
0001   6010  ZUXXX
0002   1  AAXXX -->> Row number started again with Partition
0002   2  AEXXX
0002   3  AYXXX


From the above results I want to group such that each link can have 200 records for each of c_ID.

I have tried and followed other details too but could not get to solve the above query to obtain 
correct results.

Analytic function does not allow me to apply Partition again on to it. Any suggestions.


Thanks,
Sai





Followup   March 30, 2008 - 8am Central time zone:

... I am trying to group such that maximum of 200 records can be displayed for each
of C_ID
...

I don't think that is true, aren't you trying to limit a page to 200, regardless of the number of c_id


as stated, start over, full example - pretend I haven't read your stuff for weeks (because that is the case).

since prior work doesn't answer your question, ignore it - do not include snippets of code that do not work - rather include detailed specifications of the goal, the need instead. then and only then can we work to a solution.
3 stars Pagination Query with Partition based on C_ID   March 31, 2008 - 4pm Central time zone
Reviewer: Sai from NJ
Hi Tom,

I don't think that is true, aren't you trying to limit a page to 200, regardless of the number of 
c_id 

>> No I am not trying limit a page to 200 regardless of C_ID.

Consider each C_ID as a group. So with that group I want Page No.s & Count's such that each page 
can max out at 200.

I am trying to create VIEW of results for all C_ID's since only at runtime I will know for which 
C_ID I have to fetch the page links.

Hope you understand my objective.




Followup   March 31, 2008 - 4pm Central time zone:

nope, start over is what I said above, I'm not going to read this that and the other review to get caught up.

I'll keep saying that, or just start ignoring the request.


create table, insert into, detailed, very very very detailed explanation of everything. If you already typed it in - just cut and pasted it into a nice readable understandable statement of the problem.



All information and materials provided here are provided "as-is"; Oracle disclaims all express and implied warranties, including, the implied warranties of merchantability or fitness for a particular use. Oracle shall not be liable for any damages, including, direct, indirect, incidental, special or consequential damages for loss of profits, revenue, data or data use, incurred by you or any third party in connection with the use of this information or these materials.

About Oracle | Legal Notices and Terms of Use | Privacy Statement