Отправляет email-рассылки с помощью сервиса Sendsay

Помогите с SQL-запросом.

Здравствуйте.

Помогите, кто знает!
Есть таблица A с полями a и b.
И есть таблица B с полями c и d.
Я должен сначала сделать такой запрос:

SELECT А.а, AVG(A.b) AS avgb FROM A GROUP BY A.a ORDER BT avgb DESC;

И всё бы было замечательно, если бы мне не надо было посчитать сумму
значений поля d в таблице B, где a=c. Причём записей в таблице B может
таких и не быть.

Я делаю:

SELECT А.а, AVG(A.b) AS avgb, SUM(B.d) AS sumd FROM A LEFT JOIN B ON
A.a = B.c GROUP BY A.a ORDER BT avgb DESC;

И вот в этом месте получается ерунда: вместо реальных значений суммы
получаются непомерно большие.

Есть, конечно, решение: сделать первый запрос, а потом, по каждой
строчке делать отдельные запросы на сумму. Но, думаю, это будет совсем
не рационально. Как можно правильно построить запрос, чтобы значения
были реальными?

Ответить   Andrey Yakushev Wed, 6 Dec 2006 05:12:19 +0300 (#617267)

 

Ответы:

Что-нибудь вроде:

SELECT
DISTINCT
A.a,
SUM(B.d)
FROM A
LEFT JOIN B ON (A.a=B.c)
WHERE
B.d IS NOT NULL

Покатит? Либо объясни задачу подробнее :-) Я пока не понимаю зачем
одним запросом пытаться получить среднее, сумму, и еще группировать
и сортировать. Так не делается - делают вьюхи (view) и временные
таблицы.

