32

Próba zrobienia wyraźnego w trybie z szynami.Wyraźny w Postgresql Kolumna danych 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 > 

otrzymuję błąd PG::UndefinedFunction: ERROR: could not identify an equality operator for type json

Konwersja do Hstore nie jest opcja dla mnie w tej sprawie. Wszelkie prace wokół?

Odpowiedz

42

Powodem tego jest to, że w PostgreSQL (do 9,3) nie operator równości zdefiniowane dla json (tj val1::json = val2::json zawsze wyrzucić ten wyjątek) - w 9,4 będzie jeden do rodzaju jsonb.

Jednym z rozwiązań jest to, że możesz odrzucić swoje pole json na text. Ale to nie obejmie wszystkich równości jsona. np. {"a":1,"b":2} powinien być równy {"b":2,"a":1}, ale nie będzie równy, jeśli zostanie przeniesiony do text.

Innym obejście jest (jeśli masz klucz podstawowy dla tej tabeli - co powinno być) można użyć DISTINCT ON (<expressions>) form:

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

Uwaga: jeden znany zastrzeżenie dla DISTINCT ON:

Wyrażenia DISTINCT ON muszą pasować do wyrażenia ORDER BY po lewej stronie. Klauzula ORDER BY zwykle zawiera dodatkowe wyrażenia, które określają pożądany priorytet wierszy w każdej grupie DISTINCT ON.

+0

I rzeczywiście skończyło się robi DISTINCT ON profiles.id. To było najlepsze rozwiązanie dla mnie –

+0

jak rzucić json jako tekst w instrukcji select? – ionescho

+1

@ionescho Na pewno znalazłeś to już teraz, ale ':: text' powinien to zrobić. – fzzfzzfzz

4

Przykro mi, że spóźniłem się na tę odpowiedź, ale może pomóc innym.

Jak rozumiem zapytanie, dostajesz tylko możliwe duplikaty na profiles ze względu na wiele-do-wielu dołączyć do integrations (które używasz do określenia, które profiles na dostęp).

W związku z tym, można użyć nowego GROUP BY funkcję as of 9.1:

Kiedy GROUP BY jest obecny, to nie jest ważne na liście Wybierz wyrażenia odnoszą się do niezgrupowanych kolumn z wyjątkiem wewnątrz funkcji agregujących lub jeżeli Rozgrupowana kolumna jest funkcjonalnie zależna od zgrupowanych kolumn, ponieważ w przeciwnym razie istnieje więcej niż jedna możliwa wartość zwracana dla niezgrupowanej kolumny. Funkcjonalna zależność istnieje, jeśli pogrupowane kolumny (lub ich podzestaw) są kluczem podstawowym tabeli zawierającej rozgrupowaną kolumnę.

Więc w twoim przypadku, można uzyskać Ruby stworzyć kwerendę (przepraszam, nie wiem Ruby składni używasz) ...

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" 

ja tylko usunięte DISTINCT z klauzuli SELECT i dodano GROUP BY.

Odwołując się TYLKO do id w GROUP BY, można skorzystać z tej nowej funkcji, ponieważ wszystkie pozostałe kolumny profiles są "funkcjonalnie zależne" od tego identyfikatora klucza podstawowego.

Jakoś tak wspaniale, że unika się konieczności, aby Postgres przeprowadzał kontrole równości na kolumnach zależnych (tj. W tym przypadku kolumnę json).

Rozwiązanie jest również świetne i wyraźnie wystarczające w twoim przypadku, ale nie można używać z nim funkcji agregujących, takich jak array_agg. MOŻNA z tym podejściem GROUP BY. Szczęśliwe dni! :)

1

Jeśli używasz PG 9,4, stosując JSONB zamiast JSON rozwiązuje ten problem Przykład:

-- 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"} 

Jak widać PG udało się sugerować DISTINCT na kolumnie JSONB póki to się nie powiedzie na JSON kolumna!

Spróbuj również zobaczyć, że rzeczywiście klucze w JSONB są klasyfikowane:

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"} 

uwagę, że '{ "b": "2", "a": "1"}' została wprowadzona jako '{ "a": "1", "b": "2"}' nich PG identyfikuje że tego samego rekordu:

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