32

Cercando di distinguere in una modalità con binari.Distinct on Postgresql Colonna dati 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 > 

Ottenere un errore PG::UndefinedFunction: ERROR: could not identify an equality operator for type json

Conversione in hstore non è un'opzione per me in questo caso. Qualsiasi lavoro in giro?

risposta

42

La ragione di questo, è che in PostgreSQL (fino a 9.3) non v'è alcun operatore di uguaglianza definito per json (vale a dire val1::json = val2::json sarà sempre buttare questa eccezione) - in 9.4 ci sarà uno per il tipo jsonb.

Una soluzione è che è possibile trasmettere il campo json a text. Ma questo non coprirà tutte le uguaglianze json. f.ex. {"a":1,"b":2} dovrebbe essere uguale a {"b":2,"a":1}, ma non sarà uguale se assegnato a text.

Un'altra soluzione è (se si dispone di una chiave primaria per quel tavolo - che dovrebbe essere) è possibile utilizzare il DISTINCT ON (<expressions>) form:

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

Nota: Una nota avvertimento per DISTINCT ON:

L'espressione (i) DISTINCT ON deve corrispondere all'espressione/i ORDER BY più a sinistra. La clausola ORDER BY conterrà normalmente espressioni (s) aggiuntive che determinano la precedenza desiderata delle righe all'interno di ciascun gruppo DISTINCT ON.

+0

realtà ho finito per fare il DISTINCT ON profiles.id. Questa è stata la soluzione migliore per me –

+0

come lanci il tuo json come testo in un'istruzione select? – ionescho

+1

@ionescho Sono sicuro che lo hai già trovato, ma ':: text' dovrebbe farlo. – fzzfzzfzz

4

Scusa se sono in ritardo su questa risposta, ma potrebbe aiutare gli altri.

quanto ho capito la tua ricerca, che stai ricevendo solo possibili duplicati su profiles a causa dei molti-a-molti unirsi a integrations (che si sta utilizzando per determinare quale profiles per accedere).

A causa di ciò, è possibile utilizzare una nuova GROUP BY caratteristica as of 9.1:

Quando GROUP BY è presente, non è valida per la lista espressioni SELECT per fare riferimento alle colonne non raggruppati se non entro le funzioni di aggregazione o se la colonna non raggruppata dipende funzionalmente dalle colonne raggruppate, poiché altrimenti ci sarebbe più di un possibile valore da restituire per una colonna non raggruppata. Esiste una dipendenza funzionale se le colonne raggruppate (o un loro sottoinsieme) sono la chiave primaria della tabella che contiene la colonna non raggruppata.

Quindi nel tuo caso, si potrebbe ottenere Ruby per creare la query (mi dispiace, non so Ruby sulla sintassi che si sta utilizzando) ...

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" 

ho rimosso solo il DISTINCT dalla clausola SELECT e aggiunto GROUP BY.

Riferendosi ESCLUSIVAMENTE allo, è possibile sfruttare questa nuova funzione poiché tutte le altre colonne profiles dipendono "funzionalmente" da tale chiave primaria.

In qualche modo, meravigliosamente questo evita che Postgres esegua controlli di uguaglianza sulle colonne dipendenti (es. La colonna json in questo caso).

La soluzione DISTINCT ON è anche ottima e chiaramente sufficiente nel tuo caso, ma non è possibile utilizzare funzioni di aggregazione come array_agg con esso. È possibile con questo approccio GROUP BY. Giorni felici! :)

1

Se si utilizza PG 9.4, utilizzando JSONB piuttosto che JSON risolve questo problema Esempio:

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

Come si può vedere PG riuscito implicare DISTINCT su una colonna JSONB mentre non riesce in un JSON colonna!

Prova anche il seguente per vedere che le chiavi in ​​realtà nel JSONB sono allineati:

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

nota che '{ "b": "2", "A": "1"}' stato inserito come '{ "a": "1", "b": "2"}' Perciò PG identifica che, come lo stesso record:

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