What does ORDER BY within OVER() window function mean in postgresql? - postgresql

I am trying to understand how the ORDER BY clause in the OVER() window function is different from ORDER BY clause in generic SQL.
I was solving the following problem: https://www.pgexercises.com/questions/aggregates/nummembers.html
Produce a monotonically increasing numbered list of members (including guests),
ordered by their date of joining. Remember that member IDs are not guaranteed to be sequential.
The following query is one of the accepted solutions:
SELECT COUNT(*) OVER(ORDER by joindate), firstname, surname FROM cd.members;
As per my understanding, since we are not supplying a PARTITION BY clause in the OVER() function, all the rows in cd.members table form one big partition (let's call it X). When the window function runs, it should order X by joindate, and then COUNT(*) on X would return the number of rows in X which is just the number of rows in cd.members.
But this understanding is incorrect. The 'Answers and Discussion' accompanying the aforementioned problem states:
Since we define an order for the window function, for any given row the window is: start of the dataset -> current row.
The PG documentation on window function states:
You can also control the order in which rows are processed by window functions using ORDER BY within OVER. (The window ORDER BY does not even have to match the order in which the rows are output.)
What I cannot comprehend is why will ORDER BY inside the OVER() stop at the current row? Could you please elaborate how this is working?
Thank you for reading through.

I don't know what to add beyond what the docs (same page as you already linked to) already say:
By default, if ORDER BY is supplied then the frame consists of all
rows from the start of the partition up through the current row, plus
any following rows that are equal to the current row according to the
ORDER BY clause.
I don't now if this required by the SQL standard, but it certainly seems reasonable. Why specify an ORDER BY if you expect it to have no observable effect?

Related

Getting the total number of records in a single knexjs query when using the limit() method

I use knexjs and postgresql. Is it possible in knexjs to get the total of records from the same query in which the limit is used?
For example:
knex.select().from('project').limit(50)
Is it possible to somehow get the total number of records in the same query if there are more than 50?
The question arose due to the fact that my query is much more complex, which uses a lot of subqueries and conditions, and I would not like to make this query twice to get the data in one query and the total number of records (I use the .count() method) from another.
I do not know your obscurification manager (knexjs?) but I would think you should be able to add the window version of the count() function to your select list. In plain SQL something like: Where ... represents your current select list. (see demo)
select ..., count(*) over() total_rows
from project
limit 5;
This works because the window count function counts all rows selected, after all rows selected, but before the LIMIT clause is applied. Note: This adds a column to the result set with the same value in every row.

From group by to window function postgres

my goal is to compare Event Engagement Rate = ( Event Subscribed / Event Participated ) for the newsletter source vs Email Source by using window functions
i Managed to do it by using group by
select source ,sum(event_subscribed/event_participated) as "engagement rate" from events
group by source
i keep failing with the windows function by having too many rows and wrong engagement rates
Thanks for your help.
First you need to understand the difference between the Aggregate functions and the Window variant of the same function. The aggregate functions builds groups as specified in the 'Group By' clause and reduces the result to a single row per group. The Window version also builds groups as specified in the 'Partition By' clause. However, it does not reduce the number of rows, instead it generated the same result in each of the rows within the group. See example. To reduce the Window version to a single row per group use Distinct on expression.
SELECT DISTINCT ON ( expression [, ...] ) keeps only the first row of
each set of rows where the given expressions evaluate to equal. The
DISTINCT ON expressions are interpreted using the same rules as for
ORDER BY (see above). Note that the “first row” of each set is
unpredictable unless ORDER BY is used to ensure that the desired row
appears first.
Your query becomes
select distinct on (source)
source
, sum(event_subscribed/event_participated) as "engagement rate"
from events
order by source.

Accessing current row value with lag function

I want to calculate the difference between the previous and the current column and make it a new column named increase. For this, I'm using the lag window function. The value of the first column is not defined since no previous column exists. I know that a 3rd parameter specifies the default value. However, it depends. For the first row, I want to use the value of another column e.g. the one of count from that current row. This assumes that 0 is increased to count for the first row which is what I need. Specifying the column name as 3rd argument for the lag function does not work correctly and neither does using 0. How can it be done? I'm getting strange results such as quite a random result or even negative numbers.
SELECT *, mycount - lag(mycount, 1) OVER (ORDER BY id, messtime ASC) AS increase FROM measurements;
Window functions cannot be nested either:
ERROR: window function calls cannot be nested
There is another issue with your query: So far your results are in random order, so you may think you are seeing problems that don't exist.
Add ORDER BY id, messtime to your query to see the rows in order. Now you can compare one row with its predecessor directly. Are there still issues? If so, which exactly?
SELECT *, "count" - lag("count", 1) OVER (ORDER BY id, messtime) AS increase
FROM measurements
ORDER BY id, messtime;
COUNT is a reserved word in SQL. It seems the DBMS thinks you want to nest COUNT and LAG somehow.
Use another column name or use quotes for the column:
SELECT *, "count" - lag("count", 1) OVER

running total using windows function in sql has same result for same data

From every references that I search how to do cumulative sum / running total. they said it's better using windows function, so I did
select grandtotal,sum(grandtotal)over(order by agentname) from call
but I realize that the results are okay as long as the value of each rows are different. Here is the result :
Is There anyway to fix this?
You might want to review the documentation on window specifications (which is here). The default is "range between" which defines the range by the values in the row. You want "rows between":
select grandtotal,
sum(grandtotal) over (order by agentname rows between unbounded preceding and current row)
from call;
Alternatively, you could include an id column in the sort to guarantee uniqueness and not have to deal with the issue of equal key values.

n-th row in PostgreSQL for p-quantile

I'm trying to fetch the n-th row of a query result. Further posts suggested the use of OFFSET or LIMIT but those forbid the use of variables (ERROR: argument of OFFSET must not contain variables). Further I read about the usage of cursors but I'm not quite sure how to use them even after reading their PostgreSQL manpage. Any other suggestions or examples for how to use cursors?
My main goal is to calculate the p-quantile of a row and since PostgreSQL doesn't provide this function by default I have to write it on my own.
Cheers
The following returns the 5th row of a result set:
select *
from (
select <column_list>,
row_number() over (order by some_sort_column) as rn
) t
where rn = 5;
You have to include an order by because otherwise the concept of "5th row" doesn't make sense.
You mention "use of variable" so I'm not sure what you are actually trying to achive. But you should be able to supply the value 5 as a variable for this query (or even a sub-select).
You might also want to dig further into windowing functions. Because with that you could e.g. do a sum() over the 3 rows before the current row (or similar constructs) - which could also be useful for you.
if you would like to get 10th record, below query also work fine.
select * from table_name order by sort_column limit 1 offset 9
OFFSET simply skip that many rows before beginning to return rows as mentioned in LIMIT clause.