【问题标题】:Distinct on Postgresql JSON data columnPostgresql JSON 数据列不同
【发布时间】:2014-06-23 22:54:42
【问题描述】:

尝试在带有 rails 的模式上做不同的事情。

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。有什么解决办法吗?

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 postgresql postgresql-json


    【解决方案1】:

    这背后的原因是,在 PostgreSQL(最高 9.3)中没有为json 定义相等运算符(即val1::json = val2::json 总是会抛出这个异常)——在 9.4 中将有一个用于@987654324 @类型。

    一种解决方法是,您可以将json 字段转换为text。但这不会涵盖所有 json 等式。前任{"a":1,"b":2} 应该等于 {"b":2,"a":1},但如果转换为 text,则不会相等。

    另一种解决方法是(如果您有该表的主键 - 应该是)您可以使用 DISTINCT ON (&lt;expressions&gt;) form

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

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

    DISTINCT ON 表达式必须匹配最左边的 ORDER BY 表达式。 ORDER BY 子句通常包含附加表达式,用于确定每个 DISTINCT ON 组中行的所需优先级。

    【讨论】:

    • 我实际上最终做了 DISTINCT ONprofiles.id。这对我来说是最好的解决方案
    • 如何将 json 转换为 select 语句中的文本?
    • @ionescho 我相信你现在已经找到了,但是::text 应该这样做。
    • 通过union 语句从同一错误中找到此问题。默认情况下可能会尝试合并 destinations。如果您不需要一个独特的联合,更改为 union all 就可以了。
    【解决方案2】:

    对不起,我迟到了这个答案,但它可能对其他人有所帮助。

    据我了解,您只能在 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"
    

    我只从您的SELECT 子句中删除了DISTINCT 并添加了GROUP BY

    通过仅引用GROUP BY 中的id,您可以利用该新功能,因为所有剩余的profiles 列在“功能上依赖于”该id 主键。

    不知何故,这避免了 Postgres 需要对依赖列进行相等性检查(即在这种情况下是您的 json 列)。

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

    【讨论】:

      【解决方案3】:

      如果你使用 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 成功地在 JSONB 列上暗示 DISTINCT 虽然它在 JSON 列上失败!

      还可以尝试以下操作以查看 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", "a":"1"}' 被插入为 '{"a":"1", "b":"2"}' 因此 PG 将其识别为同一记录:

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

      【讨论】:

        【解决方案4】:

        是的,不幸的是 postgres json 没有实现平等,但 jsonb 做到了。所以将json 列迁移到jsonb 应该可以正常工作。

        【讨论】:

          猜你喜欢
          • 2016-11-12
          • 1970-01-01
          • 1970-01-01
          • 2016-08-05
          • 2011-06-28
          • 2016-11-06
          • 1970-01-01
          • 2013-01-18
          • 1970-01-01
          相关资源
          最近更新 更多