Модераторы: skyboy

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> запрос с суммированием по нескольким полям 
V
    Опции темы
Akella
  Дата 22.5.2012, 13:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Творец
****


Профиль
Группа: Модератор
Сообщений: 18485
Регистрация: 14.5.2003
Где: Корусант

Репутация: нет
Всего: 329



Я не знаю, как можно было бы правильно озаглавить тему, мозгов не хватило.
Есть таблица объявлений.
Основное, что нам понадобится здесь - это 6 полей (boolean)  на каждый день с понедельника по субботу, т.е. на какой день нужно публиковать объявление.

для наглядности (весь DDL наверное ненужно представлять здесь?):

user posted image


Вот запрос, который подсчитывает количество объявлений в таблице для конкретного пользователя, а вернее сумму денег, которую он должен внести в кассу:
Код

SELECT sum(t1.sum1)
FROM
  (SELECT v.id
        , (v.price * count(v.id)) AS sum1
   FROM
     vid v
   LEFT JOIN ads a
   ON (v.id = a.vid)
   WHERE
     id_user_ekr = :id_user_ekr
     AND a.deleted <=> 0
     AND a.max_date >= :max_date
     AND a.gdn = 0
   GROUP BY
     1) AS t1

В таблице VID хранятся стили и цены объявлений
Таблицы VID и ADS связаны по полю ADS.vid (ADS.vid = VID.id)


но здесь не учитываются дни недели:
user posted image

Получается, что одно объявление может быть опубликовано 6 раз, если пользователь отметил в таблице все дни недели. Ну или 2 раза, если отмечены только Вт и Чт, даже если в таблице 1 запись (1-но объявление)

Выполнять 6 раз запрос отдельно или c UNION для каждого дня недели?
Может есть другой ит правильный путь?
PM MAIL   Вверх
Zloxa
Дата 22.5.2012, 15:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Чо?
****


Профиль
Группа: Завсегдатай
Сообщений: 3473
Регистрация: 12.9.2008

Репутация: 53
Всего: 161



Мог не правильно понять, но мне кажется что вместо:
Цитата(Akella @  22.5.2012,  14:45 Найти цитируемый пост)
(v.price * count(v.id)) AS sum1

достаточно написать
Код

(v.price * sum(v.pn+v.vt+v.sr...))


Соответственно если 0 = нет, 1 - да. Если не так, то привести к нулю и единице.


--------------------
Достоверно известно, что 89% людей доверяют статистике взятой с потолка smile
PM   Вверх
Akella
Дата 22.5.2012, 15:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Творец
****


Профиль
Группа: Модератор
Сообщений: 18485
Регистрация: 14.5.2003
Где: Корусант

Репутация: нет
Всего: 329



ок, проверим

Добавлено через 2 минуты и 50 секунд
да, ты неправильно понял
в таблице VID нет дней недели, в таблице VID живет цена объявления

Таблица VID

user posted image

Добавлено через 13 минут и 31 секунду
создал такой запрос

Код

SELECT sum(t1.sum1)
FROM
  (SELECT v.id
        , (v.price * sum(a.pn + a.vt + a.sr + a.ch + a.pt + a.sb)) AS sum1
   FROM
     vid v
   LEFT JOIN ads a
   ON (v.id = a.vid AND a.deleted <=> 0 AND a.max_date >= :max_date AND a.gdn = 0)
   WHERE
     id_user_ekr = :id_user_ekr
     
     
     
     
   GROUP BY
     1) AS t1


посмотрим, что получится smile
нужно всё теперь ручками перепроверить, но идея богатая  smile  smile 
PM MAIL   Вверх
Zloxa
Дата 22.5.2012, 15:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Чо?
****


Профиль
Группа: Завсегдатай
Сообщений: 3473
Регистрация: 12.9.2008

Репутация: 53
Всего: 161



