Процент к итогу в PostgreSQL без подзапроса - PullRequest
4 голосов
/ 27 июня 2011

У меня есть таблица с пользователями. У каждого пользователя есть страна. Я хочу получить список всех стран с количеством пользователей и процентом / итогом. То, что у меня пока есть:

SELECT
country_id,
COUNT(*) AS total,
((COUNT(*) * 100) / (SELECT COUNT(*) FROM users WHERE cond1 = true AND cond2 = true AND cond3 = true)::decimal) AS percent
FROM users
WHERE cond1 = true AND cond2 = true AND cond3 = true
GROUP BY contry_id

Условия в обоих запросах одинаковы. Я пытался сделать это без подзапроса, но тогда я не могу получить общее количество пользователей, но общее количество по стране. Есть ли способ сделать это без подзапроса? Я использую PostgreSQL. Любая помощь высоко ценится. Заранее спасибо

Ответы [ 3 ]

8 голосов
/ 27 июня 2011

Я думаю, что причина, по которой вы хотите исключить подзапрос, состоит в том, чтобы избегать сканирования таблицы пользователей дважды.Помните, что общая сумма представляет собой сумму подсчетов для каждой страны.

WITH c AS (SELECT country_id, count(*) AS cnt FROM users WHERE cond1=... GROUP BY country_id) 
SELECT *, 100.0*cnt/(SELECT sum(cnt) FROM c) AS percent FROM c;

Этот запрос создает небольшой CTE со статистикой по стране.Он будет сканировать таблицу пользователей только один раз и сгенерирует небольшой набор результатов (только одна строка на страну).

Общая сумма (SELECT sum (cnt) FROM c) рассчитывается только один раз для этого небольшого набора результатов,поэтому он использует незначительное время.

Вы также можете использовать оконную функцию:

SELECT country_id, cnt, 100.0*cnt/(sum(cnt) OVER ()) AS percent 
FROM (SELECT country_id, count(*) as cnt from users group by country_id) foo;

(что совпадает с запросом ночного волка с удаленными ошибками lol)

Оба запроса занимают примерно одно и то же время.

2 голосов
/ 27 июня 2011

Я не пользователь PostgreSQL, но общим решением было бы использование оконных функций.

Читайте о том, как использовать это в http://developer.postgresql.org/pgdocs/postgres/tutorial-window.html

Лучшее объяснение, которое я мог бы использовать, чтобы описать это: в основном это позволяет вам группировать по одному полю без предложения group by.

Я полагаю, что это может сработать:

SELECT 
    country_id, 
    COUNT(*) OVER (country_id) 
    ((COUNT(*) OVER (country_id)) * 100) / COUNT(*) OVER () )::decimal) as percent
FROM 
    users
WHERE
    cond1 = true AND cond2 = true AND cond3 = true
1 голос
/ 01 августа 2018

Это действительно старый, но оба из приведенных выше примеров либо не работают, либо слишком сложны.

SELECT
    country_id,
    COUNT(*),
    (COUNT(*) / (SUM(COUNT(*)) OVER() )) * 100
FROM
    users
WHERE
    cond1 = true AND cond2 = true AND cond3 = true
GROUP BY 
    country_id

Второе число не обязательно, оно просто для отладки, чтобы убедиться, что вы 'получаю правильные результаты.Хитрость в том, что СУММА поверх СЧЕТА над набором записей.

Надеюсь, это кому-нибудь поможет.

Также, если кто-то захочет сделать это в Django, просто взломайте совокупность:

class PercentageOverRecordCount(Aggregate):
    function = 'OVER'
    template = '(COUNT(*) / (SUM(COUNT(*)) OVER() )) * 100'

    def __init__(self, expression, **extra):
        super().__init__(
            expression,
            output_field=DecimalField(),
            **extra
        )

Теперь его можно использовать в аннотации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...