2010-05-14 1 views
1

У меня есть таблица с именем Проекты, которые имеют следующие соотношения:Какой самый эффективный запрос?

имеет много взносов имеют много платежей

В моем наборе результатов, мне нужно следующие агрегированных значения:

  • Количество уникального (Донордид в таблице взносов)
  • Всего внесенных взносов (СУММ таблицы «Сумма на вклад»)
  • Всего уплаченных (СУММЫ Paym enAmount on Payment)

Поскольку существует так много совокупных функций и множественных объединений, он становится беспорядочным, поэтому используйте стандартные агрегатные функции в предложении GROUP BY. Мне также нужна возможность сортировки и фильтрации этих полей. Так что я придумал два варианта:

Использование подзапросов:

SELECT Project.ID AS PROJECT_ID, 
(SELECT SUM(PaymentAmount) FROM Payment WHERE ProjectID = PROJECT_ID) AS TotalPaidBack, 
(SELECT COUNT(DISTINCT DonorID) FROM Contribution WHERE RecipientID = PROJECT_ID) AS ContributorCount, 
(SELECT SUM(Amount) FROM Contribution WHERE RecipientID = PROJECT_ID) AS TotalReceived 
FROM Project; 

Использование временной таблицы:

DROP TABLE IF EXISTS Project_Temp; 
CREATE TEMPORARY TABLE Project_Temp (project_id INT NOT NULL, total_payments INT, total_donors INT, total_received INT, PRIMARY KEY(project_id)) ENGINE=MEMORY; 
INSERT INTO Project_Temp (project_id,total_payments) 
SELECT `Project`.ID, IFNULL(SUM(PaymentAmount),0) FROM `Project` LEFT JOIN `Payment` ON ProjectID = `Project`.ID GROUP BY 1; 
INSERT INTO Project_Temp (project_id,total_donors,total_received) 
SELECT `Project`.ID, IFNULL(COUNT(DISTINCT DonorID),0), IFNULL(SUM(Amount),0) FROM `Project` LEFT JOIN `Contribution` ON RecipientID = `Project`.ID GROUP BY 1 
ON DUPLICATE KEY UPDATE total_donors = VALUES(total_donors), total_received = VALUES(total_received); 

SELECT * FROM Project_Temp; 

Тесты для обоих довольно сопоставимы, в размере 0,7 - 0,8 секунды в диапазоне с 1000 рядами. Но я действительно обеспокоен масштабируемостью, и я не хочу, чтобы меня перестраивали все по мере роста моих таблиц. Какой лучший подход?

ответ

2

Знание сроков для каждой строки 1K является хорошим, но реальный вопрос заключается в том, как они будут использоваться.

Планируете ли вы отправить все эти данные в пользовательский интерфейс? Google уменьшает результаты на 25 страниц; может быть, вам тоже.

Планируете ли вы выполнять вычисления в среднем ярусе? Возможно, вы можете выполнить эти вычисления в базе данных и сэкономить на том, чтобы все эти байты были перенесены через провод.

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

Вы можете ОБЪЯВИТЬ ПЛАН, чтобы узнать, какая разница между этими двумя запросами.

+0

Благодарим за отзыв. К сожалению, одно из требований заключается в том, что все эти данные отправляются на карту, содержащую мир всех проектов, поэтому разбиение на страницы недостаточно. Предполагая, что это не было требованием, на странице должно быть 16 проектов. Считаете ли вы, что не стоит делать все это и вместо этого запускать 3 или 4 простых запроса для каждого результата? Это 64 запроса на страницу, но если они простые, может быть, это тривиально? –

1

Я бы пошел с первым подходом. Вы позволяете РСУБД выполнять свою работу, а не пытаться сделать это для нее.

Создав временную таблицу, вы всегда создадите полную таблицу для каждого запроса. Если вам нужны только данные для одного проекта, вы все равно создадите полную таблицу (если вы не ограничиваете каждый оператор INSERT соответствующим образом.) Конечно, вы можете его закодировать, но он уже становится довольно объемным кодом и сложностью для небольшого прироста производительности.