Цитата(Akella @  22.5.2012,  16:18 Найти цитируемый пост)
в таблице VID нет дней недели

Тогда в твоем первом запросе не правильно использовать v.id в count. Он не вернет 0, если ads не сможет подтянуться, получишь еденички там, где должен был быть 0  smile 


--------------------
Достоверно известно, что 89% людей доверяют статистике взятой с потолка smile
PM   Вверх
Akella
Дата 22.5.2012, 15:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Творец
****


Профиль
Группа: Модератор
Сообщений: 18485
Регистрация: 14.5.2003
Где: Корусант

Репутация: нет
Всего: 329



Работет! (С)

Добавлено через 48 секунд
Zloxa,  smile 
PM MAIL   Вверх
Zloxa
Дата 23.5.2012, 09:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Чо?
****


Профиль
Группа: Завсегдатай
Сообщений: 3473
Регистрация: 12.9.2008

Репутация: 53
Всего: 161



Цитата(Zloxa @  22.5.2012,  16:37 Найти цитируемый пост)
 Он не вернет 0, если ads не сможет подтянуться, получишь еденички там, где должен был быть 0    

Наврал я.
Предикаты
Цитата(Akella @  22.5.2012,  14:45 Найти цитируемый пост)
AND a.deleted <=> 0
     AND a.max_date >= :max_date
     AND a.gdn = 0

Выродят left join в inner


--------------------
Достоверно известно, что 89% людей доверяют статистике взятой с потолка smile
PM   Вверх
Akella
Дата 23.5.2012, 09:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Творец
****


Профиль
Группа: Модератор
Сообщений: 18485
Регистрация: 14.5.2003
Где: Корусант

Репутация: нет
Всего: 329



не совсем понял ход твоих мыслей
PM MAIL   Вверх
Zloxa
Дата 23.5.2012, 11:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Чо?
****


Профиль
Группа: Завсегдатай
Сообщений: 3473
Регистрация: 12.9.2008

Репутация: 53
Всего: 161



Цитата(Akella @  23.5.2012,  10:05 Найти цитируемый пост)
не совсем понял хот твоих мыслей 

Смысл в том, что если ты в секции where фильтруешь по четким критериям вроде равенства, неравенства, без оговорок на null'абельность подсоединяемого внешним соединением набора данных, то outer join вырождается в inner..

т.е.
запросы
Код

select *
from a
left join b on a.val = b.val
where b.somve_val = 'hello'

и
Код

select *
from a
inner join b on a.val = b.val
where b.somve_val = 'hello'

Эквивалентны, потому что критерий b.somve_val = 'hello' отфильтрует те данные из таблицы а, которым не нашлось сопоставления в таблице b.
Чтобы этого не происходило и внешнее соединиение не вырождалось во внутреннее, надо либо переносить критерии отбора в критерии объединения, либо допускать не определенность в предикатах фильтра
т.е. либо
Код

select *
from a
left join b on a.val = b.val and b.somve_val = 'hello'

либо
[code=sql]
Код

select *
from a
left join b on a.val = b.val 
where (b.somve_val = 'hello' or b.val is null) -- здесь проверять именно b.val на null, ибо он в критерии объединения


Добавлено через 4 минуты и 35 секунд
Я это все к тому, что, при этих условиях, твой count(v.id) никогда не вернет нуль, и по этой причине его замена на count(a.vid), что я находил ранее более правильным, ничего не даст.

Добавлено через 5 минут и 15 секунд
Но использование left join в подобных случаях, это обфускация в чистом виде  smile 


--------------------
Достоверно известно, что 89% людей доверяют статистике взятой с потолка smile
PM   Вверх
Akella
Дата 23.5.2012, 12:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Творец
****


Профиль
Группа: Модератор
Сообщений: 18485
Регистрация: 14.5.2003
Где: Корусант

Репутация: нет
Всего: 329



