Как сделать подзапрос Postgresql в предложении select с предложением join in from, например SQL Server?
Я пытаюсь написать следующий запрос на 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
это, безусловно, будет работать на Microsoft SQL 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 не может ссылаться на другие отношения того же уровня запроса". Так что я застрял. Кто-нибудь знает как я могу достичь этого?
спасибо
4 ответа:
Я не уверен, что я прекрасно понимаю ваши намерения, но, возможно, следующее будет близко к тому, что вы хотите:
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)
к сожалению, это добавляет требование группировки первого подзапроса по идентификатору, а также имени и author_id, который я не думаю, что был нужен. Я не уверен, как обойти это, хотя, поскольку вам нужно иметь идентификатор, доступный для присоединения ко второму подзапросу. Возможно, кто-то другой придумает лучшее решение.
поделиться и наслаждаться.
Я просто отвечаю здесь с форматированной версией окончательного 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)
дополнение @Bob Jarvis и @dmikam ответ, Postgres не выполняют хороший план, когда вы не используете боковой, ниже моделирования, в обоих случаях результаты запроса данных одинаковы, но стоимость очень разные
структура таблицы
CREATE TABLE ITEMS ( N INTEGER NOT NULL, S TEXT NOT NULL ); CREATE INDEX N_INDEX ON ITEMS(N); INSERT INTO ITEMS SELECT (random()*1000000)::integer AS n, md5(random()::text) AS s FROM generate_series(1,10000000);
выполняя
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)
Я знаю, что это старый, но так как Postgresql 9.3 существует возможность использовать ключевое слово "LATERAL" для использования связанных подзапросов внутри соединений, поэтому запрос из вопроса будет выглядеть так:
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