web-dev-qa-db-ja.com

結合されたテーブルの1行のみをpostgresで結合するにはどうすればよいですか?

私は次のスキーマを持っています:

CREATE TABLE author (
    id   integer
  , name varchar(255)
);
CREATE TABLE book (
    id        integer
  , author_id integer
  , title     varchar(255)
  , rating    integer
);

そして、私は各著者に最後の本が欲しいです:

SELECT book.id, author.id, author.name, book.title as last_book
FROM author
JOIN book book ON book.author_id = author.id

GROUP BY author.id
ORDER BY book.id ASC

どうやらそれをmysqlで行うことができます: MySQLの2つのテーブルに参加し、2番目のテーブルから1行だけを返します

しかし、postgresはこのエラーを出します:

エラー:列 "book.id"はGROUP BY句に表示するか、集計関数で使用する必要があります:SELECT book.id、author.id、author.name、book.title as last_book FROM author JOIN book book ON book.author_id = author.id GROUP BY author.id ORDER BY book.id ASC

理由

GROUP BYが存在する場合、グループ化されていない列に対して複数の値が返される可能性があるため、集計関数内を除き、SELECTリスト式がグループ化されていない列を参照することは無効です。

Postgresに指定するにはどうすればよいですか:「joined_table.id、結合されたテーブル内? "


編集:このデータで:

INSERT INTO author (id, name) VALUES
  (1, 'Bob')
, (2, 'David')
, (3, 'John');

INSERT INTO book (id, author_id, title, rating) VALUES
  (1, 1, '1st book from bob', 5)
, (2, 1, '2nd book from bob', 6)
, (3, 1, '3rd book from bob', 7)
, (4, 2, '1st book from David', 6)
, (5, 2, '2nd book from David', 6);

表示されるはずです:

book_id author_id name    last_book
3       1         "Bob"   "3rd book from bob"
5       2         "David" "2nd book from David"
31
select distinct on (author.id)
    book.id, author.id, author.name, book.title as last_book
from
    author
    inner join
    book on book.author_id = author.id
order by author.id, book.id desc

チェック distinct on

SELECT DISTINCT ON(expression [、...])は、指定された式が等しいと評価される各行セットの最初の行のみを保持します。 DISTINCT ON式は、ORDER BYと同じルールを使用して解釈されます(上記を参照)。 ORDER BYを使用して目的の行が最初に表示されるようにしない限り、各セットの「最初の行」は予測できないことに注意してください。

Distinctをオンにすると、order byに「distinct」列を含める必要があります。それが望む順序でない場合は、クエリをラップして並べ替える必要があります

select 
    *
from (
    select distinct on (author.id)
        book.id, author.id, author.name, book.title as last_book
    from
        author
        inner join
        book on book.author_id = author.id
    order by author.id, book.id desc
) authors_with_first_book
order by authors_with_first_book.name

別の解決策は、レナートの答えのようにウィンドウ関数を使用することです。そしてもう一つの非常に一般的なものはこれです

select 
    book.id, author.id, author.name, book.title as last_book
from
    book
    inner join
    (
        select author.id as author_id, max(book.id) as book_id
        from
            author
            inner join
            book on author.id = book.author_id
        group by author.id
    ) s
    on s.book_id = book.id
    inner join
    author on book.author_id = author.id
43
Clodoaldo Neto

これは古風で非常に単純に見えるかもしれませんが、ウィンドウ関数、CTE、および集約サブクエリに依存しません。ほとんどの場合、最速でもあります。

SELECT bk.id, au.id, au.name, bk.title as last_book
FROM author au
JOIN book bk ON bk.author_id = au.id
WHERE NOT EXISTS (
    SELECT *
    FROM book nx
    WHERE nx.author_id = bk.author_id
    AND nx.book_id > bk.book_id
    )
ORDER BY book.id ASC
    ;
6
wildplasser

1つの方法を次に示します。

SELECT book_id, author_id, author_name, last_book
FROM (
    SELECT b.id as book_id
         , a.id as author_id
         , a.name as author_name
         , b.title as last_book
         , row_number() over (partition by a.id
                              order by b.id desc) as rn
    FROM author a
    JOIN book b 
        ON b.author_id = a.id
) last_books
WHERE rn = 1;
3
Lennart

@wildplasserの提案のわずかなバリエーションとして、実装全体で機能するため、存在しないよりもmaxを使用できます。これは、長いwhere句よりも短い結合を好む場合に読みやすくなります。

select * 
  from author au
  join (
    select max(id) as max_id, author_id
      from book bk
     group by author_id) as lb 
    on lb.author_id = au.id
  join bk 
    on bk.id = lb.max_id;

または、サブクエリに名前を付けて物事を明確にするには、WITHを使用します

with last_book as 
   (select max(id) as max_id, author_id
      from book bk
     group by author_id)

select * 
  from author au
  join last_book lb
    on au.id = lb.author_id
  join bk 
    on bk.id = lb.max_id;
0
jobermark
create temp table book_1 as (
SELECT
id
,title
,author_id
,row_number() OVER (PARTITION BY id) as rownum 
FROM
book)  distributed by ( id );

select author.id,b.id, author.id, author.name, b.title as last_book
from
    author

    left  join
   (select * from  book_1 where rownum = 1 ) b on b.author_id = author.id
order by author.id, b.id desc
0
Bobburi Madhu