Window functions

更新时间:
复制 MD 格式

AnalyticDB for PostgreSQL supports all window functions available in PostgreSQL. Window functions perform calculations across a set of rows related to the current row — such as ranking, cumulative distribution, and offset access to adjacent rows — without collapsing those rows into a single result.

For the full PostgreSQL window function reference, see Window Functions.

Quick examples

The following snippets cover the most common window function patterns:

-- Assign a unique row number within each department, ordered by salary
SELECT employee_id, department, salary,
  row_number() OVER (PARTITION BY department ORDER BY salary DESC) AS row_num
FROM employees;

-- Rank employees by salary, with gaps for ties
SELECT employee_id, salary,
  rank() OVER (ORDER BY salary DESC) AS salary_rank
FROM employees;

-- Access the previous row's value to calculate change
SELECT order_date, revenue,
  lag(revenue) OVER (ORDER BY order_date) AS prev_revenue,
  revenue - lag(revenue) OVER (ORDER BY order_date) AS change
FROM daily_sales;

-- Divide rows into four equal groups (quartiles)
SELECT employee_id, salary,
  ntile(4) OVER (ORDER BY salary) AS quartile
FROM employees;

General-purpose window functions

FunctionReturn typeDescriptionExample
row_number()BIGINTReturns the row's position within the partition, starting from 1.row_number() OVER (ORDER BY salary DESC)
rank()BIGINTReturns the rank within the partition. Rows with equal values receive the same rank, and the next rank skips the tied positions. For example, three rows ranked 1 are followed by rank 4.rank() OVER (ORDER BY score DESC)
dense_rank()BIGINTReturns a consecutive rank within the partition. Rows with equal values receive the same rank, but the next rank increments by 1 rather than skipping. For example, three rows ranked 1 are followed by rank 2.dense_rank() OVER (ORDER BY score DESC)
percent_rank()DOUBLE PRECISIONReturns the relative rank of the row as a value from 0 to 1. Formula: (Rank - 1) / (Total number of partition rows - 1), where Rank is the rank() result for the current row. Rows with equal values return the same result.percent_rank() OVER (ORDER BY score)
cume_dist()DOUBLE PRECISIONReturns the cumulative distribution of the row within the partition. Formula: (Number of partition rows before the current row + Number of partition rows with the same value as the current row) / Total number of partition rows.cume_dist() OVER (ORDER BY score)
ntile(number_buckets)INTEGERDivides the partition into the specified number of equal buckets and returns the bucket number for each row. number_buckets must be a positive integer.ntile(4) OVER (ORDER BY salary)
lag(column [,offset [,default]])Same type as columnReturns the value from the row that is offset rows before the current row. If the offset row does not exist, returns default. offset defaults to 1; default defaults to null.lag(revenue, 1, 0) OVER (ORDER BY date)
lead(column [,offset [,default]])Same type as columnReturns the value from the row that is offset rows after the current row. If the offset row does not exist, returns default. offset defaults to 1; default defaults to null.lead(revenue, 1, 0) OVER (ORDER BY date)
first_value(column)Same type as columnReturns the value of the first row within the partition.first_value(salary) OVER (PARTITION BY department ORDER BY salary)
last_value(column)Same type as columnReturns the value of the last row within the partition.last_value(salary) OVER (PARTITION BY department ORDER BY salary)
nth_value(column, nth)Same type as columnReturns the value of the nth row within the partition, counting from 1. Returns null if the row does not exist. nth must be a positive integer. nth_value(column, 1) is equivalent to first_value(column).nth_value(salary, 3) OVER (PARTITION BY department ORDER BY salary)

Choosing between similar functions

rank() vs. dense_rank()

Both assign the same rank to rows with equal values, but differ in how they handle the next rank:

SELECT name, score,
  rank()       OVER (ORDER BY score DESC) AS rank,
  dense_rank() OVER (ORDER BY score DESC) AS dense_rank
FROM scores;
namescorerankdense_rank
Alice9011
Bob9011
Carol9011
Dave8042
Eve7053

Use rank() when gaps in ranking matter (for example, competition scoring). Use dense_rank() when you need contiguous rank values.

lag() vs. lead()

lag() looks backward; lead() looks forward:

SELECT order_date, revenue,
  lag(revenue,  1, 0) OVER (ORDER BY order_date) AS prev_revenue,
  lead(revenue, 1, 0) OVER (ORDER BY order_date) AS next_revenue
FROM daily_sales;

Both accept an optional offset (default: 1) and a default value returned when the offset row does not exist (default: null).