Цитата(Akella @  22.5.2012,  15:18 Найти цитируемый пост)
ON (v.id = a.vid AND a.deleted <=> 0 AND a.max_date >= :max_date AND a.gdn = 0)
   WHERE
     id_user_ekr = :id_user_ekr


ну я же так и сделал smile
PM MAIL   Вверх
Akella
Дата 23.5.2012, 14:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Творец
****


Профиль
Группа: Модератор
Сообщений: 18485
Регистрация: 14.5.2003
Где: Корусант

Репутация: нет
Всего: 329



Усложняем запрос.
Оказалось, что нужно вычислять сумму отдельно платных и бесплатных объявлений, опираясь на поле paid.

Как-то так
Код
SELECT sum(t1.sum_free)
     , sum(t1.sum_paid)
FROM
  (SELECT v.id
        , CASE a.paid
          WHEN 0 THEN
            (v.price * sum(a.pn + a.vt + a.sr + a.ch + a.pt + a.sb))
          END AS sum_free
        , CASE a.paid
          WHEN 1 THEN
            (v.price * sum(a.pn + a.vt + a.sr + a.ch + a.pt + a.sb))
          END AS sum_paid
   FROM
     vid v
   LEFT JOIN ads a
   ON (v.id = a.vid AND a.deleted <=> 0 AND a.max_date >= :max_date AND a.gdn <=> 0)
   WHERE
     id_user_ekr = :id_user_ekr


   GROUP BY
     1) AS t1


PM MAIL   Вверх
Akella
  Дата 30.5.2012, 16:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Творец
****


Профиль
Группа: Модератор
Сообщений: 18485
Регистрация: 14.5.2003
Где: Корусант

Репутация: нет
Всего: 329



Не могу понять, почему этот запрос возвращает 10 и 1, хотя по идее должен вернуть 7 и 4
Код

SELECT sum(t1.sum_free) AS sum_free
     , sum(t1.sum_paid) AS sum_paid
FROM
  (SELECT v.id
        , CASE a.paid
          WHEN 0 THEN
            (/*v.price **/ sum(a.pn + a.vt + a.sr + a.ch + a.pt + a.sb))
          END AS sum_free
        , CASE a.paid
          WHEN 1 THEN
            (/*v.price **/ sum(a.pn + a.vt + a.sr + a.ch + a.pt + a.sb))
          END AS sum_paid
   FROM
     vid v
   LEFT JOIN ads a
   ON (v.id = a.vid )
   WHERE
     id_user_ekr = :id_user_ekr
    AND a.deleted <=> 0 AND a.max_date >= :max_date AND a.gdn = 0
    


   GROUP BY
     1) AS t1


user posted image


а если "выкусить" из него часть запроса

Код

SELECT sum(a.pn + a.vt + a.sr + a.ch + a.pt + a.sb) as sum_free
       
FROM
  vid v
LEFT JOIN ads a
ON (v.id = a.vid)
WHERE
  id_user_ekr = :id_user_ekr
  AND a.deleted <=> 0
  AND a.max_date >= :max_date
  AND a.gdn = 0
  AND a.paid = 0


то получается уже не 10, а 7

user posted image


я понимаю, что всё дело в поле paid, т.к. именно на одну запись разница (где a.vt = 1  a.ch =1 a.sb = 1, получается 3)
PM MAIL   Вверх
Akella
Дата 31.5.2012, 10:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Творец
****


Профиль
Группа: Модератор
Сообщений: 18485
Регистрация: 14.5.2003
Где: Корусант

Репутация: нет
Всего: 329



Цитата(Zloxa @  23.5.2012,  11:12 Найти цитируемый пост)
Эквивалентны, потому что критерий b.somve_val = 'hello' отфильтрует те данные из таблицы а, которым не нашлось сопоставления в таблице b.
Чтобы этого не происходило и внешнее соединиение не вырождалось во внутреннее, надо либо переносить критерии отбора в критерии объединения, либо допускать не определенность в предикатах фильтра


