In PostgreSQL the first_value()
function returns the value from the first row in the current window frame.
We can use this function to get the value from the first row in a result set, or from the first row in the current partition, or some other window frame that’s been specified.
Syntax
The syntax goes like this:
first_value ( value )
The value
argument is required. It’s the expression that is evaluated at the first row of the window frame.
Example
Here’s an example to demonstrate:
SELECT
vendorid,
productname,
productprice,
first_value( productprice ) OVER ( ORDER BY productprice )
FROM products;
Result:
vendorid | productname | productprice | first_value ----------+---------------------------------+--------------+------------- 1004 | Bottomless Coffee Mugs (4 Pack) | 9.99 | 9.99 1003 | Hammock | 10.00 | 9.99 1001 | Long Weight (green) | 11.99 | 9.99 1004 | Tea Pot | 12.45 | 9.99 1001 | Long Weight (blue) | 14.75 | 9.99 1001 | Left handed screwdriver | 25.99 | 9.99 1001 | Right handed screwdriver | 25.99 | 9.99 1002 | Sledge Hammer | 33.49 | 9.99 1003 | Straw Dog Box | 55.99 | 9.99 1003 | Chainsaw | 245.00 | 9.99
We can see that the first_value
column returns the value from the first row of the productprice
column. That’s because I specified productprice
as the argument.
Change the Order
In the previous example I sorted the results by the productprice
column in ascending order. Let’s change it to descending order:
SELECT
vendorid,
productname,
productprice,
first_value( productprice ) OVER ( ORDER BY productprice DESC )
FROM products;
Result:
vendorid | productname | productprice | first_value ----------+---------------------------------+--------------+------------- 1003 | Chainsaw | 245.00 | 245.00 1003 | Straw Dog Box | 55.99 | 245.00 1002 | Sledge Hammer | 33.49 | 245.00 1001 | Left handed screwdriver | 25.99 | 245.00 1001 | Right handed screwdriver | 25.99 | 245.00 1001 | Long Weight (blue) | 14.75 | 245.00 1004 | Tea Pot | 12.45 | 245.00 1001 | Long Weight (green) | 11.99 | 245.00 1003 | Hammock | 10.00 | 245.00 1004 | Bottomless Coffee Mugs (4 Pack) | 9.99 | 245.00
This time the highest price is at the first row. Therefore, first_value()
returns that price.
Partition the Results
Let’s add a PARTITION BY
clause to partition by vendor:
SELECT
v.vendorname,
p.productname,
p.productprice,
first_value( p.productprice ) OVER (
PARTITION BY p.vendorid
ORDER BY p.productprice
)
FROM products p INNER JOIN
vendors v ON p.vendorid = v.vendorid;
Result:
vendorname | productname | productprice | first_value ---------------+---------------------------------+--------------+------------- Mars Supplies | Long Weight (green) | 11.99 | 11.99 Mars Supplies | Long Weight (blue) | 14.75 | 11.99 Mars Supplies | Right handed screwdriver | 25.99 | 11.99 Mars Supplies | Left handed screwdriver | 25.99 | 11.99 Randy Roofers | Sledge Hammer | 33.49 | 33.49 Pedal Medals | Hammock | 10.00 | 10.00 Pedal Medals | Straw Dog Box | 55.99 | 10.00 Pedal Medals | Chainsaw | 245.00 | 10.00 Katty Kittens | Bottomless Coffee Mugs (4 Pack) | 9.99 | 9.99 Katty Kittens | Tea Pot | 12.45 | 9.99
We can see that the first_value
column resets with each new partition.
By the way, in this example I also ran a SQL join between the products
table and the vendors
table in order to get each vendor’s name instead of just the ID.
Using a Frame Clause
We can use a frame clause to explicitly specify a window frame to use:
SELECT
vendorid,
productname,
productprice,
first_value( productprice ) OVER (
ORDER BY productprice
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
)
FROM products;
Result:
vendorid | productname | productprice | first_value ----------+---------------------------------+--------------+------------- 1004 | Bottomless Coffee Mugs (4 Pack) | 9.99 | 9.99 1003 | Hammock | 10.00 | 9.99 1001 | Long Weight (green) | 11.99 | 10.00 1004 | Tea Pot | 12.45 | 11.99 1001 | Long Weight (blue) | 14.75 | 12.45 1001 | Left handed screwdriver | 25.99 | 14.75 1001 | Right handed screwdriver | 25.99 | 25.99 1002 | Sledge Hammer | 33.49 | 25.99 1003 | Straw Dog Box | 55.99 | 33.49 1003 | Chainsaw | 245.00 | 55.99
Here, I specified ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
for the frame clause which means that for each row, the window frame consists of three rows; the preceding row, the current row, and the next row. We can see how that affects the outcome of the first_value
column. Given we’re sorting it in ascending order, first_value
always the lowest value from the frame.
Let’s change to descending order:
SELECT
vendorid,
productname,
productprice,
first_value( productprice ) OVER (
ORDER BY productprice DESC
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
)
FROM products;
Result:
vendorid | productname | productprice | first_value ----------+---------------------------------+--------------+------------- 1003 | Chainsaw | 245.00 | 245.00 1003 | Straw Dog Box | 55.99 | 245.00 1002 | Sledge Hammer | 33.49 | 55.99 1001 | Left handed screwdriver | 25.99 | 33.49 1001 | Right handed screwdriver | 25.99 | 25.99 1001 | Long Weight (blue) | 14.75 | 25.99 1004 | Tea Pot | 12.45 | 14.75 1001 | Long Weight (green) | 11.99 | 12.45 1003 | Hammock | 10.00 | 11.99 1004 | Bottomless Coffee Mugs (4 Pack) | 9.99 | 10.00
This time the first_value
is always the highest value of the current frame.
Removing the OVER
Clause
The OVER
clause is required when using nonaggregate window functions such as first_value()
. Removing it results in an error:
SELECT
vendorid,
productname,
productprice,
first_value( productprice )
FROM products;
Result:
ERROR: window function first_value requires an OVER clause LINE 5: first_value( productprice ) ^