简体   繁体   中英

Reuse result from subquery when creating a view

Is it possible to reuse the result from a subquery in a subsequent subquery when creating a view in postgresql?

For example I have the following two tables:

CREATE TABLE application
(
    id INT PRIMARY KEY,
    name CHARACTER VARYING(255)
);
CREATE TABLE application_user
(
    id INT PRIMARY KEY,
    application_id INT REFERENCES application (id) ON DELETE CASCADE,
    active BOOLEAN
);

-- some sample data
INSERT INTO application (id, name) VALUES 
    (10, 'application1'), 
    (20, 'application2'), 
    (30, 'application3');
INSERT INTO application_user (id, application_id, active) VALUES 
    (1, 10, true),
    (2, 10, false),
    (3, 20, false),
    (4, 20, false),
    (5, 20, false);

The view that I need looks (right now) as follows:

CREATE VIEW application_stats AS
SELECT a.name,
    (SELECT COUNT(1) FROM application_user u 
        WHERE a.id = u.application_id) AS users,
    (SELECT COUNT(1) FROM application_user u 
        WHERE a.id = u.application_id AND u.active = true) AS active_users
    FROM application a;

This does give me the correct result:

name             users    active_users
application1     2        1
application2     3        0
application3     0        0

However it is also pretty inefficient since I'm using two times almost the same query and ideally I would like to reuse the result from the first query. Is there an efficient way to do this?

This would normally be expressed as a join / group by :

SELECT a.name, COUNT(au.application_id) as users,
       SUM( (au.active = true)::int) as active_users
FROM application a LEFT JOIN
     application_user au
     ON a.name = au.application_id
GROUP BY a.name;

I'm rather surprised that application doesn't have a serial primary key. But because you are using name , perhaps the join is not needed at all:

SELECT au.application_id, COUNT(*) as users,
       SUM( (au.active = true)::int) as active_users
FROM application_user au
GROUP BY au.application_id;

This will return applications that have at least one server.

You should join the two tables, group by application_id and use count with a FILTER (WHERE ...) clause to count only the rows you want:

CREATE VIEW application_stats AS
SELECT a.name
       count(*) AS users,
       count(*) FILTER (WHERE u.active) AS active_users
FROM application a
   LEFT JOIN application_user u ON a.id = u.application_id
GROUP BY a.id;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM