Hi @jonmeyers_io XD Is it possible to view how many times a field in a row in a @supabase table has been updated in a given time frame? An example would be views of an ad in the last 7 days, or 30 days, etc… Thanks :-)
Create a select trigger in PostgreSQL
2nd February 2023This tweet from @rchrdnsh got me thinking, I wonder if there is a way to set up a trigger on select?
Turns out, No!
“The event can be
INSERT
,DELETE
,UPDATE
orTRUNCATE
” - PostgreSQL Tutorial
And any time I hit a point where I think I can ‘t do something in Postgres, I start playing with PostgreSQL Functions.
So let ‘s say we have a simple posts
table:
create table if not exists posts (
id bigint generated by default as identity primary key,
created_at timestamp with time zone default timezone('utc'::text, now()) not null,
title text not null,
views int default 0 not null
);
And let ‘s insert a test post:
insert into posts(title)
values('First post');
Now we want a function to increment the views
column every time we select a particular post:
create or replace function get_post(id int) returns posts as
$$
-- increment views
update posts p
set views = views + 1
where p.id = get_post.id;
-- return posts
select *
from posts p
where p.id = get_post.id;
$$ language sql security definer;
We can call this function with a select statement in SQL:
select * from get_post(1);
If you ‘re using Supabase, you can trigger this function with RPC in supabase-js
🚀
const { data } = await supabase.rpc("get_post", {
id: 1,
});
To be able to look at views across time — last 7 days etc — we would need another table — we can call this post_views
— which tracks the number of views for each post. We can insert a new row into this table every time a post is viewed.
Okay, let ‘s start over.
drop function get_post;
drop table posts;
And now, let ‘s create the two related tables:
create table if not exists posts (
id bigint generated by default as identity primary key,
created_at timestamp with time zone default timezone('utc'::text, now()) not null,
title text not null
);
create table if not exists post_views (
id bigint generated by default as identity primary key,
created_at timestamp with time zone default timezone('utc'::text, now()) not null,
post_id bigint references posts not null
);
With an example post:
insert into posts(title)
values('First post');
Our function can now insert a new row into the post_views
table, and then return our posts:
create or replace function get_post(id int) returns posts as
$$
-- increment views
insert into post_views(post_id)
values(get_post.id);
-- return posts
select *
from posts p
where p.id = get_post.id;
$$ language sql security definer;
We can call this function the exact same way, so in a select statement in SQL:
select * from get_post(1);
And with RPC and supabase-js
:
const { data } = await supabase.rpc("get_post", {
id: 1,
});
Now we can query how many times each post was been viewed:
select posts.*, count(post_views.post_id) as views
from posts
left join post_views
on posts.id = post_views.post_id
group by posts.id;
And to filter down to just the posts that had views from the last 7 days, we can add a where
clause that uses an interval
:
select posts.*, count(post_views.post_id) as views
from posts
left join post_views
on posts.id = post_views.post_id
where post_views.created_at > now() - interval '7 days'
group by posts.id;
Now, if we are going to use this complex query often, or want to query it from supabase-js
, we can wrap it in a view.
create or replace view post_views_in_last_week as
select posts.*, count(post_views.post_id) as views
from posts
left join post_views
on posts.id = post_views.post_id
where post_views.created_at > now() - interval '7 days'
group by posts.id;
We can now treat this view like any table in our db. We can write a select statement with SQL:
select * from post_views_in_last_week;
And even tack on an additional where
clause to filter this down further:
select *
from post_views_in_last_week
where title = 'First post';
And since this view is just like any other other table, we can query it very easily with supabase-js
:
const { data } = await supabase.from("post_views_in_last_week").select();
Found this article useful? Let me know on Twitter! 🐦