web-dev-qa-db-ja.com

Postgresql JSONデータ列で異なる

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への変換はオプションではありません。回避策はありますか?

38

この理由は、PostgreSQL(9.3まで)でjsonに定義された等値演算子がない(つまり、_val1::json = val2::json_は常にこの例外をスローする)ためです。9.4では、 jsonbタイプ。

回避策の1つは、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グループ内の行の望ましい優先順位を決定する追加の式が含まれます。

54
pozs

申し訳ありませんが、この回答には遅れていますが、他の人の助けになるかもしれません。

クエリを理解すると、profilesへの多対多結合(アクセスするintegrationsを決定するために使用しているため)がprofilesで重複する可能性があります。

そのため、新しいGROUP BY機能を使用できます 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アプローチでできます。幸せな日々! :)

6
poshest

PG 9.4を使用する場合、JSONではなくJSONBを使用すると、この問題が解決されます例:

-- 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"}'として挿入されたことに注意してください。記録:

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

残念ながら、postgres jsonは等式を実装していませんが、jsonbは実装しています。したがって、json列をjsonbに移行すると、問題なく動作するはずです。

0
bright