【问题标题】:Rails Query - Group By with 2 groupsRails Query - Group By 2组
【发布时间】:2015-02-23 17:48:10
【问题描述】:

在我的网站中,我的用户有一个属性localidade。这指定了他们居住的地方。

我正在尝试按以下方式对结果进行分组:

localidade   | Number of Users
-------------+--------------
New York     | 6
Not New York | 8

我想要来自纽约的用户数量以及来自纽约以外任何地方的用户数量。

我试过这个:

User.group("lower(localidade) = 'new york'").count

但由于我没有来自纽约的任何用户,并且只有 1 个不是来自纽约的用户,所以它返回:

{false => 1}

我可以为组指定别名吗?有没有办法以这种方式对结果进行分组?

我会将结果用于 Graphkick 的饼图。

【问题讨论】:

  • 您使用的是哪个数据库?

标签: sql ruby-on-rails postgresql aggregate-functions aggregate-filter


【解决方案1】:

您可以编写查询:

User.group("lower(localidade)")
    .select("CASE WHEN lower(localidade) = 'new york' THEN COUNT(id) END AS NewYork, 
             CASE WHEN lower(localidade) != 'new york' THEN COUNT(id) END AS Non-NewYork")

9.4 开始,您可以将FILTER聚合表达式一起使用:

User.group("lower(localidade)")
    .select("COUNT(id) FILTER (WHERE lower(localidade) != 'new york') AS NonNewyork, 
             COUNT(id) FILTER (WHERE lower(localidade) = 'new york') AS Newyork")

我创建了一个表格来解释和测试上面的 sql,它们按预期工作:

[shreyas@rails_app_test (master)]$ rails db
psql (9.4.1)
Type "help" for help.

app_development=# select id, location, name from people;
 id | location | name
----+----------+------
  2 | X        | foo
  3 | X        | foo
  4 | Y        | foo
(3 rows)

app_development=# SELECT COUNT(id) FILTER(WHERE lower(location) != 'x') AS Non_X_loc, COUNT(id) FILTER (WHERE lower(location) = 'x') AS X_loc FROM "people";
 non_x_loc | x_loc
-----------+-------
         1 |     2
(1 row)

现在让我跳到 Rails 控制台,并测试等效的 Rails 代码:

[2] pry(main)> p = Person.select("COUNT(id) FILTER(WHERE lower(location) != 'x') AS Non_X_loc, COUNT(id) FILTER (WHERE lower(location) = 'x') AS X_loc ")
  Person Load (0.5ms)  SELECT COUNT(id) FILTER(WHERE lower(location) != 'x') AS Non_X_loc, COUNT(id) FILTER (WHERE lower(location) = 'x') AS X_loc  FROM "people"
=> [#<Person:0x007fd85ed71980 id: nil>]
[3] pry(main)> p.first.attributes
=> {"id"=>nil, "non_x_loc"=>1, "x_loc"=>2}
[6] pry(main)> Person.group("lower(location)").select("CASE WHEN lower(location) = 'x' THEN COUNT(id) END AS X_loc, CASE WHEN lower(location) != 'x' THEN COUNT(id) END AS Non_X_loc")
  Person Load (0.6ms)  SELECT CASE WHEN lower(location) = 'x' THEN COUNT(id) END AS X_loc, CASE WHEN lower(location) != 'x' THEN COUNT(id) END AS Non_X_loc FROM "people" GROUP BY lower(location)
=> [#<Person:0x007fd8608281e8 id: nil>, #<Person:0x007fd860828008 id: nil>]
[7] pry(main)> p = _
=> [#<Person:0x007fd8608281e8 id: nil>, #<Person:0x007fd860828008 id: nil>]
[8] pry(main)> p.map { |rec| rec.attributes }
=> [{"id"=>nil, "x_loc"=>nil, "non_x_loc"=>1}, {"id"=>nil, "x_loc"=>2, "non_x_loc"=>nil}]
[9] pry(main)> p.map { |rec| rec.attributes.except('id') }
=> [{"x_loc"=>nil, "non_x_loc"=>1}, {"x_loc"=>2, "non_x_loc"=>nil}]

更新

您只能从数据库级别删除那些nil

导轨代码:

[shreyas@rails_app_test (master)]$ rails c
Loading development environment (Rails 4.2.0)
[1] pry(main)> Person.group("lower(location)").select("CASE WHEN lower(location) = 'x' THEN COUNT(id) ELSE 0 END AS X_loc, CASE WHEN lower(location) != 'x' THEN COUNT(id) ELSE 0 END AS Non_X_loc")
  Person Load (0.9ms)  SELECT CASE WHEN lower(location) = 'x' THEN COUNT(id) ELSE 0 END AS X_loc, CASE WHEN lower(location) != 'x' THEN COUNT(id) ELSE 0 END AS Non_X_loc FROM "people" GROUP BY lower(location)
=> [#<Person:0x007fd858c100b0 id: nil>, #<Person:0x007fd860853e88 id: nil>]
[2] pry(main)> p = _
=> [#<Person:0x007fd858c100b0 id: nil>, #<Person:0x007fd860853e88 id: nil>]
[3] pry(main)> p.map { |rec| rec.attributes }
=> [{"id"=>nil, "x_loc"=>0, "non_x_loc"=>1}, {"id"=>nil, "x_loc"=>2, "non_x_loc"=>0}]
[4] pry(main)> p.map { |rec| rec.attributes.except('id') }
=> [{"x_loc"=>0, "non_x_loc"=>1}, {"x_loc"=>2, "non_x_loc"=>0}]
[5] pry(main)> p = Person.select("count(CASE WHEN lower(location) = 'x' THEN 1 END) AS X_loc, count(CASE WHEN lower(location) != 'x' THEN 1 END) AS Non_X_loc").group("lower(location)")
  Person Load (0.9ms)  SELECT count(CASE WHEN lower(location) = 'x' THEN 1 END) AS X_loc, count(CASE WHEN lower(location) != 'x' THEN 1 END) AS Non_X_loc FROM "people" GROUP BY lower(location)
=> [#<Person:0x007fd85b150f78 id: nil>, #<Person:0x007fd85b150230 id: nil>]
[6] pry(main)> p.map { |rec| rec.attributes.except('id') }
=> [{"x_loc"=>0, "non_x_loc"=>1}, {"x_loc"=>2, "non_x_loc"=>0}]

SQL

app_development=# select CASE WHEN lower(location) = 'x' THEN COUNT(id) ELSE 0 END AS X_loc, CASE WHEN lower(location) != 'x' THEN COUNT(id) ELSE 0 END AS Non_X_loc from people group by lower(location);
 x_loc | non_x_loc
-------+-----------
     0 |         1
     2 |         0
(2 rows)
app_development=# select count(CASE WHEN lower(location) = 'x' THEN 1 END) AS X_loc, count(CASE WHEN lower(location) != 'x' THEN 1 END) AS Non_X_loc from people group by lower(location);
 x_loc | non_x_loc
-------+-----------
     0 |         1
     2 |         0
(2 rows)

更新-II

获得与FILTER相同的输出的经典方法:

app_development=# select count(CASE WHEN lower(location) = 'x' THEN 1 END) AS X_loc, sum(CASE WHEN lower(location) != 'x' THEN 1 END) AS Non_X_loc from people;
 x_loc | non_x_loc
-------+-----------
     2 |         1
(1 row)

app_development=# select sum(CASE WHEN lower(location) = 'x' THEN 1 END) AS X_loc, sum(CASE WHEN lower(location) != 'x' THEN 1 END) AS Non_X_loc from people;
 x_loc | non_x_loc
-------+-----------
     2 |         1
(1 row)

app_development=# select id, location, name from people;
 id | location | name
----+----------+------
  2 | X        | foo
  3 | X        | foo
  4 | Y        | foo
(3 rows)

app_development=#

在 Rails 中:-

Loading development environment (Rails 4.2.0)
[1] pry(main)> p = Person.select("sum(CASE WHEN lower(location) = 'x' THEN 1 END) AS X_loc, sum(CASE WHEN lower(location) != 'x' THEN 1 END) AS Non_X_loc")
  Person Load (0.6ms)  SELECT sum(CASE WHEN lower(location) = 'x' THEN 1 END) AS X_loc, sum(CASE WHEN lower(location) != 'x' THEN 1 END) AS Non_X_loc FROM "people"
=> [#<Person:0x007fd85b6e6a78 id: nil>]
[2] pry(main)> p.first.attributes.except("id")
=> {"x_loc"=>2, "non_x_loc"=>1}
[3] pry(main)> p = Person.select("count(CASE WHEN lower(location) = 'x' THEN 1 END) AS X_loc, count(CASE WHEN lower(location) != 'x' THEN 1 END) AS Non_X_loc")
  Person Load (0.5ms)  SELECT count(CASE WHEN lower(location) = 'x' THEN 1 END) AS X_loc, count(CASE WHEN lower(location) != 'x' THEN 1 END) AS Non_X_loc FROM "people"
=> [#<Person:0x007fd85b77f098 id: nil>]
[4] pry(main)> p.first.attributes.except("id")
=> {"x_loc"=>2, "non_x_loc"=>1}
[5] pry(main)>

【讨论】:

    【解决方案2】:

    老实说,您所拥有的一切正常,您只需要了解,如果 true(或 false 的哈希值)中没有值,那么该值必须默认为零,您可以这样做.to_i 将是 nil 值。所以,例如:

    ny_count = User.group("lower(localidade) = 'new york'").count
    
    "New York: #{ny_count[true].to_i}
    Not New York: #{ny_count[false].to_i}
    "
    

    【讨论】:

      猜你喜欢
      • 2013-11-20
      • 1970-01-01
      • 1970-01-01
      • 2016-09-28
      • 2015-02-17
      • 1970-01-01
      • 1970-01-01
      • 2012-08-12
      • 1970-01-01
      相关资源
      最近更新 更多