SQLServerのようなjoinin from句を使用してselect句でPostgresqlサブクエリを実行するにはどうすればよいですか?


83

postgresqlで次のクエリを書き込もうとしています。

select name, author_id, count(1), 
    (select count(1)
    from names as n2
    where n2.id = n1.id
        and t2.author_id = t1.author_id
    )               
from names as n1
group by name, author_id

これは確かにMicrosoftSQL Serverで機能しますが、postegresqlではまったく機能しません。そのドキュメントを少し読んだところ、次のように書き直すことができたようです。

select name, author_id, count(1), total                     
from names as n1, (select count(1) as total
    from names as n2
    where n2.id = n1.id
        and n2.author_id = t1.author_id
    ) as total
group by name, author_id

ただし、postegresqlで次のエラーが返されます:「FROMのサブクエリは同じクエリレベルの他のリレーションを参照できません」。だから私は立ち往生しています。誰かが私がそれを達成する方法を知っていますか?

ありがとう


実際、これはPostgresで機能するはずです(おそらく6年前は機能しませんでした:))
qwertzguy 2016

回答:


124

私はあなたの意図を完全に理解しているかどうかはわかりませんが、おそらく次のことがあなたが望むものに近いでしょう:

select n1.name, n1.author_id, count_1, total_count
  from (select id, name, author_id, count(1) as count_1
          from names
          group by id, name, author_id) n1
inner join (select id, author_id, count(1) as total_count
              from names
              group by id, author_id) n2
  on (n2.id = n1.id and n2.author_id = n1.author_id)

残念ながら、これにより、最初のサブクエリをid、name、author_idでグループ化するという要件が追加されますが、これは望ましくないと思います。ただし、2番目のサブクエリに参加するにはIDを使用できるようにする必要があるため、これを回避する方法がわかりません。おそらく他の誰かがより良い解決策を思い付くでしょう。

共有してお楽しみください。


完璧なボブ、それは本当にうまくいった。どうもありがとう!idとの結合は必要なく、author_idだけが必要なので、少し変更する必要がありました。したがって、最終的なクエリは次のようになります。selectn1.name、n1.author_id、count_1、total_count from(select id、name、author_id、count(1)as count_1 from names group by id、name、author_id)n1 inner join(select author_id、 count(1)as total_count from names group by author_id)n2 on(n2.author_id = n1.author_id)これで、count_1をtotal_countで除算して、正規化された頻度にすることができます。= D
リカルド

ここでSQLが正しくフォーマットされていないことに気づきました。:(は補完するために答えを与える。
リカルド

リカドが話していた問題はありませんでしたが、このSQLは私の問題を完全に修正しました...:Dありがとう!!!
tftd 2011年

16

@Bob Jarvis@dmikamの回答を補完するものとして、シミュレーションの下でLATERALを使用しない場合、Postgresは適切な計画を実行しません。どちらの場合も、クエリデータの結果は同じですが、コストは大きく異なります。

テーブル構造

CREATE TABLE ITEMS (
    N INTEGER NOT NULL,
    S TEXT NOT NULL
);

INSERT INTO ITEMS
  SELECT
    (random()*1000000)::integer AS n,
    md5(random()::text) AS s
  FROM
    generate_series(1,1000000);

CREATE INDEX N_INDEX ON ITEMS(N);

実行JOINしてGROUP BYサブクエリにすることなく、LATERAL

EXPLAIN 
SELECT 
    I.*
FROM ITEMS I
INNER JOIN (
    SELECT 
        COUNT(1), n
    FROM ITEMS
    GROUP BY N
) I2 ON I2.N = I.N
WHERE I.N IN (243477, 997947);

結果

Merge Join  (cost=0.87..637500.40 rows=23 width=37)
  Merge Cond: (i.n = items.n)
  ->  Index Scan using n_index on items i  (cost=0.43..101.28 rows=23 width=37)
        Index Cond: (n = ANY ('{243477,997947}'::integer[]))
  ->  GroupAggregate  (cost=0.43..626631.11 rows=861418 width=12)
        Group Key: items.n
        ->  Index Only Scan using n_index on items  (cost=0.43..593016.93 rows=10000000 width=4)

使用する LATERAL

EXPLAIN 
SELECT 
    I.*
FROM ITEMS I
INNER JOIN LATERAL (
    SELECT 
        COUNT(1), n
    FROM ITEMS
    WHERE N = I.N
    GROUP BY N
) I2 ON 1=1 --I2.N = I.N
WHERE I.N IN (243477, 997947);

結果

Nested Loop  (cost=9.49..1319.97 rows=276 width=37)
  ->  Bitmap Heap Scan on items i  (cost=9.06..100.20 rows=23 width=37)
        Recheck Cond: (n = ANY ('{243477,997947}'::integer[]))
        ->  Bitmap Index Scan on n_index  (cost=0.00..9.05 rows=23 width=0)
              Index Cond: (n = ANY ('{243477,997947}'::integer[]))
  ->  GroupAggregate  (cost=0.43..52.79 rows=12 width=12)
        Group Key: items.n
        ->  Index Only Scan using n_index on items  (cost=0.43..52.64 rows=12 width=4)
              Index Cond: (n = i.n)

私のPostgresバージョンは PostgreSQL 10.3 (Debian 10.3-1.pgdg90+1)


3
LATERALを使用するためのヒントをありがとう!
leole

14

これは古いことは知っていますが、Postgresql 9.3以降、キーワード「LATERAL」を使用してJOINS内のRELATEDサブクエリを使用するオプションがあるため、質問からのクエリは次のようになります。

SELECT 
    name, author_id, count(*), t.total
FROM
    names as n1
    INNER JOIN LATERAL (
        SELECT 
            count(*) as total
        FROM 
            names as n2
        WHERE 
            n2.id = n1.id
            AND n2.author_id = n1.author_id
    ) as t ON 1=1
GROUP BY 
    n1.name, n1.author_id

1
これらの2つのクエリのパフォーマンスが違いを持っている場合、またはPostgreSQLのそれは同じ計画であるのだろうか
deFreitas

1
私はこのテストを行いました 答えはここにあります(私の答え)
deFreitas 2018年

13

上記のコメントに投稿されているBobJarvisの回答に基づいて、必要な最終SQLのフォーマットされたバージョンでここに回答しています。

select n1.name, n1.author_id, cast(count_1 as numeric)/total_count
  from (select id, name, author_id, count(1) as count_1
          from names
          group by id, name, author_id) n1
inner join (select author_id, count(1) as total_count
              from names
              group by author_id) n2
  on (n2.author_id = n1.author_id)

2
select n1.name, n1.author_id, cast(count_1 as numeric)/total_count
  from (select id, name, author_id, count(1) as count_1
          from names
          group by id, name, author_id) n1
inner join (select distinct(author_id), count(1) as total_count
              from names) n2
  on (n2.author_id = n1.author_id)
Where true

distinctより多くの結合グループのパフォーマンスが遅いため、より多くの内部結合の場合に使用されます

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.