[英]Chartkick group and order by at the same time
I have a working chart in chartkick but months are not showing in order我在 chartkick 中有一个工作图表,但月份没有按顺序显示
(October -> March -> February) (十月 -> 三月 -> 二月)
I need them in a correct order我需要它们以正确的顺序
(February -> March -> October ) (二月 -> 三月 -> 十月)
My schema我的架构
create_table "business_data", force: :cascade do |t|
t.integer "net_sales"
t.text "next_steps"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "month_name"
t.date "start_date"
t.date "end_date"
end
Controller Controller
@business_data = BusinessDatum.all.order(start_date: :asc)
View看法
<%= line_chart @business_data.group(:month_name).sum(:net_sales) %>
By adding the .order
I get the following error:通过添加
.order
我得到以下错误:
PG::GroupingError: ERROR: column "business_data.start_date" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: ...a" GROUP BY "business_data"."month_name" ORDER BY "business_...
¿How can I order the months in the correct order? ¿我怎样才能以正确的顺序排列月份?
tl;dr tl;博士
You can try finding the aggregates sales per month_name
, and then sort them您可以尝试查找每月的总
month_name
,然后对其进行排序
Here is what that looks like这就是它的样子
<%= line_chart BusinessDatum.all.group(:month_name).sum(:net_sales).sort_by {|s| Date::MONTHNAMES.index(s[0])} %>
Note: I put the whole query on a single line, but that should work even if you break it between controller and action (or preferably move completely into the controller action)注意:我将整个查询放在一行上,但即使您在 controller 和操作之间中断它也应该有效(或者最好完全进入 controller 操作)
Longer Discussion:更长的讨论:
Based on the question, I am going to guess that there is a conceptual misunderstanding about how ActiveRecord/Postgres is executing your query.基于这个问题,我猜测对于 ActiveRecord/Postgres 如何执行您的查询存在概念上的误解。 If you look at the original query
如果您查看原始查询
BusinessDatum.all.order(start_date: :asc).group(:month_name).sum(:net_sales)
the SQL that this expression would generate is该表达式将生成的 SQL 是
; SQL generated by AR
SELECT
SUM("net_sales"), "month_name"
FROM
"business_data"
GROUP BY
"month_name"
ORDER BY
"start_date" ASC
I am gonna guess that you were expecting Postgres to first order the data by start_date
and then do a group_by
(assuming that if grouping is applied to the ordered data, the aggregations will be ordered as well).我猜你希望 Postgres 首先按
start_date
对数据进行排序,然后执行group_by
(假设如果对有序数据应用分组,聚合也将被排序)。 However, that is not how Postgres works.然而,这不是 Postgres 的工作方式。
In fact, if we change the query to have order by
before group by
, it would be an invalid query.事实上,如果我们将查询更改为在
group by
之前有order by
,这将是一个无效的查询。 The following is an invalid query以下是无效查询
; this query is invalid because the order of order by and group by is not correct
SELECT
SUM("net_sales"), "month_name"
FROM
"business_data"
ORDER BY
"start_date" ASC
GROUP BY
"month_name"
Let's dig a little more.让我们再挖一点。 If you look at the SQL generated by AR, the error that you are seeing gets clear.
如果您查看 AR 生成的 SQL,您看到的错误就会很清楚。 Postgres is going to take the data in the table, group the data by month, and then sum the
net_sales
. Postgres 将取表中的数据,按月份对数据进行分组,然后对
net_sales
。 When it does that the outcome will contain only the sum(net_sales)
and month_name
.当它这样做时,结果将仅包含
sum(net_sales)
和month_name
。 The data will look something like this数据看起来像这样
sum(net_sales)![]() |
month_name![]() |
---|---|
100 ![]() |
October![]() |
200 ![]() |
February![]() |
300 ![]() |
March![]() |
As you can see, the grouped data does not contain the start_date
.如您所见,分组数据不包含
start_date
。 In fact, it probably would not make sense to have a single start_date
here as the start_date
column can contain lots of values.事实上,这里有一个
start_date
可能没有意义,因为start_date
列可以包含很多值。 This is why the error you saw gave you two options to fix:这就是为什么您看到的错误为您提供了两个修复选项:
start_date
in the group by
clause (so that the start_date
is part of the grouped data (which will then allow you to order by start_date
. If you add start_date
in the group clause though, the data will look something like this (the month_name
and start_date
are no longer unique in the grouped datagroup by
子句中使用start_date
(以便start_date
是分组数据的一部分(然后允许您按start_date
排序。如果您在 group 子句中添加start_date
,数据将看起来像这样( month_name
和start_date
在分组数据中不再唯一sum(net_sales)![]() |
month_name![]() |
start_date![]() |
---|---|---|
50 ![]() |
October![]() |
Oct 1, 2022 ![]() |
50 ![]() |
October![]() |
Oct 2, 2022 ![]() |
200 ![]() |
February![]() |
Feb 1, 2022 ![]() |
300 ![]() |
March![]() |
March 5, 2022 ![]() |
max(start_date)
)max(start_date)
) I don't think either of the two suggestions makes sense in this case.在这种情况下,我认为这两个建议中的任何一个都没有意义。
Note, that the aggregated data does not guarantee that the data is ordered by month_name
by default.请注意,聚合数据不保证数据默认按
month_name
名称排序。 You can try order by month_name
.您可以尝试
order by month_name
。 However, I do not think that will give you data in the order you want because order by month_name
will use the alphabetic ordering of months.但是,我认为这不会按照您想要的顺序为您提供数据,因为
order by month_name
排序将使用月份的字母顺序。
So, one solution would be to generate the aggregate on the database as follows:因此,一种解决方案是在数据库上生成聚合,如下所示:
# you do not need the "all" here; it should work without it
@business_data = BusinessDatum.all.group(:month_name).sum(:net_sales)`
The generated data will contain the month_name
(as strings).生成的数据将包含
month_name
(作为字符串)。 The output of the above expression will be a hash上述表达式的 output 将是 hash
{"March" => 300, "February" => 200, "October" => 300}
You can then sort this data on the application server (ie within rails)然后,您可以在应用程序服务器上对这些数据进行排序(即在 rails 内)
@business_data.sort_by {|s| Date::MONTHNAMES.index(s[0])}
This will generate the data in the calendar month order这将按日历月顺序生成数据
[ ["February", 200], ["March", 300], ["October", 100] ]
Here the sort
method converted the hash to an array while sorting, but that is okay (probably preferable over hash as arrays communicate ordering) since chartkick can take array or hash as arguments for line chart. Here the
sort
method converted the hash to an array while sorting, but that is okay (probably preferable over hash as arrays communicate ordering) since chartkick can take array or hash as arguments for line chart. If, for some reason, you do want a hash, you can convert it to hash by using the to_h
function on the array.如果出于某种原因,您确实需要 hash,您可以使用阵列上的
to_h
function 将其转换为 hash。
Caveat: One assumption that I made here is that Business Data can have multiple entries for a given month, where the start_date
for entries in a given month, may or may not be unique.警告:我在这里所做的一个假设是,业务数据在给定月份可以有多个条目,其中给定月份中条目的
start_date
可能是唯一的,也可能不是唯一的。
Like the error msg points out, in a SELECT
query with aggregate functions, ORDER BY
only allows references to grouped or aggregated columns.就像错误消息指出的那样,在带有聚合函数的
SELECT
查询中, ORDER BY
只允许引用分组或聚合列。 Other references would be ambiguous.其他参考将是模棱两可的。 But - as a quick fix - you can work with an expression based on the grouped column
month_name
:但是- 作为一个快速修复 - 您可以使用基于分组列
month_name
的表达式:
SELECT month_name, sum(net_sales)
FROM business_data
GROUP BY month_name
ORDER BY to_date(month_name, 'Month'); -- date derived from month name
If start_date
is guaranteed to lie within month_name
, you can also:如果
start_date
保证在month_name
内,您还可以:
SELECT month_name, sum(net_sales)
FROM business_data
GROUP BY date_trunc('month', start_date), month_name
ORDER BY date_trunc('month', start_date);
This also enforces the same year (like you probably want).这也强制执行同一年(就像您可能想要的那样)。
Storing the name of the month is not very useful to begin with.存储月份的名称一开始并不是很有用。 (What about the year anyway?)
(那一年呢?)
If possible, fix your relational design to store a date
instead.如果可能,请修复您的关系设计以存储
date
。 Or, if start_date
always lies within month_name
, simply drop the column month_name
.或者,如果
start_date
始终位于month_name
内,只需删除列month_name
。 It's just redundant ballast.这只是多余的镇流器。 Don't store redundant data.
不要存储冗余数据。 You can derive the name of the month from
start_date
cheaply with to_char()
:您可以使用
to_char()
廉价地从start_date
派生月份名称:
SELECT to_char(start_date, 'Month') AS month_name
However, to group and order by month, throw in date_trunc()
like above:但是,要按月分组和排序,请像上面一样输入
date_trunc()
:
SELECT to_char(date_trunc('month', start_date), 'Month') AS month_name, sum(net_sales)
FROM business_data
GROUP BY date_trunc('month', start_date)
ORDER BY date_trunc('month', start_date);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.