473,406 Members | 2,620 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,406 software developers and data experts.

Ranking values within a query (pseudo-ROWNUM)

I hope this helps someone else ... I had struggled some time ago with
attempts to get a rank of values query to work, but then I gave up and
set it aside. I had another reason to attack it, and in between then and
now I learned how to return "setof" values from a function, as well as
how to construct "dynamic" queries inside a function.

Returning the top 10 values from a query is no big deal:

create table my_table (field1 integer, field2 integer, field3 text);
...
select * from my_table order by field1 limit 10;

If you want the top value for each value of field1, a DISTINCT works:

select distinct on (field1) * from my_table
order by field1, field2 desc;

(i.e., the row with the biggest field2 value for each set of rows with
the same field1 value).

However, if you want the top *N* values, it gets a lot trickier.
PostgreSQL lacks the concept of ROWNUM, which would let you filter in
just those rows in the top 5, for example.

Here's what I came up with; I've not applied this to more than a couple
of test cases, so there may very well be flaws in this approach.

create table rank_of_values(rank_of integer, the_value integer);

(The table is necessary so that our functions can return a rowtype.
Someday PostgreSQL may have a way to add a rowtype to the database
without a corresponding table.)

Now there are two functions, with the same name (and Pg isn't confused,
because they have different numbers of arguments).

The first function is a straightforward ranking of values. Given a
column name, and a "FROM" clause, it returns a set of rows with the
column value and rank (tied values do NOT have the same rank).

CREATE FUNCTION fn_rank_values(TEXT,TEXT)
RETURNS setof rank_of_values AS '
DECLARE
t RECORD;
r rank_of_values%ROWTYPE;
curs REFCURSOR;
col ALIAS FOR $1;
stmt ALIAS FOR $2;
rank INTEGER;
BEGIN
OPEN curs FOR EXECUTE 'SELECT "' || col || '" AS
"the_value" ' || stmt;
rank := 1;
LOOP
FETCH curs INTO t;
EXIT WHEN NOT FOUND;
r.rank_of = rank;
r.the_value = t.the_value;
RETURN next r;
rank := 1 + rank;
END LOOP;
CLOSE curs;
RETURN;
END;
' LANGUAGE 'plpgsql' STABLE;

The second function is the "rank-within-group" facility. Given a
value-column, a group-column, and a "FROM" clause, it returns the
original value, and its rank within the group formed by identical values
of the group-column.

CREATE FUNCTION fn_rank_values(TEXT,TEXT,TEXT)
RETURNS setof rank_of_values AS '
DECLARE
t RECORD;
r rank_of_values%ROWTYPE;
curs REFCURSOR;
col ALIAS FOR $1;
grp ALIAS FOR $2;
clause ALIAS FOR $3;
rank INTEGER;
curr_grp INTEGER;
stmt TEXT;
BEGIN
stmt := 'SELECT "' || col || '" AS "the_value", "' || grp
|| '" AS "the_group" ' || clause;
OPEN curs FOR EXECUTE stmt;
rank := 1;
LOOP
FETCH curs INTO t;
EXIT WHEN NOT FOUND;
IF curr_grp IS NULL
THEN
curr_grp = t.the_group;
ELSIF curr_grp != t.the_group
THEN
curr_grp = t.the_group;
rank = 1;
END IF;
r.rank_of = rank;
r.the_value = t.the_value;
RETURN next r;
rank := 1 + rank;
END LOOP;
CLOSE curs;
RETURN;
END;
' LANGUAGE 'plpgsql' STABLE;

Now you can do things like this:

select rank_of, the_value from fn_rank_values('field1','from mytable
order by field1') order by rank_of;

which will give you the rank number and original value of each row.

The second function is more interesting, because that's where you get
the ability to do "top N by X" kinds of queries. For instance,

select field1, field2, field3 from my_table
join fn_rank_values('field1','field2','from my_table order by field1,
field2') on (field1=the_value)
where rank_of <= 5 order by field2, rank_of;
--
Jeff Boes vox 269.226.9550 ext 24
Database Engineer fax 269.349.9076
Nexcerpt, Inc. http://www.nexcerpt.com
...Nexcerpt... Extend your Expertise

---------------------------(end of broadcast)---------------------------
TIP 5: Have you checked our extensive FAQ?

http://www.postgresql.org/docs/faqs/FAQ.html

Nov 23 '05 #1
0 7891

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

5
by: Randy Harris | last post by:
How can I report an average of non zero values? If the values are: 5, 0, 6, 0, 4 I would like the result 5 (15 / 3), not 3 (15 / 5) Thanks for any help...
1
by: Jimmy | last post by:
I have a table that is shown below in ORIGINAL TABLE. It is common for the VEHICLE # field to have repeating vehicles (ID 3,4,5 & 8,9). Usually the Vehicle will have SHIPDATE, DATE_RECEIVED, and...
2
by: bhavik | last post by:
hi i want to know how to hide the values in query string in ASP.net. here i want to send the values from one page to another page through query string with out explicitly visible the values in...
0
by: Jeff Boes | last post by:
Gah, I'm dreadfully sorry. The original functions were cut-and-pasted from a "\df+" window, which meant they lost their quoted-ness, which means if you try to cut and paste from my message to a SQL...
4
by: Marc DVer | last post by:
As a simple example, say there is table 'namelist' with column 'names' as char(20). I would like to do something akin to: select namelist.names as mynames, left(mynames,2) as initials; In...
1
by: srimandadapu | last post by:
hi! i need little help in query SELECT idate,idesc as payoutdesc, SUM(amount) as sum_AMOUNT ,COMMENTS >From payout WHERE IDATE Between #3/1/2006# And #3/31/2006 # and idesc='COFFEE RED DIAMOND'...
2
by: sluster | last post by:
I have a Union Query that is tied directly to a report and one field needs to be modified. The "CountOfAttended" results in a number that needs to have the "Programs.walk-ins" field added to it. ...
0
by: gcardozo | last post by:
Could anyone help me with the task of copying and pasting the values of changing cell references in excel using code for a macro?(macro to copy and paste values within a currently selected column). ...
6
by: keyur1719 | last post by:
Hi, I have a crosstab query which is based on a simple select query. Here is how the query works.. The base query gets it date from employee table and their incentives table for the given...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.