0

This is a follow-up to this excellent Q&A: 13227142.

I almost have to do the same thing (with the constraint of PostgreSQL 9.2) but I'm using only one table. Therefore the query uses a self-join (in order to produce the correct JSON format) which results in a duplicate id field. How can I avoid this?

Example:

CREATE TABLE books
(
  id serial primary key,
  isbn text,
  author text,
  title text,
  edition text,
  teaser text
);

SELECT row_to_json(row)
FROM
(
  SELECT id AS bookid,
  author,
  cover
  FROM books
  INNER JOIN 
  (
    SELECT id, title, edition, teaser
    FROM books
  ) cover(id, title, edition, teaser)
  USING (id)
) row;

Result:

{
  "bookid": 1,
  "author": "Bjarne Stroustrup",
  "cover": {
    "id": 1,
    "title": "Design and Evolution of C++",
    "edition": "1st edition",
    "teaser": "This book focuses on the principles, processes and decisions made during the development of the C++ programming language"
  }
}

I want to get rid of "id" in "cover".

Vao Tsun
  • 47,234
  • 13
  • 100
  • 132
Markus L
  • 932
  • 2
  • 20
  • 38

3 Answers3

1

you need id to join, so without id you can't make such short query. You need to struct it. Smth like:

select row_to_json(row,true)
FROM
(
with a as (select id,isbn,author,row_to_json((title,edition,teaser)) r from books
)
select a.id AS bookid,a.author, concat('{"title":',r->'f1',',"edition":',r->'f2',',"teaser":',r->'f3','}')::json as cover
  from a
) row;
                      row_to_json
--------------------------------------------------------
 {"bookid":1,                                          +
  "author":"\"b\"",                                    +
  "cover":{"title":"c","edition":"d","teaser":"\"b\""}}
(1 row)

Also without join you use twice as less resources

Vao Tsun
  • 47,234
  • 13
  • 100
  • 132
1

This turned out to be a tricky task. As far as I can see it's impossible to achieve with a simple query. One solution is to use a predefined data type:

CREATE TYPE bookcovertype AS (title text, edition text, teaser text);

SELECT row_to_json(row)
FROM
(
  SELECT books.id AS bookid, books.author, 
    row_to_json(row(books.title, books.edition, books.teaser)::bookcovertype) as cover
  FROM books
) row;
pumbo
  • 3,646
  • 2
  • 25
  • 27
0

For the sake of completeness I've stumbled upon another answer myself: The additional fields can be eliminated by string functions. However, I prefer AlexM's anwer because it will be faster and is still compatible with PostgreSQL 9.2.

SELECT regexp_replace(
  (
      SELECT row_to_json(row)
      FROM
      (
        SELECT id AS bookid,
        author,
        cover
        FROM books
        INNER JOIN 
        (
          SELECT id, title, edition, teaser
          FROM books
        ) cover(id, title, edition, teaser)
        USING (id)
      ) row
  )::text,
  '"id":\d+,',
  '')
Markus L
  • 932
  • 2
  • 20
  • 38