дело в том, что в указанных в where полях, отсутствуют записи с NULL-значениями
PM MAIL   Вверх
Akella
Дата 31.5.2012, 11:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Творец
****


Профиль
Группа: Модератор
Сообщений: 18485
Регистрация: 14.5.2003
Где: Корусант

Репутация: нет
Всего: 329



на другом форуме подсказали IF

Код

SELECT v.id,
     (v.price * SUM(IF(a.paid = 0, a.pn + a.vt + a.sr + a.ch + a.pt + a.sb, 0))) as sum_free,
     (v.price * SUM(IF(a.paid = 1, a.pn + a.vt + a.sr + a.ch + a.pt + a.sb, 0))) as sum_paid
FROM
  vid v
LEFT JOIN ads a
ON (v.id = a.vid AND a.deleted = 0 AND a.gdn = 0 AND id_user_ekr = :id_user_ekr AND a.max_date >= :max_date)
GROUP BY
  1

PM MAIL   Вверх
Zloxa
Дата 31.5.2012, 11:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Чо?
****


Профиль
Группа: Завсегдатай
Сообщений: 3473
Регистрация: 12.9.2008

Репутация: 53
Всего: 161



Я понял в чем подвох. Дело не в case/if

Цитата(Akella @  23.5.2012,  15:16 Найти цитируемый пост)
       , CASE a.paid
          WHEN 0 THEN
            (v.price * sum(a.pn + a.vt + a.sr + a.ch + a.pt + a.sb))
          END

здесь в результате a.paid примет ЛЮБОЕ значение из тех, которые присутствуют в группе. Так же, как и v.price. Но, в отличии от v.price, который для группы будет всегда одинаков, т.к. группировка происходит по v.id, потому этот "любой" всегда будет одинаков, a.paid в пределах группы может принимать различные значения, и "любой", в зависимости от времени года и сочетания звезд на небе, может принимать непредсказеумые значения. Все нормальные движки запрещают подобную мешанину аггрегируемых и не аггрегируемых данных. MySQL - исключение.

Правильное решение - перенести case внутрь sum. Как это сделано в примере с if.

Это сообщение отредактировал(а) Zloxa - 31.5.2012, 11:51


--------------------
Достоверно известно, что 89% людей доверяют статистике взятой с потолка smile
PM   Вверх
Akella
Дата 31.5.2012, 13:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Творец
****


Профиль
Группа: Модератор
Сообщений: 18485
Регистрация: 14.5.2003
Где: Корусант

Репутация: нет
Всего: 329



Код

SELECT v.id
     , v.price * sum(
       CASE a.paid
       WHEN 0 THEN
         a.pn + a.vt + a.sr + a.ch + a.pt + a.sb
       ELSE
         0
       END) AS sum_free
     , v.price * sum(
       CASE a.paid
       WHEN 1 THEN
         a.pn + a.vt + a.sr + a.ch + a.pt + a.sb
       ELSE
         0
       END) AS sum_paid
FROM
  vid v
LEFT JOIN ads a
ON v.id = a.vid AND a.deleted = 0 AND a.max_date >= :max_date AND a.gdn = 0
WHERE
  id_user_ekr = :id_user_ekr
GROUP BY
  v.id


Добавлено через 58 секунд
Цитата(Zloxa @  31.5.2012,  11:47 Найти цитируемый пост)
Правильное решение - перенести case внутрь sum. Как это сделано в примере с if.


да пытался я, только запутался со скобками, в итоге забросил case

Добавлено через 5 минут и 26 секунд
А теперь мне надо ещё осложнить запрос  smile 
проблема заключается вот в чем:
для тех записей, у которых a.pn = 1 нужно взять не v.price, а v.price_d  smile 
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Составление SQL-запросов | Следующая тема »


 




[ Время генерации скрипта: 0.1098 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.