J'essaie d'écrire la requête suivante sur 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
Cela fonctionnerait certainement sur Microsoft SQL Server, mais pas du tout sur postegresql. J'ai lu un peu sa documentation et il semble que je pourrais la réécrire comme suit:
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
Mais cela renvoie l'erreur suivante sur postegresql: "la sous-requête dans FROM ne peut pas faire référence à d'autres relations du même niveau de requête". Donc je suis coincé. Est-ce que quelqu'un sait comment je peux y arriver?
Merci
Je ne suis pas sûr de comprendre parfaitement votre intention, mais peut-être que ce qui suit serait proche de ce que vous voulez:
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)
Malheureusement, ceci ajoute la nécessité de regrouper la première sous-requête par id, ainsi que name et author_id, ce que je ne pensais pas être voulu. Je ne sais pas trop comment contourner ce problème, car vous devez disposer d'un identifiant pour pouvoir participer à la deuxième sous-requête. Peut-être que quelqu'un d'autre trouvera une meilleure solution.
Partager et profiter.
Je réponds simplement ici avec la version formatée de la version finale dont j'avais besoin, basée sur la réponse de Bob Jarvis, telle que publiée dans mon commentaire ci-dessus:
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)
Complémentant @ Bob Jarvis et @ dmikam answer, Postgres n'effectue pas un bon plan lorsque vous n'utilisez pas LATERAL, sous une simulation, dans les deux cas, les résultats des données de la requête sont les mêmes, mais les coûts sont très différents
Structure de la table
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);
Exécution de JOIN
avec GROUP BY
Dans une sous-requête sans 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);
Les resultats
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)
Utiliser 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);
Résultats
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)
Ma version de Postgres est PostgreSQL 10.3 (Debian 10.3-1.pgdg90+1)
Je sais que cela est ancien, mais puisque Postgresql 9. , il existe une option permettant d’utiliser un mot clé "LATERAL" pour utiliser les sous-requêtes RELATED dans JOINS. La requête de la question se présentait ainsi:
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
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
utilisé distinct
si plus de jointure interne, parce que plus la performance du groupe de jointure est lente