Próbuję napisać następujące zapytanie na 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
To z pewnością działałoby na Microsoft SQL Server, ale w ogóle nie działa na postegresql. Przeczytałem trochę jego dokumentację i wydaje mi się, że mógłbym ją przepisać jako:
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
Ale to zwraca następujący błąd w postegresql: "podzapytanie w FROM nie może odnosić się do innych relacji na tym samym poziomie zapytania". Więc utknąłem. Czy ktoś wie, jak mogę to osiągnąć?
Dzięki
sql
sql-server
postgresql
subquery
Ricardo
źródło
źródło
Odpowiedzi:
Nie jestem pewien, czy doskonale rozumiem twój zamiar, ale być może poniższe byłyby bliskie temu, czego chcesz:
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)
Niestety dodaje to wymóg grupowania pierwszego podzapytania według identyfikatora, a także nazwy i identyfikatora autora, co nie wydaje mi się potrzebne. Nie jestem jednak pewien, jak to obejść, ponieważ musisz mieć dostępny identyfikator, aby dołączyć do drugiego podzapytania. Być może ktoś inny wymyśli lepsze rozwiązanie.
Udostępniaj i ciesz się.
źródło
Uzupełniając odpowiedź @Bob Jarvis i @dmikam , Postgres nie wykonuje dobrego planu, gdy nie używasz LATERAL, poniżej symulacji, w obu przypadkach wyniki zapytania są takie same, ale koszt jest bardzo różny
Struktura tabeli
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);
Wykonywanie
JOIN
zGROUP BY
podzapytaniem bezLATERAL
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);
Wyniki
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)
Za pomocą
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);
Wyniki
Moja wersja Postgres to
PostgreSQL 10.3 (Debian 10.3-1.pgdg90+1)
źródło
Wiem, że to jest stare, ale od Postgresql 9.3 istnieje opcja użycia słowa kluczowego "LATERAL", aby użyć PODKREWNYCH podzapytań wewnątrz JOINS, więc zapytanie z pytania wyglądałoby następująco:
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
źródło
Odpowiadam tutaj, podając sformatowaną wersję ostatecznej wersji sql, której potrzebowałem, na podstawie odpowiedzi Boba Jarvisa, zamieszczonej w moim komentarzu powyżej:
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)
źródło
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
używane,
distinct
jeśli więcej sprzężeń wewnętrznych, ponieważ większa wydajność grup sprzężeń jest wolnaźródło