Reputation: 4574
I got a values
table such as:
id | user_id | value | date
---------------------------------
1 | 12 | 38 | 2014-04-05
2 | 15 | 19 | 2014-04-05
3 | 12 | 47 | 2014-04-08
I want to retrieve all values for given dates. However, if I don't have a value for one specific date, I want to get the previous available value. For instance, with the above dataset, if I query values for user 12 for dates 2014-04-07
and 2014-04-08
, I need to retrieve 38 and 47.
I succeeded using two queries like:
SELECT *
FROM values
WHERE date <= $date
ORDER BY date DESC
LIMIT 1
However, it would require dates.length
requests each time. So, I'm wondering if there is any more performant solution to retrieve all my values in a single request?
Upvotes: 1
Views: 26
Reputation: 32179
In general, you would use a VALUES
clause to specify multiple values in a single query.
If you have only occasional dates missing (and thus no big gaps in dates between rows for any particular user_id
) then this would be an elegant solution:
SELECT dt, coalesce(value, lag(value) OVER (ORDER BY dt)) AS value
FROM (VALUES ('2014-04-07'::date), ('2014-04-08')) AS dates(dt)
LEFT JOIN "values" ON "date" = dt AND user_id = 12;
The lag()
window function picks the previous value
if the current row does not have a value
.
If, on the other hand, there may be big gaps, you need to do some more work:
SELECT DISTINCT dt, first_value(value) OVER (ORDER BY diff) AS value
FROM (
SELECT dt, value, dt - "date" AS diff
FROM (VALUES ('2014-04-07'::date), ('2014-04-08')) AS dates(dt)
CROSS JOIN "values"
WHERE user_id = 12) sub;
In this case a CROSS JOIN
is made for user_id = 12
and differences between the dates in the VALUES
clause and the table rows computed, in a sub-query. So every row has a value for field value
. In the main query the value
with the smallest difference is selected using the first_value()
window function. Note that ordering on diff
and picking the first row would not work here because you want values for multiple dates returned.
Upvotes: 1