Ich versuche die folgende Abfrage auf 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 unter Microsoft SQL Server funktionieren, aber unter postegresql überhaupt nicht. Ich habe die Dokumentation ein wenig gelesen und es scheint, als könnte ich sie wie folgt umschreiben:
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 Beziehungen derselben Abfrageebene verweisen". Also stecke ich fest. Weiß jemand, wie ich das erreichen kann?
Vielen Dank
sql
sql-server
postgresql
subquery
Ricardo
quelle
quelle
Antworten:
Ich bin mir nicht sicher, ob ich Ihre Absicht perfekt verstehe, aber vielleicht entspricht Folgendes Ihren Wünschen:
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 wird dadurch die Anforderung hinzugefügt, die erste Unterabfrage nach ID sowie nach Name und author_id zu gruppieren, was meiner Meinung nach nicht erwünscht war. Ich bin mir jedoch nicht sicher, wie ich das umgehen soll, da Sie eine ID zur Verfügung haben müssen, um an der zweiten Unterabfrage teilnehmen zu können. Vielleicht findet jemand anderes eine bessere Lösung.
Teile und genieße.
quelle
Als Ergänzung zu der Antwort von @Bob Jarvis und @dmikam führt Postgres keinen guten Plan durch, wenn Sie LATERAL unterhalb einer Simulation nicht verwenden. In beiden Fällen sind die Ergebnisse der Abfragedaten 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);
Durchführen
JOIN
mitGROUP BY
in Unterabfrage ohneLATERAL
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)
Verwenden von
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);
Ergebnisse
Meine Postgres-Version ist
PostgreSQL 10.3 (Debian 10.3-1.pgdg90+1)
quelle
Ich antworte hier nur mit der formatierten Version des endgültigen SQL, die ich brauchte, basierend auf der Antwort von Bob Jarvis, 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)
quelle
Ich weiß, dass dies alt ist, aber seit Postgresql 9.3 gibt es eine Option, ein Schlüsselwort "LATERAL" zu verwenden, um RELATED-Unterabfragen innerhalb von JOINS zu verwenden, sodass die Abfrage aus der Frage folgendermaßen 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
quelle
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
Wird verwendet, wenn mehr innere Verknüpfungen vorhanden sind, da die Leistung der Verknüpfungsgruppen langsamer istquelle