it-swarm.com.de

Wie mache ich eine Postgresql-Unterabfrage in einer select-Klausel mit einer join-in-from-Klausel wie SQL Server?

Ich versuche, die folgende Abfrage in postgresql zu schreiben:

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

Dies würde sicherlich auf Microsoft SQL Server funktionieren, aber auf postegresql überhaupt nicht. Ich habe die Dokumentation ein wenig gelesen und es scheint, als könnte ich sie umschreiben als:

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

Dies gibt jedoch den folgenden Fehler in postegresql zurück: "Unterabfrage in FROM kann nicht auf andere Relationen derselben Abfrageebene verweisen". Also stecke ich fest. Weiß jemand, wie ich das erreichen kann?

Vielen Dank

69
Ricardo

Ich bin mir nicht sicher, ob ich Ihre Absicht genau verstehe, aber vielleicht entspricht Folgendes Ihren Vorstellungen:

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)

Leider kommt die Anforderung hinzu, die erste Unterabfrage nach id sowie name und author_id zu gruppieren, was meines Erachtens nicht erwünscht war. Ich bin mir jedoch nicht sicher, wie ich das umgehen soll, da Sie eine ID benötigen, um an der zweiten Unterabfrage teilnehmen zu können. Vielleicht findet jemand anderes eine bessere Lösung.

Teile und genieße.

99
Bob Jarvis

Ich antworte hier nur mit der formatierten Version der endgültigen SQL, die ich basierend auf der Antwort von Bob Jarvis brauchte, wie in meinem Kommentar oben angegeben:

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)
12
Ricardo

Wenn Sie @ Bob Jarvis und @ dmikam ergänzen, führen Postgres keinen guten Plan durch, wenn Sie LATERAL nicht verwenden. In beiden Fällen werden die Abfragedaten unterhalb einer Simulation angezeigt sind gleich, aber die Kosten sind sehr unterschiedlich

Tabellenstruktur

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);

Ausführen von JOIN mit GROUP BY In Unterabfragen ohne 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);

Die Ergebnisse

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 verwenden

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);

Ergebnisse

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)

Meine Postgres-Version ist PostgreSQL 10.3 (Debian 10.3-1.pgdg90+1)

7
deFreitas

Ich weiß, dass dies alt ist, aber seit Postgresql 9. gibt es die Option, ein Schlüsselwort "LATERAL" zu verwenden, um RELATED-Unterabfragen innerhalb von JOINS zu verwenden, sodass die Abfrage aus der Frage wie folgt aussehen würde:

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
6
dmikam
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

verwendet distinct, wenn mehr innerer Join vorhanden ist, da die Leistung der Join-Gruppe langsam ist

0
Zahid Gani