С помощью SELECT, db может получить соответствующее количество данных, оптимизируя весь запрос на основе контекста. Если другие пользователи запросили одни и те же данные, их можно даже кэшировать (запрос и, возможно, данные, в зависимости от вашего db). Если производительность действительно вызывает беспокойство, вы можете рассмотреть использование индексированных/материализованных представлений или создание таблицы в триггере INSERT/UPDATE/DELETE. Масштабирование, вы можете использовать кластеры серверов и partioned views - то, что, я считаю, будет сложно, если вы создаете временные таблицы.

EDIT: вышеупомянутое написано без каких-либо конкретных rdbms в виду, хотя OP добавил, что mysql является целевым db.

+0

Мне нравится предложение использования просмотров. Я никогда не создавал его раньше, но похоже, что это идеальное приложение. Интересно, что временные таблицы кажутся избыточными, делая то, что mysql будет делать в любом случае. –

+0

Хорошо, это mysql - вы можете добавить это на свой вопрос, а также версию, которую используете. – mdma

+0

Mysql не поддерживает индексированные представления - вы можете создать представление, но это больше похоже на простой способ скрытия деталей, поэтому текст запроса живет в вашей базе данных, а не в вашем коде. Представления также обеспечивают способ разбивки сложных запросов на более мелкие фрагменты. Они могут скрыть много деталей, что обычно является преимуществом, но также может привести к простому отображению скрытых сложных запросов, если их не удалось тщательно контролировать. – mdma

1

Существует третий вариант, который является производным таблицы:

Select Project.ID AS PROJECT_ID 
    , Payments.Total AS TotalPaidBack 
    , Coalesce(ContributionStats.DonarCount, 0) As ContributorCount 
    , ContributionStats.Total As TotalReceived 
From Project 
    Left Join (
       Select C1.RecipientId, Sum(C1.Amount) As Total, Count(Distinct C1.DonarId) ContributorCount 
       From Contribution As C1 
       Group By C1.RecipientId 
       ) As ContributionStats 
     On ContributionStats.RecipientId = Project.Project_Id 
    Left Join (
       Select P1.ProjectID, Sum(P1.PaymentAmount) As Total 
       From Payment As P1 
       Group By P1.RecipientId 
       ) As Payments 
     On Payments.ProjectId = Project.Project_Id 

Я не уверен, если он будет работать лучше, но вы могли бы дать ему расстреливали.

1

Несколько мыслей:

  • Производная таблица идея была бы хорошо и на других платформах, но MySQL имеет один и тот же вопрос с производными таблицами, что она делает с видом: они не индексируются. Это означает, что MySQL будет выполнять полное содержимое производной таблицы перед применением предложения WHERE, которое вообще не масштабируется.

  • Вариант 1 хорош для компактности, но синтаксис может оказаться сложным, если вы хотите начать создавать производные выражения в предложении WHERE.

  • Предложение материализованных представлений является хорошим, но, к сожалению, MySQL их не поддерживает. Мне нравится идея использования триггеров. Вы можете перевести эту временную таблицу в реальную таблицу, которая сохраняется, а затем использовать триггеры INSERT/UPDATE/DELETE в таблицах «Платежи и взносы» для обновления таблицы «Статистика проекта».

  • Наконец, если вы не хотите использовать триггеры, и если вы не слишком озабочены свежестью, вы всегда можете иметь отдельную таблицу статистики и обновлять ее в автономном режиме, имея задание cron, которое выполняется каждые несколько минут, который выполняет работу, указанную в Query # 2 выше, за исключением реальной таблицы. В зависимости от нюансов вашего приложения эта небольшая задержка в обновлении статистики может или не может быть приемлемой для ваших пользователей.

 Смежные вопросы

  • Нет связанных вопросов^_^