Rails, Count of Users Grouped by created_at day - PostgreSQL - ruby-on-rails

I am using Rails 5, PostgreSQL. I need to get count of users grouped by created_at day, using postgres DATE_TRUNC.
The conditions are users created within a date range and have orders within the same date range.
Below is my code which result in an AmbiguousFunction error
Spree::User.joins(:orders)
.where(spree_orders: { completed_at: params[:start_date]..params[:end_date] })
.order("DATE_TRUNC('day', 'created_at')")
.group("DATE_TRUNC('day', 'created_at')")
.count
The params of start_date and end_date are as follow:
params[:end_date] = Time.current.end_of_day
params[:start_date] = (Time.current - 200.days).beginning_of_day
I get the following error
ActiveRecord::StatementInvalid: PG::AmbiguousFunction: ERROR: function date_trunc(unknown, unknown) is not unique
and even when I explicitly write spree_users.created_at I get the same error.
Is there a better way to achieve the required or a solution for this error?

ActiveRecord::StatementInvalid: PG::AmbiguousFunction
This error occurs when our query contains a column name, which may belong to more than one table. For example, we have two tables, User and Company, and both of them have a column called name. Now the following query would raise an error similar to the one that you are facing:
User.joins(:companies).where("name = ABC")
This happens because we do not specify which table to search the name in. Hence, ActiveRecord gets confused and cannot create a unique query.
In the case mentioned above, the error can be resolved simply by prepending spree_users to the created_at column name used in the order and group queries:
Spree::User.joins(:orders)
.where(spree_orders: { completed_at: params[:start_date]..params[:end_date] })
.order("DATE_TRUNC('day', 'spree_users.created_at')")
.group("DATE_TRUNC('day', 'spree_users.created_at')")
.count

I think you can use date function from sql to get date of timestamp field, and since table User and SpreeOrder has created_at field, you should inform table name (spree_orders.created_at)
Spree::User.joins(:orders)
.where(spree_orders: { completed_at: params[:start_date]..params[:end_date]})
.order("date(spree_orders.created_at)")
.group("date(spree_orders.created_at)")
.count

Related

group results by field on rails postgres

I'm studying rails at the moment and i'm trying to group results of a query by field.
the query is being created by:
#orders = Order.includes(:product).where(user_id: current_user.id, paid: 0).group(:product_id)
It works fine on sqlite (dev env) but it throws a fatal error while on production (postgreSQL).
Error is:
ActionView::Template::Error (PG::GroupingError: ERROR: column "orders.id" must appear in the GROUP BY clause or be used in an aggregate function
Hmm.. what am i doing wrong here?
Thanks
If you want to group in postgres, you need to either directly select that column or you need to call an aggregate function that uses this column. Count is such an aggregate function and you can achieve that in Rails via
#product_counts = Order.where(user: current_user, paid: 0).group(:product_id).count
Postgres automatically executes Count on the column you grouped by, which is product_id in this case. This will give you a hash like this:
{ product_id => count }
For more information, see here: https://www.tutorialspoint.com/postgresql/postgresql_group_by.htm

Group records by created_at in ActiveRecord

I'm trying to find how many records were created on a given day, to get something like this:
User uploaded 5 objects two days ago
User uploaded 12 objects 21 days ago
I've tried:
Flight.group("created_at::date")
but get the error:
ActiveRecord::StatementInvalid: PG::GroupingError: ERROR: column "flights.id" must appear in the GROUP BY clause or be used in an aggregate function
but when I try adding the id either directly to the group field or by adding another .group I end up grouping on both the id and created_at, which obviously doesn't group anything since there would only be one instance of each match.
What am I missing here?
If you're trying to count things then count them:
Flight.group('created_at::date').count # PostgreSQL-only
Flight.group('date(created_at)').count # PostgreSQL and AFAIK MySQL
Flight.group('cast(created_at as date)').count # Standard SQL
That will give you a hash with dates as keys and counts as values. Otherwise, the database won't know which values from the rows in the group should be used to represent the group. If you add the count aggregate function then the ambiguity is gone and the database knows what you want it to do.

.distinct not returning unique entries

I have a table of Projects, in that table, there may be the same project multiple times with the same name, however the created_at month will be different. I'm trying to select the most recent record of each project in my table. Using select works, however I need the entire record so that then I can loop through the records and print out different attributes eg price or what not.
I've tried:
Project.distinct(:project_name) – Prints all records (to check this I copied the project name and did a find and all projects with the identical name would still print out)
Project.order(project_name: :asc, created_at: :desc).uniq(:project_name) – Same result as above
Project.select(:project_name).distinct – Pulls only 1 of each, however it only selects the project name and no other data from the record.
This is the case where DISTINCT ON comes to rescue.
This should work:
Project.select("DISTINCT ON (project_name) *").order("project_name, created_at DESC")
for selecting only particular columns specify them instead of *.
Project.select("DISTINCT ON (project_name) project_name, created_at, price").order("project_name, created_at DESC")

PostgreSQL in Rails: sorting object by two date attributes in descending order

I have an index of active job positions. Currently, they're sorted by the most recent i.e. created_at. However, recently i've added in a renewal feature that updates a renewal_date attribute without updating the created_at.
What I want to achieve is to sort the list in descending order using both renewal_date and created_at.
jobs = Job.where(active: true).reorder("renewal_date DESC NULLS LAST", "created_at DESC")
With this code, the renewed job will always be at the top regardless of how many new jobs are created. How do I sort it so it checks for the date for both attributes and sorts it according to most recent?
Your code will order first by renewal_date with nulls at the end, and then will look at the created_at if two records have the same renewal_date.
I assume that what you want to do is something like "max(renewal_date, created_at)", which will take the "last modification date", or another custom way to compare the two fields.
If then, you can find your answer here : merge and order two columns in the same model
Job.where(active: true).reorder('GREATEST(renewal_date, created_at) DESC')
Let try a standard SQL, so it can work with all types of database:
Job.where(active: true).order('CASE WHEN renewal_date IS NULL THEN created_at ELSE renewal_date END DESC')

Rails Where Created_at Equals specific number

I'm trying to figure out how to do a query where created_at.year == a given year, and created_at.month equals a given month.
However I can't figure out what I'm doing wrong.
Model.where("'created_at.month' = ? AND 'created_at.year' = ?", 7,2013)
results in nothing being shown.
However when I try Model.first.created_at.month ==7 and
Model.first.created_at.year ==2013 I get true for both.
Therefore theoretically my query should be at least be returning my first record.
Anyone know what I'm doing wrong or any alternative way to find records created on specific months?
Note that in my views the month / year will be parameters but for the purposes of this example I used actual values.
using ruby 1.9.3
rails 3.2.13
You can use the extract SQL function, that will extract the month and year of the timestamp:
Model.where('extract(year from created_at) = ? and extract(month from created_at) = ?', '2013','7')
This query should give you the desired result.
created_at is a timestamp; it is not a set of discrete fields in the database. created_at.year and such don't exist in your DB; it's simply a single timestamp field. When you call #model.created_at.year, Rails is loading the created_at field from the database, and creating a Time object from it, which has a #year method you can call.
What you want is to query on a range of dates:
Model.where("created_at >= ? and created_at < ?", Time.mktime(2013, 7), Time.mktime(2013, 8))
This will find any Model with a created_at timestamp in July 2013.

Resources