2014-05-07 27 views
32

试图在使用rails的模式上做不同的处理。不同于Postgresql JSON数据列

2.1.1 :450 > u.profiles.select("profiles.*").distinct 


Profile Load (0.9ms) SELECT DISTINCT profiles.* FROM "profiles" INNER JOIN "integration_profiles" ON "profiles"."id" = "integration_profiles"."profile_id" INNER JOIN "integrations" ON "integration_profiles"."integration_id" = "integrations"."id" WHERE "integrations"."user_id" = $1 [["user_id", 2]] 
PG::UndefinedFunction: ERROR: could not identify an equality operator for type json 
LINE 1: SELECT DISTINCT profiles.* FROM "profiles" INNER JOIN "integ... 
         ^
: SELECT DISTINCT profiles.* FROM "profiles" INNER JOIN "integration_profiles" ON "profiles"."id" = "integration_profiles"."profile_id" INNER JOIN "integrations" ON "integration_profiles"."integration_id" = "integrations"."id" WHERE "integrations"."user_id" = $1 
ActiveRecord::StatementInvalid: PG::UndefinedFunction: ERROR: could not identify an equality operator for type json 
LINE 1: SELECT DISTINCT profiles.* FROM "profiles" INNER JOIN "integ... 
         ^
: SELECT DISTINCT profiles.* FROM "profiles" INNER JOIN "integration_profiles" ON "profiles"."id" = "integration_profiles"."profile_id" INNER JOIN "integrations" ON "integration_profiles"."integration_id" = "integrations"."id" WHERE "integrations"."user_id" = $1 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/rack-mini-profiler-0.9.1/lib/patches/sql_patches.rb:109:in `prepare' 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/rack-mini-profiler-0.9.1/lib/patches/sql_patches.rb:109:in `prepare' 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/activerecord-4.0.4/lib/active_record/connection_adapters/postgresql_adapter.rb:834:in `prepare_statement' 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/activerecord-4.0.4/lib/active_record/connection_adapters/postgresql_adapter.rb:795:in `exec_cache' 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/activerecord-4.0.4/lib/active_record/connection_adapters/postgresql/database_statements.rb:139:in `block in exec_query' 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/activerecord-4.0.4/lib/active_record/connection_adapters/abstract_adapter.rb:442:in `block in log' 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/activesupport-4.0.4/lib/active_support/notifications/instrumenter.rb:20:in `instrument' 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/activerecord-4.0.4/lib/active_record/connection_adapters/abstract_adapter.rb:437:in `log' 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/activerecord-4.0.4/lib/active_record/connection_adapters/postgresql/database_statements.rb:137:in `exec_query' 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/activerecord-4.0.4/lib/active_record/connection_adapters/postgresql_adapter.rb:908:in `select' 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/activerecord-4.0.4/lib/active_record/connection_adapters/abstract/database_statements.rb:32:in `select_all' 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/activerecord-4.0.4/lib/active_record/connection_adapters/abstract/query_cache.rb:63:in `select_all' 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/activerecord-4.0.4/lib/active_record/querying.rb:36:in `find_by_sql' 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/activerecord-4.0.4/lib/active_record/relation.rb:585:in `exec_queries' 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/activerecord-4.0.4/lib/active_record/association_relation.rb:15:in `exec_queries' 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/activerecord-4.0.4/lib/active_record/relation.rb:471:in `load' 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/activerecord-4.0.4/lib/active_record/relation.rb:220:in `to_a' 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/activerecord-4.0.4/lib/active_record/relation.rb:573:in `inspect' 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/railties-4.0.4/lib/rails/commands/console.rb:90:in `start' 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/railties-4.0.4/lib/rails/commands/console.rb:9:in `start' 
    from /Users/mmahalwy/.rvm/gems/ruby-2.1.1/gems/railties-4.0.4/lib/rails/commands.rb:62:in `<top (required)>' 
    from bin/rails:4:in `require' 
    from bin/rails:4:in `<main>'2.1.1 :451 > 

得到一个错误PG::UndefinedFunction: ERROR: could not identify an equality operator for type json

转换为Hstore是不是在这种情况下,我的选择。任何解决方法?

回答

42

这背后的原因是,在PostgreSQL的(高达9.3)没有用于json没有定义相等操作符(即val1::json = val2::json总会抛出此异常) - 在9.4会有一个用于jsonb类型。

一种解决方法是,您可以将您的json字段投射到text。但是这不会涵盖所有JSON平衡。 f.ex. {"a":1,"b":2}应该等于{"b":2,"a":1},但如果铸造为text,则不会相同。

另一个解决方法是(如果你有该表的主键 - 这应该是)你可以使用DISTINCT ON (<expressions>) form

u.profiles.select("DISTINCT ON (profiles.id) profiles.*") 

注意:一个已知的警告为DISTINCT ON

DISTINCT ON表达式必须匹配最左边的ORDER BY表达式。 ORDER BY子句通常包含额外的表达式,这些表达式决定了每个DISTINCT ON组内行的期望优先级。

+0

我实际上结束了做的DISTINCT ON profiles.id。这对我来说是最好的解决方案 –

+0

如何在选择语句中将您的json作为文本进行投射? – ionescho

+1

@ionescho我相信你现在已经找到了这个,但':: text'应该这样做。 – fzzfzzfzz

4

对不起,我迟到了这个答案,但它可能会帮助别人。

根据我的理解,只有profiles因为多对多连接integrations(您用来确定要访问哪个profiles)才能获得重复项。

正因为如此,你可以使用一个新GROUP BY功能as of 9.1

当GROUP BY存在,它是不是有效的SELECT列表中的表达式是指未分组列除了聚集函数或者内未分组的列在功能上依赖于分组列,因为否则会有多个可能的值返回未分组列。如果分组列(或其子集)是包含未分组列的表的主键,则存在函数依赖关系。

所以你的情况,你可以让Ruby创建查询(对不起,我不知道的Ruby语法你使用)...

SELECT profiles.* 
FROM "profiles" 
    INNER JOIN "integration_profiles" ON "profiles"."id" = "integration_profiles"."profile_id" 
    INNER JOIN "integrations" ON "integration_profiles"."integration_id" = "integrations"."id" 
WHERE "integrations"."user_id" = $1 
GROUP BY "profiles"."id" 

我只取出DISTINCT从您的SELECT条款和添加GROUP BY

仅通过参考GROUP BY中的id,您可以充分利用该新功能,因为所有其余的profiles列都对该ID主键具有“功能上的依赖性”。

不知何故,奇妙的是避免了需要Postgres的做对因列平等检查(即在这种情况下,你json列)。

DISTINCT ON解决方案也很棒,在你的情况下显然是足够的,但你不能像array_agg那样使用聚合函数。你可以用这个GROUP BY方法。快乐的时光! :)

1

如果使用PG 9.4,使用JSONB而不是JSON解决了这个问题 例子:

-- JSON datatype test 

create table t1 (id int, val json); 
insert into t1 (id,val) values (1,'{"name":"value"}'); 
insert into t1 (id,val) values (1,'{"name":"value"}'); 
insert into t1 (id,val) values (2,'{"key":"value"}'); 
select * from t1 order by id; 
select distinct * from t1 order by id; 

-- JSONB datatype test 

create table t2 (id int, val jsonb); 
insert into t2 (id,val) values (1,'{"name":"value"}'); 
insert into t2 (id,val) values (1,'{"name":"value"}'); 
insert into t2 (id,val) values (2,'{"key":"value"}'); 

select * from t2 order by id; 

select distinct * from t2 order by id; 

Result of running the above script : 

CREATE TABLE 
INSERT 0 1 
INSERT 0 1 
INSERT 0 1 
1 | {"name":"value"} 
1 | {"name":"value"} 
2 | {"key":"value"} 

ERROR: could not identify an equality operator for type json 
LINE 1: select distinct * from t1 order by id; 
        ^
CREATE TABLE 
INSERT 0 1 
INSERT 0 1 
INSERT 0 1 
1 | {"name": "value"} 
1 | {"name": "value"} 
2 | {"key": "value"} 

1 | {"name": "value"} 
2 | {"key": "value"} 

正如你可以看到PG成功,而失败的一个JSON意味着DISTINCT上JSONB列 专栏!

尝试也下面,看看在JSONB实际键进行排序:

insert into t2 values (3, '{"a":"1", "b":"2"}'); 
insert into t2 values (3, '{"b":"2", "a":"1"}'); 
select * from t2; 

1 | {"name": "value"} 
1 | {"name": "value"} 
2 | {"key": "value"} 
3 | {"a": "1", "b": "2"} 
3 | {"a": "1", "b": "2"} 

注意, '{ “B”: “2”, “一个”: “1”}' 被插入作为'{ “一”: “1”, “b”: “2”}' 为此PG标识为相同的记录:

select distinct * from t2; 
3 | {"a": "1", "b": "2"} 
2 | {"key": "value"} 
1 | {"name": "value"} 
相关问题