Ответить   Wed, 6 Dec 2006 19:40:24 +0300 (#617578)

 

Здравствуйте, dgstudio.

Вы писали 6 декабря 2006 г., 19:40:24:

Не покатит. Мне нужно получить из этих двух таблиц 3 поля. На самом
деле больше (и таблиц и полей), но я для примера взял 2 таблицы.

Первая таблица - это голосование, где есть несколько одинаковых полей
"a", и к ним разные поля "b". Из неё я получаю средний балл голосов к полю
"a". Следовательно, мне нужно, чтобы запрос просмотрел все строки "a" и
посчитал среднее значение "b". Далее, в таблице B у меня есть тоже
несколько строк "c" с разными значениями "d", которые мне надо тоже
все пройти и просуммировать.
Вот пример:

Таблица "A"
ааа 3
ббб 5
ааа 8
ббб 1

Таблица "B"
ааа 10
ааа 4
ааа 5

Результат
ааа 5,5 19
ббб 3 0

Сейчас я уже реализовал эту задачу, но сделал её так:
Я одним запросам обрабатываю первую таблицу, получаю некоторое
количество записей. А потом, к каждой записи делаю новый запрос ко
второй таблице и получаю при каждом запросе по одному значению. Думаю,
этим я очень сильно гружу sql-сервер...

Ответить   Andrey Yakushev Thu, 7 Dec 2006 02:34:12 +0300 (#617637)

 

SELECT
a,
AVG(b)
FROM a
GROUP BY a

SELECT
a,
SUM(b)
FROM b
GROUP BY a

Ответить   Thu, 7 Dec 2006 20:27:38 +0300 (#617880)

 

Здравствуйте, dgstudio.

Вы писали 7 декабря 2006 г., 20:27:38:

т.е., никак одним запросом таблицу не сделать?

Ответить   Andrey Yakushev Thu, 7 Dec 2006 21:30:44 +0300 (#617915)

 

Хм. Ты решал задачу количеством запросов, равным количеству уникальных
строк в таблице. Я предложил решение всего из двух запросов. По-моему,
стоит остановиться :-) Так как SQL-сервер, собственно, для того и
создан, чтобы выполнять запросы. Если он не справляется, то его надо
пересаживать на новое железо.

Ответить   Thu, 7 Dec 2006 22:59:59 +0300 (#618001)

 

Здравствуйте, dgstudio.

Вы писали 7 декабря 2006 г., 22:59:59:

Прошу прощения, не разобрался...

В этом случае - тоже не выход.
Ситуация такая: Первым запросом я выгребаю не всю таблицу, а только
первые 30 записей (или не первые), отсортированные по результирующему
среднему баллу. Если я сделаю запрос ко второй таблице и выдеру оттуда
все записи, то их будет около 2000. Расскажите, как мне из них выбрать
именно те 30, которые попали в первую выборку? Плюс ко всему, я думаю,
что тащить в скрипт результат в 2000 записей, не на много лучший
вариант, чем сделать 30 запросов, возвращающих одно единственное
значение.

Не помню, говорил или нет, но из первой таблицы я беру не одно поле, а
2: среднее значение и количество строк по каждому уникальному полю
"a". А дополнительных таблиц у меня 2: из одной я беру сумму значений
по ключевому полю. А из другой - количество записей с этим полем.

Ответить   Andrey Yakushev Fri, 8 Dec 2006 07:39:28 +0300 (#618099)

 

Собственно, мое ощущение, что база организована не совсем правильно,
возрастает с каждым письмом :-) К сожалению, я не вижу задачи в целом
и не могу это достоверно оценить. Но субъективно, язык SQL писался
изначально НЕ для программистов (см. историю этого языка), а для
бухгалтеров, поэтому его операторы так легко читаются. И поэтому
таблицы SQL должны говорить сами за себя. Если программист создал
набор таблиц, которые сами по себе (в совокупности) не являются
НАГЛЯДНЫМ решением задачи, то такому программисту надо выпрямлять
руки.

По вопросу нагрузки: на практике, нагрузка от "много маленьких
запросов" не эквивалентна нагрузке от "мало больших" (почему это так,
объяснят *никсовые админы), и различие может быть в обе стороны.
Поэтому, надо проверять экспериментально. Я считаю, что ни тридцать,
ни даже две тысячи запросов не положат сервак :-)

Ответить   Fri, 8 Dec 2006 20:48:53 +0300 (#618320)

 

Здравствуйте, dgstudio.

Вы писали 8 декабря 2006 г., 20:48:53:

Вот задача:
http://pritchi.ru/?top=1
Первые 2 столбца выбираются из таблицы голосования, где есть поля:
"притча", "пользователь", "голос".
Выбирается среднее значение поля "голос" по каждой "притче" и
количество "пользователей" по каждой притче.

Третий столбец - из таблицы "Хистори", где есть столбцы
"дата", "притча", "пользователь", "количество просмотров".
Выбирается сумма "количества просмотров" по каждой "притче".

Четвёртый - из таблицы постов, где среди прочих полей есть поле
"притча". Выбирается количество "айди" по каждой "притче".

Буду благодарен, если покажете, в каких местах мне надо распрямить
руки.

Ответить   Andrey Yakushev Sat, 9 Dec 2006 04:59:47 +0300 (#618434)

 

Я бы сделал так:
Сохраняем голоса в таблицу

CREATE TABLE `pritcha_votes` (
`id` bigint(20) unsigned NOT NULL auto_increment,
`pritcha_id` bigint(20) default NULL,
`user_id` bigint(20) default NULL,
`vote` tinyint(4) default NULL,
PRIMARY KEY (`id`)
) TYPE=MyISAM;

Поле user_id не нужно, если голосовать может кто угодно.
Сортируем по среднему баллу, с подсчетом числа голосов за каждую
(хотя, как показано далее, это число можно и не подсчитывать)

SELECT
pritcha_id,
COUNT(*) AS c,
AVG(vote) AS avg_vote
FROM
pritcha_votes
GROUP BY pritcha_id
ORDER BY avg_vote DESC

Получаем массив T1, ключами которого являются pritcha_id (точнее,
настоящими ключами будут, естественно, 0-1-2-3..., но нас интересуют
ключи второго уровня вложенности).

Из других таблиц аналогичным образом получаем число голосов (в массив
T2), число показов (в массив T3), число комментариев (в массив T4).
То есть, обрати внимание, всего четыре запроса для построения
рейтингов, показанных на этом сайте.

После этого, берем foreach T1 и по каждому ключу (pritcha_id)
записываем в него данные из массивов T2, T3, и T4, таким образом,
дополняя его. Потом то же действие проводим с массивом T2,
затем с массивом T3, и с массивом T4.

В результате у нас получается четыре массива, отсортированные каждый
по своему признаку (по среднему баллу, по количеству голосов, по числу
показов, и по числу комментариев). Делаем serialize каждому из них,
записываем в файлы, и пользуемся в течение ближайших 5 минут / часа /
суток. А так как подготовка этих данных будет выполняться не в коде
сайта, а по крону, то сам процесс сбора и подготовки практически никак
не будет влиять на быстродействие отдачи страниц.

Ответить   Sat, 9 Dec 2006 18:44:04 +0300 (#618694)

 

Здравствуйте, dgstudio.

Спасибо. Мысль очень интересная. Я уже подумывал о том, чтобы генерить
некоторые страницы (часто вызываемые) куда-нибудь на диск, а потом
вызывать их.
В общем, надо будет понаблюдать за сайтом, посмотреть, где будет у
него бутылочное горло, и его таким образом принудительно кэшировать.

Ответить   Andrey Yakushev Sat, 9 Dec 2006 19:04:02 +0300 (#618704)

 

В тех CMS, которые претендуют на статус "профессиональные",
обязательно присутствует кэширование страниц, а так же исполнимого
кода, и в некоторых случаях - кэширование результатов SQL-запросов.
Так что дело правильное :-)

Ответить   Sun, 10 Dec 2006 00:08:22 +0300 (#618787)