За последние 24 часа нас посетили 17833 программиста и 1719 роботов. Сейчас ищут 984 программиста ...

подсчет только выбранных столбцов

Тема в разделе "MySQL", создана пользователем Alexnewaro, 13 апр 2015.

  1. Alexnewaro

    Alexnewaro Активный пользователь

    С нами с:
    8 фев 2015
    Сообщения:
    109
    Симпатии:
    1
    Приветствую!
    Вопрос такой-
    Есть объекты аренды, цена на которые меняется несколько раз в году. Бронировать объекты можно в любое время. Цена при этом ставится из того периода, на который создается бронь. На данный момент все реализовано по месяцам.

    Человек выбирает период дат, на который ему нужен объект. Например ему нужно с 4 апреля по 2 мая.
    У каждого объекта есть строка в таблице, в котором указано 12 цен, по количеству месяцев. Подставляя значение месяца из разложенной на атомы даты, я получаю цену за этот месяц, а специальный алгоритм, вычисляя сколько в выбранном периоде дат дней в одном месяце, а сколько в другом - собирает финальную цену.

    Ну все работает как часы, аллилуйя.
    И тут - гром с неба...

    Цены теперь меняются не по месяцам, а... как угодно менеджеру.
    То есть, например, до 14 февраля одна цена, после 14го - другая.
    И наступил вселенский пук :)

    Добавлено спустя 6 минут 3 секунды:
    Часть 2:
    Извините, пишу с телефона, не туда тыкнул...

    Быстрое решение пришло такого рода - при забивке цен на объект, менеджер указывает, если месяц состоит из двух ценников. Тогда он вводит в отдельной открывшейся форме цену за каждый день такого месяца.
    И эти данные заносятся в отдельную таблицу, где у каждой строки 30 столбцов цены, по количеству дней в месяце.
    При выборке проверяется, "сложный" месяц или простой. Если простой - берется месячная цена. Если сложный - берется из новой таблице сумма столбцов по количеству дней в это месяце.
    И вот вопрос - а как подсчитать эту сумму? К примеру имея столбцы формата названия price_day01, как сложить между собой 26 таких столбцов?

    Заранее спасибо. Синтаксис SUM понимаю для суммы значений столбца в куче строк, но не суммы значений кучи столбцов у одной строки
     
  2. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.131
    Симпатии:
    1.250
    Адрес:
    там-сям
    Сделать 30 столбцов это ппц какой гибельный путь. ))) Не надо смотреть на базу SQL как на таблицу Excel! Все данные одного вида должны быть в одной колонке.

    Надо отдельно рассматривать дни? 30 дней = 30 строк в таблице дней.
    Надо получать быстро без суммирования общую сумму? Хранишь сумму одной строкой в другой таблице.
    Это не альтернативы, а взаимодополняющие варианты. Уж как тебе удобнее покажется - в одной или в двух таблицах.

    Добавлено спустя 4 минуты 39 секунд:
    Раскладывать данные по колонкам нормально в отчетах — т.н. кросс-таблицах. Но не хранить их в таком виде. Просто за*бешся работать с такой структурой, поверь.
     
  3. Alexnewaro

    Alexnewaro Активный пользователь

    С нами с:
    8 фев 2015
    Сообщения:
    109
    Симпатии:
    1
    Спасибо за советы!
    Вообще я подсчитал - как-то напрягся. 70 объектов аренды, 8 разновидной каждого, для каждого по 30 строк - сходу 16800 строк, а ведь это еще "леди даже не разделась".

    не надо злоупотреблять цитированием. ты отвечаешь на конкретную фразу — её и цитируй.
           — модераторъ
     
  4. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.131
    Симпатии:
    1.250
    Адрес:
    там-сям
    MySQL без проблем работает с миллионами записей.

    Добавлено спустя 16 минут 13 секунд:
    А вообще для проверки пересечения временных диапазонов достаточно иметь запись буквально "с - по". Незачем для каждого дня отдельную запись.

    Предположим имеется прейскурант с записями (object_id, date_from, date_to, price) и допустим у нас в скрипте есть массив id объектов, подходящих по критериям выборки, неважно какой. Просто мы что-то вычислили предварительно.
    d1, d2 - интересующий нас период,
    ids - айдишники интересующих нас объектов
    Хотим узнать все цены:

    Код (Text):
    1. SELECT *
    2. FROM prices
    3. WHERE
    4.   ( object_id IN(:ids) ) AND
    5.   ( date_from <= :d2 AND :d1 <= date_to )
    Аналогично можно организовать таблицу броней и искать какие из желаемых номеров уже заняты.

    См. про оптимальную выборку за период, подстановка массива в запрос
     
  5. Alexnewaro

    Alexnewaro Активный пользователь

    С нами с:
    8 фев 2015
    Сообщения:
    109
    Симпатии:
    1
    Изначально план был именно такой.
    период 'n' - datestart / datefinish / price.

    И уже все накатал, проверка с помощью between правда, а не операторами сравнения, может я неправ...
    Но я уперся лбищем в проблему - человек может приехать в один период и уехать в другой.
    И вот котовасия с расчетом сколько дней в каком периоде он провел...
    Я понимаю, что докумекать можно, но у меня сроки - вчера, как говорится. Потом я допилю как угодно, но шеф уже просто кожу лоскутами с меня снимает, хочу сделать костыль быстрый.

     
  6. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.131
    Симпатии:
    1.250
    Адрес:
    там-сям
    Может мы с тобой на разных языках говорим. Внесем ясность.
    Период это две даты: «дата С» и «дата ПО».
    Про дату, которая больше или равна С и меньше или равна ПО говорят что «дата попадает в период».
    Про два периода говорят что «периоды пересекаются» если хотябы один день у них общий.
    Длина пересечения периодов вычисляется так: min($end1, $end2) - max($begin1, $begin2)
    [​IMG]

    Может быть воот такое выражение на MySQL поможет тебе врубиться?
    Код (Text):
    1. SET @beg1 = DATE('2015-04-01');
    2. SET @end1 = DATE('2015-04-14');
    3. SET @beg2 = DATE('2015-03-25');
    4. SET @end2 = DATE('2015-04-05');
    5.  
    6. SELECT DATEDIFF(LEAST(@end1,@end2), GREATEST(@beg1, @beg2));
     
  7. Alexnewaro

    Alexnewaro Активный пользователь

    С нами с:
    8 фев 2015
    Сообщения:
    109
    Симпатии:
    1
    Ну вот есть номер. У него цена - периодами.
    Период 1 - с 13.04.2015 по 25.05.2015, цена 2000
    Период 2 - с 26.05.2015 по 16.09.2015, цена 3000
    и так далее.
    Пользователь задает - приеду с 23.05.2015 по 14.06.2015. Получается он попадает и на период 1 и на период 2.
    Схема ж верная?
    Или его заданные даты въезда-выезда рассматривать как условный период 3?

     
  8. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.131
    Симпатии:
    1.250
    Адрес:
    там-сям
    все верно. клиентский период пересекается с двумя периодами цен. как их найти средствами SQL я уже показал. надо просчитывать все найденные периоды.

    псевдокод для вычисления итоговой стоимости брони
    Код (PHP):
    1. $sum = 0;
    2. while ($row = mysqli_fetch_assoc($result)) {
    3.   $days = intersect_period($raw['date_from'], $raw['date_to'], $d1, $d2);
    4.   $price = $row['price'];
    5.   $sum += $price * $days;
    6. } 
     
  9. Alexnewaro

    Alexnewaro Активный пользователь

    С нами с:
    8 фев 2015
    Сообщения:
    109
    Симпатии:
    1
    Проверьте меня в теории, пожалуйста.

    То бишь я сначала снимаю информацию, не в одном ли ценовом периоде весь период юзера. Это я могу сделать так:

    Код (Text):
    1.  
    2. SELECT *
    3. WHERE дата въезда => periodstart
    4. AND дата выезда =< periodfinish
    Если num вернется один, то период один и ценник очевиден.
    Если num вернется не один, то начинаются пляски...

    У меня есть два периода, полученных из запроса.

    Код (Text):
    1.  
    2. SELECT DATEDIFF(LEAST(@дата выезда,@end1), GREATEST(@дата въезда, @beg1));
    = количество дней, заказанных юзером из первого периода.

    Код (Text):
    1.  
    2. SELECT DATEDIFF(LEAST(@дата выезда,@end2), GREATEST(@дата въезда, @beg2));
    = количество дней, заказанных юзером из второго периода.

    ТАК?


     
  10. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.131
    Симпатии:
    1.250
    Адрес:
    там-сям
    когда непонятно — смотри на рисунок и попробуй представить что это ТВОИ периоды.

    "… WHERE дата въезда… " ты написал неправильно. перечитай и попробуй понять прежде чем копировать.

    не надо условий "если num один". просто всегда считай в цикле, будь готов что строк будет сколько угодно: от одной до дофига.

    примером с datediff я показал как считать пересечение на стороне mysql. но только придется переменные заменить на реальные поля и значения, это понятно? или напиши функцию на php которая будет считать то же самое.
     
  11. Alexnewaro

    Alexnewaro Активный пользователь

    С нами с:
    8 фев 2015
    Сообщения:
    109
    Симпатии:
    1
    [​IMG]

    Разбираю пример с картинки по вашей статье.

    min(21.07, 01.07) - max(25.06, 22.06) = 01.07 - 25.06 = 7 суток
    min(21.07, 16.07) - max(25.06, 02.07) = 16.07 - 02.07 = 15 суток
    min(21.07, 01.08) - max(25.06, 17.07) = 21.07 - 17.07 = 5 суток
    Всего выходит: 27суток

    Ручками проверяю по календарю - выбранный период - 27 суток.

    Добавлено спустя 35 минут 9 секунд:
    В принципе дальнейший путь более-менее ясен, но не могу уже сообразить (вы мне сегодня порядком мозг порушили, если честно, тема жутко интересная!), как получить id всех периодов, которые затронуты выбором юзера.
    Вот такая тестовая конструкция отлично выбирает один период, но если затронуты два и более - результат пуст...

    Код (Text):
    1.  
    2. $datein = '2015-04-24';
    3. $dateout = '2015-05-18';
    4.  
    5. $connect = connectDB();
    6.             {
    7.                 $query="SELECT *
    8.                         FROM periods
    9.                         WHERE ( periodfrom <= '$datein' AND '$dateout' <= periodto )";
    10.                 $query_res=mysql_query($query);
    11.             }
    12.         closeDB ($connect);
    13.    
    14. $periods = mysql_result($query_res,0,period_id);
    15.  
    16. echo '<br><br>'.$query.'<br><br>';
    17. echo '<br><br>'.$periods.'<br><br>';
    Массив IDшников пытался и через mysql_fetch_array и через mysql_fetch_row выводить...
     
  12. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.131
    Симпатии:
    1.250
    Адрес:
    там-сям
    вот и прекрасно. если всё делать аккуратно, успех гарантирован!

    Добавлено спустя 3 минуты 12 секунд:
    вернись к моей формуле с поиском периодов и еще раз внимательно посмотри где начало, где конец.
     
  13. Alexnewaro

    Alexnewaro Активный пользователь

    С нами с:
    8 фев 2015
    Сообщения:
    109
    Симпатии:
    1
    Я перепутал точку входа и выхода.

    Верно так?

    Код (Text):
    1.  
    2. $query="SELECT *
    3.                   FROM periods
    4.                   WHERE ( periodfrom <= '$dateout' AND '$datein' <= periodto )";
    Но тогда он фигню выдает. Задаешь числа охватывающие три периода, а он выдает только один.

     
  14. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.131
    Симпатии:
    1.250
    Адрес:
    там-сям
    я этой теме отдал уже достаточно времени, дальше сам.

    если захочешь чтобы тебе помогли с SQL-запросом, не поленись создать песочницу на http://sqlfiddle.com с кучкой правдоподобных данных, помошники обязательно найдутся.
     
  15. Alexnewaro

    Alexnewaro Активный пользователь

    С нами с:
    8 фев 2015
    Сообщения:
    109
    Симпатии:
    1
    Я стормозил просто. Запрос заработал, а вывод остался одного значения...

    Да, огромное спасибо! Огромнейшее!

    Одно уточнение, если можно...

    Код (Text):
    1. $days = min($periodout, $dateout) - max($periodin, $datein);
    вычисляться не хочет никак.
    Эхом проверяю - все переменные не пустые и формата даты. Кавычки не помогают (((
     
  16. Alexnewaro

    Alexnewaro Активный пользователь

    С нами с:
    8 фев 2015
    Сообщения:
    109
    Симпатии:
    1
    Прошу помощи в этом, сил уже нет, начитываю работу с датами, понимаю, что не числовые же значения...
    Пробовал перегнать в секунды - не то.
    Подскажите, пожалуйста, как формулу запустить, уже сил от этого проекта никаких :-(
     
  17. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.131
    Симпатии:
    1.250
    Адрес:
    там-сям
    Да потому что не по уровню задачку себе нашел, прямо скажем.

    Что в твоих переменных, из которых ты days вычисляешь? "Формат даты" это текст чтоли? Так почитай про даты в PHP и как с ними работать. viewtopic.php?f=20&t=50838
     
  18. Alexnewaro

    Alexnewaro Активный пользователь

    С нами с:
    8 фев 2015
    Сообщения:
    109
    Симпатии:
    1
    Спорить не буду, у меня вообще другой профиль работы, но больше некому, пришлось изучать :)


    Добавлено спустя 15 минут 10 секунд:
    Работает, большое спасибо!
     
  19. Alexnewaro

    Alexnewaro Активный пользователь

    С нами с:
    8 фев 2015
    Сообщения:
    109
    Симпатии:
    1
    Все работает отлично, но считает сутки, а не ночи, как принято в отелях.То есть последние сутки в выбранном диапазоне считать как бы не нужно. Игры с прибавлением/убавлением 86400 секунд рушат всю систему подсчета, в сетке дней появляются бреши на стыках периодов и начинается жесть.
    На уровне формулы, можно ли отцепить один день с конца?

    Добавлено спустя 39 минут 40 секунд:
    Очень прошу помощи, завтра директорша будет мне зарплату резать за то, что я делать вообще не обязан. Что-то я уже на грани нервного срыва из-за всей этой хрени.
     
  20. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.131
    Симпатии:
    1.250
    Адрес:
    там-сям
    http://www.youtube.com/watch?v=T_CrevdjExM

    С самим периодом не должно быть проблем. Если бронируем на одну ночь 15 апреля, то это период ('2014-04-15' .. '2014-04-15') - по 15е, а не по 16е!
    Если бронируем на 17 ночей, начиная с 1 июля, то это период ('2015-07-01' .. '2015-07-17')
    Видимо надо поправить вычисление длины периода:
    Код (Text):
    1. N = дата_конца - дата_начала + 1
    Проверка формулы на PHP:
    Код (PHP):
    1. echo (strtotime('2015-07-17') - strtotime('2015-07-01')) / (24*60*60) + 1; // 17               
    Чистый SQL:
    http://sqlfiddle.com/#!9/fab4bf/3
    дает в сумме 17 ночей