1

在我的网站上我的用户有一个属性localidade。这指定他们居住的地方。Rails Query - Group By with 2 groups

我试图做一个查询,我组的结果通过以下方式:

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

我想从纽约的用户数量,并从其他地方,但纽约的用户数量。

我尝试这样做:

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

但因为我没有来自纽约的任何用户,只有1不是从纽约返回:

{false => 1} 

我是否能够给别名组?有没有将这种方式分组的结果?

我要使用Graphkick的饼图结果。

+0

您正在使用哪个数据库? – 2015-02-23 17:52:25

+0

我正在使用PostgrSQL – Lokuzt 2015-02-23 17:53:08

回答

2

你可以写你的查询:

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 ,他们按预期工作:

[[email protected]_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代码:

[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}] 

更新

您可以删除只有DB级别的nil

Rails代码:

[[email protected]_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)> 
0

老实说,你的工作正常,你只需要明白,如果没有价值的散列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} 
"