46

I have the following query:

SELECT 
  distinct(date(survey_results.created_at)), 
  json_build_object(
    'high', 
    ROUND( 
      COUNT(*) FILTER (WHERE ( scores#>>'{medic,categories,motivation}' in('high', 'medium'))) OVER(order by date(survey_results.created_at) ) * 1.0 / 
      (
        CASE (COUNT(*) FILTER (WHERE (scores#>>'{medic,categories,motivation}' in('high','medium','low'))) OVER(order by date(survey_results.created_at))) 
        WHEN 0.0 THEN 1.0 
        ELSE (COUNT(*) FILTER (WHERE (scores#>>'{medic,categories,motivation}' in('high','medium','low'))) OVER(order by date(survey_results.created_at))) 
        END)* 100, 2 ) ) AS childcare FROM survey_results GROUP BY date, scores ORDER BY date asc; 

The problem is with using distinct(date(survey_results.created_at)). With that in place query returns error:

could not identify an equality operator for type json

Here is db fiddle that show that problem. How can I fix that?

2
  • 3
    distinct is NOT a function. It always applies to all columns of the result. distinct(a),b is the same as distinct a, (b) or distinct a, b. And because of that, distinct tries to compare identical values of your second column which is of type json and can't be compared with = Commented Jan 24, 2018 at 10:43
  • Got it, this query returns a duplicated dates, how can I fix it without using distinct? Commented Jan 24, 2018 at 10:45

5 Answers 5

79

Use jsonb_build_object. Notice the b for binary after json.

Sign up to request clarification or add additional context in comments.

5 Comments

to_jsonb helped me. The important thing is the fact that the jsonb type does have an equality operator.
I was trying to make a UNION with JSON and was getting this message. I switched from JSON to JSONB and it worked fine. Thanks.
I discovered a similar error trying to union json_build_array() with another json_build_array() and finding it wouldn't work, then I found a similar solution switching to jsonb functions or casting to jsonb. I'm curious why there was no operator.
I understand now after running this query. It's questionable what it means to be equal for json. SELECT LENGTH('{"a":1,"a":2}'::json::text), LENGTH('{"a":1,"a":2}'::jsonb::text), LENGTH('{ "a":1}'::json::text), LENGTH('{ "a":1}'::jsonb::text) A similar comparison is shown here: stackoverflow.com/a/33731703/5078765
There is also a similar function for json_agg called jsonb_agg
23

The problem is with using distinct(date(survey_results.created_at))

No. The problem is with using DISTINCT in that it is not a function. It always applies to all columns of the result. distinct(a), b is the same as distinct a, (b) or distinct a, b. And because of that, distinct tries to compare identical values of your second column which is of type json and can't be compared with =

If you just want the "latest" value, you can do this with Postgres' distinct on () operator:

SELECT distinct on (date(survey_results.created_at)) 
       date(survey_results.created_at) as date,
       json_build_object('high', 
        ROUND( 
      COUNT(*) FILTER (WHERE ( scores#>>'{medic,categories,motivation}' in('high', 'medium'))) OVER(order by date(survey_results.created_at) ) * 1.0 / 
      (
        CASE (COUNT(*) FILTER (WHERE (scores#>>'{medic,categories,motivation}' in('high','medium','low'))) OVER(order by date(survey_results.created_at))) 
        WHEN 0.0 THEN 1.0 
        ELSE (COUNT(*) FILTER (WHERE (scores#>>'{medic,categories,motivation}' in('high','medium','low'))) OVER(order by date(survey_results.created_at))) 
        END)* 100, 2 ) ) AS childcare 
FROM survey_results 
GROUP BY date, scores 
ORDER BY date asc; 

The distinct on () combined with order by picks the first row for subsequent identical values of the column(s) specified in the ON () part. In this case it would return the earliest date. If you want the "latest" row, change the sort order to desc

https://www.db-fiddle.com/f/vUBjUyKDUNLWzySHKCKcXA/1

1 Comment

Can we not do group by date(suryve_results.created_at)? @a_horse_with_no_name
9

Migrate to using JSONB and you won't have this issue.

This is the standard piece of advice followed a few years ago when Postgres 9.4 was coming out. Here is a thread in the Ruby on Rails community that describes migrating to JSONB as the solution.

Here is the thread: https://github.com/rails/rails/issues/17706

Comments

2

Facing a similar issue while working on the below SQL, we could able to fix it by explicitly casting JSON extracted column to text. as below

select 
b.booking_id as booking_id, 
(p.product_meta_data::json->'confirmation_window')::text as confirmation_window, 
bi.product_id as product_id 
from bookings b 
left join booking_items bi on b.booking_id = bi.booking_id 
left join products p on p.product_id = bi.product_id

Comments

0

To use distinct you need to change column from json to jsonb.

Described in article : https://github.com/filamentphp/filament/issues/8698

"the solution is to switch to a jsonb column. The distinct() part of the query does not come from the Filament codebase (as far as I can see), so there is not anything we can change about it."

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.