Estoy tratando de escribir la siguiente consulta en 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
Esto sin duda funcionaría en Microsoft SQL Server, pero no en postegresql. Leí un poco su documentación y parece que podría reescribirlo como:
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
Pero eso devuelve el siguiente error en postegresql: "la subconsulta en FROM no puede referirse a otras relaciones del mismo nivel de consulta". Entonces estoy estancado. ¿Alguien sabe cómo puedo lograr eso?
Gracias
                    
                        sql
                                sql-server
                                postgresql
                                subquery
                                
                    
                    
                        Ricardo
fuente
                
                fuente

Respuestas:
No estoy seguro de entender perfectamente tu intención, pero quizás lo siguiente se acerque a lo que quieres:
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)Desafortunadamente, esto agrega el requisito de agrupar la primera subconsulta por id, así como por nombre y author_id, que no creo que se quisiera. Sin embargo, no estoy seguro de cómo solucionarlo, ya que necesita tener una identificación disponible para unirse a la segunda subconsulta. Quizás a alguien más se le ocurra una solución mejor.
Comparte y Disfruta.
fuente
Como complemento de @Bob Jarvis y @dmikam responden, Postgres no realiza un buen plan cuando no usa LATERAL, debajo de una simulación, en ambos casos los resultados de los datos de consulta son los mismos, pero el costo es muy diferente
Estructura de la mesa
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);Realización
JOINconGROUP BYen subconsulta sinLATERALEXPLAIN 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);Los resultados
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)Utilizando
LATERALEXPLAIN 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);Resultados
Mi versión de Postgres es
PostgreSQL 10.3 (Debian 10.3-1.pgdg90+1)fuente
Solo estoy respondiendo aquí con la versión formateada del sql final que necesitaba según la respuesta de Bob Jarvis como se publicó en mi comentario anterior:
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)fuente
Sé que esto es antiguo, pero desde Postgresql 9.3 hay una opción para usar una palabra clave "LATERAL" para usar subconsultas RELACIONADAS dentro de JOINS, por lo que la consulta de la pregunta se vería así:
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_idfuente
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 truese usa
distinctsi hay más unión interna, porque el rendimiento de un grupo de unión más es lentofuente