27

I have the following data in a matches table:

5;{"Id":1,"Teams":[{"Name":"TeamA","Players":[{"Name":"AAA"},{"Name":"BBB"}]},{"Name":"TeamB","Players":[{"Name":"CCC"},{"Name":"DDD"}]}],"TeamRank":[1,2]}
6;{"Id":2,"Teams":[{"Name":"TeamA","Players":[{"Name":"CCC"},{"Name":"BBB"}]},{"Name":"TeamB","Players":[{"Name":"AAA"},{"Name":"DDD"}]}],"TeamRank":[1,2]}

I want to select each last distinct Team in the table by their name. i.e. I want a query that will return:

6;{"Name":"TeamA","Players":[{"Name":"CCC"},{"Name":"BBB"}
6;{"Name":"TeamB","Players":[{"Name":"AAA"},{"Name":"DDD"}

So each team from last time that team appears in the table.
I have been using the following (from here):

WITH t AS (SELECT id, json_array_elements(match->'Teams') AS team FROM matches)
SELECT MAX(id) AS max_id, team FROM t GROUP BY team->'Name';

But this returns:

ERROR: could not identify an equality operator for type json
SQL state: 42883
Character: 1680

I understand that Postgres doesn't have equality for JSON. I only need equality for the team's name (a string), the players on that team don't need to be compared.

Can anyone suggest an alternative way to do this?
For reference:

SELECT id, json_array_elements(match->'Teams') AS team FROM matches

returns:

5;"{"Name":"TeamA","Players":[{"Name":"AAA"},{"Name":"BBB"}]}"
5;"{"Name":"TeamB","Players":[{"Name":"CCC"},{"Name":"DDD"}]}"
6;"{"Name":"TeamA","Players":[{"Name":"CCC"},{"Name":"BBB"}]}"
6;"{"Name":"TeamB","Players":[{"Name":"AAA"},{"Name":"DDD"}]}"

EDIT: I cast to text and following this question, I used DISTINCT ON instead of GROUP BY. Here's my full query:

WITH t AS (SELECT id, json_array_elements(match->'Teams') AS team
           FROM matches ORDER BY id DESC)
SELECT DISTINCT ON (team->>'Name') id, team FROM t;

Returns what I wanted above. Does anyone have a better solution?

2
  • 1
    Try casting team->'Name' to text Commented May 29, 2015 at 2:05
  • It should be obvious to provide your version of Postgres. Important for the best answer. Table definition would also be useful. Commented May 29, 2015 at 3:41

1 Answer 1

18

Shorter, faster and more elegant with a LATERAL join:

SELECT DISTINCT ON (t.team->>'Name') t.team
FROM   matches m, json_array_elements(m.match->'Teams') t(team);
ORDER  BY t.team->>'Name', m.id DESC;  -- to get the "last"

If you just want distinct teams, the ORDER BY can go. Related:

JSON and equality

There is no equality operator for the json data type in Postgres, but there is one for jsonb (Postgres 9.4+):

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

3 Comments

Just need to change (t.team->'Name') to (t.team->>'Name'), json has no equality.
@Watson: Right, fixed.
Since jsonb has an equality operator in 9.4+, you can just cast the json column to jsonb in some cases, using my_json_column::jsonb

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.