Warning: Cannot use a scalar value as an array in /home/admin/public_html/forum/include/fm.class.php on line 757

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/include/fm.class.php on line 770

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/topic.php on line 737
Форумы портала PHP.SU :: Задачка по SQL

 PHP.SU

Программирование на PHP, MySQL и другие веб-технологии
PHP.SU Портал     На главную страницу форума Главная     Помощь Помощь     Поиск Поиск     Поиск Яндекс Поиск Яндекс     Вакансии  Пользователи Пользователи


 Страниц (1): [1]   

> Без описания
DeepVarvar Супермодератор
Отправлено: 12 Января, 2014 - 10:02:38
Post Id



Активный участник


Покинул форум
Сообщений всего: 10377
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




В каталоге есть два типа товаров: одежда и обувь.
Предусматривается фильтрация в условиях (WHERE IN/NOT IN) по брендам, размерам, категориям.
Заранее известно что в дизайне высота блока позиции с одеждой вдвое больше высоты блока позиции с обувью.
Необходимо выводить товары в последовательности: 1 одежда, 2 обуви, 1 одежда, 2 обуви и т.д..
Предположим, одежда это тип 1, а обувь это тип 2.
В классическом виде на одной странице каталога по лимиту нужно вывести
(две еденички как двойная высота блока с одеждой, а двойки - каждая как одна обувь) в три ряда сверху вниз:

1|2|1
1|2|1
2|1|2
2|1|2
1|2|1
1|2|1

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

В случае если больше не осталось одежды - подключается шаблон вывода только обуви, и наоборот.

Сам вопрос только о сортировке вида: 1 одежда, 2 обуви, 1 одежда, 2 обуви и т.д..
Остальное я описал для понимания того что вообще имеется.
 
 Top
Panoptik
Отправлено: 12 Января, 2014 - 10:31:43
Post Id



Постоянный участник


Покинул форум
Сообщений всего: 2493
Дата рег-ции: Нояб. 2011  
Откуда: Одесса, Украина


Помог: 131 раз(а)




отличная задачка вместо проснуться

насколько я помню NOT IN в мускуле не работает
ну в прочем это к делу не относится

извращениям заказчика нет предела

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

вот псевдо код
PHP:
скопировать код в буфер обмена
  1.  
  2. // for example items per page 15
  3. $clothes = (array) 'SELECT * FROM items WHERE category=1 LIMIT 15' // must be at least 5
  4. $shoes = (array) 'SELECT * FROM items WHERE category=2 LIMIT 15' // must be at least 10
  5.  
  6. $result = array();
  7. if($clothes >= 5 && $shoes >= 10) {
  8.  for($i = 0; $i < 5; $i++) {
  9.    $result[] = $clothes[$i];
  10.    $result[] = array_shift($shoes);
  11.    $result[] = array_shift($shoes);
  12.  }
  13. } else {
  14. // тут нужно подумать как делать когда не хватает определенных наименований
  15. }
  16.  


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

PHP:
скопировать код в буфер обмена
  1. $clothes = (array) 'SELECT * FROM items WHERE category=1 LIMIT 5' // must be at least 5
  2. $shoes = (array) 'SELECT * FROM items WHERE category=2 LIMIT 10' // must be at least 10


по какому критерию происходит базовая сортировка?
(Добавление)
вообще есть изящная идея но пока не знаю как реализовать

смысл в следующем

генерируем в мускуле последовательность 1,2,3,4,5,6 ... и т.д

считаем формулу в которой каждый вариант будет соответствовать порядку в последовательности своей категорией
к примеру одежда - категория 1, обувь - 2

последовательность назовем s

значит получается что мы должны джойнить одежду к последовательности как s%3 = 1 and category = 1
и обувь к последовательности как s%3 <> 1 and category = 2
и сделать order by s, но как быть с другими сортировками нужно еще подумать, может у кого-то будут более светлые мысли по этому поводу

(Отредактировано автором: 12 Января, 2014 - 10:47:03)



-----
Just do it
 
 Top
DeepVarvar Супермодератор
Отправлено: 12 Января, 2014 - 11:11:05
Post Id



Активный участник


Покинул форум
Сообщений всего: 10377
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




Цитата:
по какому критерию происходит базовая сортировка?

Первичная всегда по дате добавления - новые в начале.
Цитата:
мы должны джойнить одежду к последовательности как s%3 = 1 and category = 1
и обувь к последовательности как s%3 <> 1 and category = 2
и сделать order by s
Я так примерно до тогго-же щас допетрил, с предварительной выборкой. Но пока конечного варианта не написано, все находится только в домыслах в голове.
 
 Top
Мелкий Супермодератор
Отправлено: 12 Января, 2014 - 11:32:52
Post Id



Активный участник


Покинул форум
Сообщений всего: 11926
Дата рег-ции: Июль 2009  
Откуда: Россия, Санкт-Петербург


Помог: 618 раз(а)




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

Для mysql могу такое предложить:
CODE (SQL):
скопировать код в буфер обмена
  1. SET @sortacounter:=-1, @sortbcounter:=0, @rowbcounter:=-1;
  2. SELECT id, FIELD, IF(FIELD='a',@sortacounter:=@sortacounter+1,IF((@rowbcounter:=@rowbcounter+1)=2,@sortbcounter:=@sortbcounter+1 + (@rowbcounter:=0), @sortbcounter)) AS sort FROM `splits` ORDER BY sort, FIELD


С прочими сортировками просто, но безрадостно, надо уходить в подзапрос:
CODE (SQL):
скопировать код в буфер обмена
  1. SET @sortacounter:=-1, @sortbcounter:=0, @rowbcounter:=-1;
  2. SELECT id, FIELD, IF(FIELD='a',@sortacounter:=@sortacounter+1,IF((@rowbcounter:=@rowbcounter+1)=2,@sortbcounter:=@sortbcounter+1 + (@rowbcounter:=0), @sortbcounter)) AS sort FROM (SELECT id, FIELD FROM `splits` /**/ ORDER BY другое_поле) AS dataset ORDER BY sort, FIELD


-----
PostgreSQL DBA
 
 Top
DeepVarvar Супермодератор
Отправлено: 14 Января, 2014 - 00:29:48
Post Id



Активный участник


Покинул форум
Сообщений всего: 10377
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




Добавил спасибки обоим. Сделал некий симбиоз обоих предложенных вариантов.
Описывать долго, некогда и лень, задача решена Закатив глазки
 
 Top
DeepVarvar Супермодератор
Отправлено: 14 Января, 2014 - 04:01:01
Post Id



Активный участник


Покинул форум
Сообщений всего: 10377
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




Отпишусь, и для себя и для других.
Вдруг надо будет такое же чудо наковыривать.
Да и критика всегда к месту:

CODE (php):
скопировать код в буфер обмена
  1. <?php
  2. // получаем все товары
  3. $allProducts = dbQuery("
  4.  
  5.    SELECT DISTINCT
  6.        innerovp.id,
  7.        IF(ammpv.view = 'default', 0, 1) view
  8.    FROM innerovp
  9.    LEFT JOIN ammpv ON ammpv.idproduct = innerovp.id
  10.    WHERE innerovp.published = 1
  11.    ORDER BY innerovp.created_on DESC
  12.  
  13. ");
  14.  
  15. // разбираем кто есть одежда, а кто обувь
  16. $clothes = array();
  17. $shoes = array();
  18. foreach ($allProducts as $item) {
  19.     if ($item['view']) {
  20.         array_push($clothes, $item['id']);
  21.     } else {
  22.         array_push($shoes, $item['id']);
  23.     }
  24. }
  25.  
  26. // больше это нам не нужно
  27. unset($allProducts);
  28.  
  29. // подсчет товаров
  30. $sizeOfClothes = sizeof($clothes);
  31. $sizeOfShoes   = sizeof($shoes);
  32. $sizeOfAllProducts = $sizeOfClothes + $sizeOfShoes;
  33.  
  34. // переменные для постранички, число 18 выбрано не случайно
  35. // оно прекрасно делится на 3, а 6 позиций
  36. // максимально корректно располагаются в одном ряду
  37. $itemsPerPage = 18;
  38. $maxPageNumber = ceil($sizeOfAllProducts / $itemsPerPage);
  39. $currentPage = (int) $_GET['page'];
  40. $currentPage = $currentPage > $maxPageNumber
  41.     ? $maxPageNumber : ($currentPage < 1 ? 1 : $currentPage);
  42. $offset = ($currentPage - 1) * $itemsPerPage;
  43.  
  44. // под айдишники для выборки на страницу
  45. $productsID = array();
  46.  
  47. // пока забиваем порядок айдишников,
  48. // составим "карту" вывода товаров на странице,
  49. // в классическом виде это три колонки
  50. // с порядком следования на НЕчётных страницах:
  51. //
  52. // для первой колонки  - одежда, 2 обуви, одежда, 2 обуви
  53. // для второй колонки  - 2 обуви, одежда, 2 обуви, одежда
  54. // для третьей колонки - аналогично первой
  55. //
  56. // и порядком следования на чётных страницах:
  57. //
  58. // для первой колонки  - 2 обуви, одежда, 2 обуви, одежда
  59. // для второй колонки  - одежда, 2 обуви, одежда, 2 обуви
  60. // для третьей колонки - аналогично первой
  61. //
  62. // т.е. вывод с чередованием
  63.  
  64. $cs = $sizeOfClothes;
  65. $ss = $sizeOfShoes;
  66.  
  67. while ($cs > 0 || $ss > 0) {
  68.  
  69.     $cStep = 2;
  70.     while ($cStep > 0) {
  71.         if ($cs > 0) {
  72.             array_push($productsID, array_shift($clothes));
  73.             $cs = sizeof($clothes);
  74.         }
  75.         if ($ss > 0) {
  76.             array_push($productsID, array_shift($shoes));
  77.             $ss = sizeof($shoes);
  78.         }
  79.         if ($ss > 0) {
  80.             array_push($productsID, array_shift($shoes));
  81.             $ss = sizeof($shoes);
  82.         }
  83.         $cStep -= 1;
  84.     }
  85.  
  86.     $sStep = 2;
  87.     while ($sStep > 0) {
  88.         if ($ss > 0) {
  89.             array_push($productsID, array_shift($shoes));
  90.             $ss = sizeof($shoes);
  91.         }
  92.         if ($ss > 0) {
  93.             array_push($productsID, array_shift($shoes));
  94.             $ss = sizeof($shoes);
  95.         }
  96.         if ($cs > 0) {
  97.             array_push($productsID, array_shift($clothes));
  98.             $cs = sizeof($clothes);
  99.         }
  100.         $sStep -= 1;
  101.     }
  102.  
  103. }
  104.  
  105. // выбираем срез из массива
  106. $productsID = array_slice($productsID, $offset, $itemsPerPage);
  107.  
  108. // если что-нибудь есть
  109. $products = array();
  110. if ($productsID) {
  111.  
  112.     // получаем товары для текущей страницы
  113.     $productsID = join(',', $productsID);
  114.     $products = dbQuery("
  115.  
  116.        SELECT
  117.            ovp.id,
  118.            IF(ammpv.view = 'default', 0, 1) view,
  119.            oapc.cena,
  120.            ovpp.product_price
  121.        FROM ovp
  122.        /* тип товара */
  123.        LEFT JOIN ammpv
  124.            ON ammpv.idproduct = ovp.id
  125.        /* старый прайс, его может не быть */
  126.        LEFT JOIN oapc ON oapc.idproduct = ovp.id
  127.        /* основной прайс, или скидочный в контексте старого прайса */
  128.        INNER JOIN ovpp ON ovpp.product_id = ovp.id
  129.        WHERE ovp.id IN({$productsID})
  130.        ORDER BY FIELD(ovp.id, {$productsID})
  131.  
  132.    ");
  133.  
  134.     // добавляем к товарам их основные картинки,
  135.     // чота этот запрос слишком прожорлив,
  136.     // но только он корректно достает главные картинки,
  137.     // запросы в цикле это моветон,
  138.     // картинка с наименьшим значением сортировки и является главной,
  139.     // а наименьшее значение может быть любым, и NULL, и 0 и больше нуля
  140.     $productImages = dbQuery("
  141.  
  142.        SELECT
  143.            ovm.file_url,
  144.            ovpmj.product_id
  145.        FROM (
  146.            SELECT product_id, MIN(ordering) min_ordering
  147.            FROM iovpm
  148.            WHERE product_id IN({$productsID})
  149.            GROUP BY product_id
  150.        ) ovpm
  151.        INNER JOIN ovpmj ON ovpmj.product_id = ovpm.product_id
  152.            AND ovpmj.ordering = ovpm.min_ordering
  153.        LEFT JOIN ovm
  154.            ON ovm.media_id = ovpmj.media_id
  155.  
  156.    ");
  157.  
  158.     $pImages = array();
  159.     foreach ($productImages as $item) {
  160.         $pImages[$item['product_id']] = $item['file_url'];
  161.     }
  162.     unset($productImages);
  163.     foreach ($products as $k => $item) {
  164.         if (isset($pImages[$item['id']])) {
  165.             $products[$k]['file_url'] = $pImages[$item['id']];
  166.         }
  167.     }
  168.     unset($pImages);
  169.  
  170.     // для расчета как размещать товары на странице
  171.     // одежда по высоте в два раза больше обуви
  172.     $productPlaces = 0;
  173.     foreach ($products as $item) {
  174.         $productPlaces += ($item['view'] ? 2 : 1);
  175.     }
  176.  
  177.     // для расчета как размещать товары на странице
  178.     // сколько товаромест есть в каждой из трёх колонок
  179.     $sliceSize = ceil($productPlaces / 3);
  180.  
  181. }
  182.  
  183. // поехали выводить товары
  184. ?>
  185. <div class="maincatalog span9">
  186.  
  187.     <div class="span3">
  188.     <?php $i = 0; ?>
  189.     <?php $sliceCnt = 1; ?>
  190.     <?php foreach ($products as $k => $item) { ?>
  191.  
  192.         <?php $i += ($item['view'] ? 2 : 1); ?>
  193.  
  194.         <div>ТУТ ВЫВОД ОДНОГО ТОВАРА, НИЧЕГО ИНТЕРЕСНОГО</div>
  195.  
  196.         <?php if ($i < $productPlaces && $i >= ($sliceSize * $sliceCnt)) { ?>
  197.             <?php $sliceCnt += 1; ?>
  198.             </div><div class="span3">
  199.         <?php } ?>
  200.  
  201.     <?php } ?>
  202.     </div>
  203.  
  204. </div>
  205. <div class="clear"></div>
  206. <!-- дальше вывод постранички, у нас все расчитано выше -->
  207.  

(Добавление)
Ну и для полноты картины покажу что там было до меня.
Наслаждайтесь:
Спойлер (Отобразить)
 
 Top
tuareg
Отправлено: 14 Января, 2014 - 13:59:54
Post Id


Участник


Покинул форум
Сообщений всего: 1234
Дата рег-ции: Июнь 2010  


Помог: 69 раз(а)




Это конечно хорошо, а если товаров будет 500 000? Не жирно будет каждый дергать все товары?
 
 Top
DeepVarvar Супермодератор
Отправлено: 14 Января, 2014 - 14:15:11
Post Id



Активный участник


Покинул форум
Сообщений всего: 10377
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




tuareg пишет:
Не жирно будет каждый дергать все товары?
Хм.., а есть конкретное предложение реализации, хотябы на уровне "мне подумалось вот именно так вот будет лучше"?
Товаров там сейчас 2100, ну чутка меньше.
 
 Top
Champion Супермодератор
Отправлено: 14 Января, 2014 - 14:26:52
Post Id



Активный участник


Покинул форум
Сообщений всего: 4350
Дата рег-ции: Авг. 2008  
Откуда: Москва


Помог: 57 раз(а)




Не смотрел код, поленился. Но такое решение будет удовлетворительно работать на больших объемах:
1 - Посчитать общее количество одежды (КОд) и общее количество обуви(КОб), подходящие под условия.
2 - Исходя из этого определить, на какой странице заканчивается первый то, чего меньше.
3 - Если мы просматриваем более раннюю страницу, то два селекта с одинаковым limit и фетчить оба по очереди.
4 - Если мы на странице, на которой закончился один из товаров, то LIMIT законченный товар делается по старым правилам, а новый товар - плюс разницу, необходимую для заполнения дыр.
5 - Если мы на странице, на которой товар уже закончился, то фетчим двойное количество незаконченного товара.

Только надо учесть, что размер страницы для того товара, который больше не одинаковый на каждой странице, а сначала N, потом N + K, потом 2*N, где К - количество недостающих товаров, чтобы заткнуть дыры там, где закончился первый тип товаров, а N обычное количество товара на странице, и поэтому вычисление первой цифры в LIMIT получится чуть сложнее, чем хотелось бы
 
 Top
DeepVarvar Супермодератор
Отправлено: 14 Января, 2014 - 14:34:24
Post Id



Активный участник


Покинул форум
Сообщений всего: 10377
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




Champion пишет:
Не смотрел код
А зря, я оттуда еще и фильтры повырезал дабы не заострять на них внимания.
Там по сути два главных запроса: один выбирает все айдишники (+ тип) которые соответствуют условиям выборки, затем после распихивания на "карту" выбирается срез из массива и делается второй основной запрос, который подтягивает все нужные данные для группы в срезе (по 18 штук на страницу).
Champion пишет:
заполнения дыр
При 18 могут случиться только либо одна большая дырка (одежда), либо маленькая (обувь), и то только в момент когда то или это "внезапно" закончится. Остальные распальцовки делает счетчик товаромест - по сколько в вертикальный ряд напихать.
 
 Top
Champion Супермодератор
Отправлено: 14 Января, 2014 - 14:41:05
Post Id



Активный участник


Покинул форум
Сообщений всего: 4350
Дата рег-ции: Авг. 2008  
Откуда: Москва


Помог: 57 раз(а)




DeepVarvar пишет:
один выбирает все айдишники
Ну да, это я заметил. Но, как заметил tuareg, а если товаров будет многомного? Поэтому предложил вариант без стягивания всех айдишек.
 
 Top
DeepVarvar Супермодератор
Отправлено: 14 Января, 2014 - 14:46:05
Post Id



Активный участник


Покинул форум
Сообщений всего: 10377
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




Champion пишет:
без стягивания всех айдишек
Если учесть что подсчет всеравно бежит по выборке с условиями, то два подсчета по типам - это две полноценные транзакции с доп-условиями.
Согласен - при многомного это сыграет в плюс.
Но как тогда строить "карту"? Схема сложнее чем было предложено Мелким.
 
 Top
Champion Супермодератор
Отправлено: 14 Января, 2014 - 15:14:55
Post Id



Активный участник


Покинул форум
Сообщений всего: 4350
Дата рег-ции: Авг. 2008  
Откуда: Москва


Помог: 57 раз(а)




count можно закешировать на некотрое время, чтоб не считать при каждом переходе на страницу.
Про строить карту я не очень увидел проблему. Когда в массивах с объувью и одеждой элементов поровну, тут всё понятно. А когда в одном больше, перекидываешь часть из одного в другой и опять же делаешь по-старому.
 
 Top
tuareg
Отправлено: 14 Января, 2014 - 15:59:22
Post Id


Участник


Покинул форум
Сообщений всего: 1234
Дата рег-ции: Июнь 2010  


Помог: 69 раз(а)




DeepVarvar пишет:
Хм.., а есть конкретное предложение реализации, хотябы на уровне "мне подумалось вот именно так вот будет лучше"?
Я на работе был.
По поводу решения. С решением Мелкий надо смотреть, тут на вскидку я не скажу.
А так уже все сказано Улыбка Мое ИМХО.
Общее движение:
Сделать как предложил Panoptik, т.е 2 простых запроса. И смотрим есть ли рез-ты для Одежды и Обуви. Есть все хорошо, запомнили текущую страницу, и последние id.
При переходе на следующую, запрос будет простым where id>(запомненного) + фильтры + limit
Это я думаю достаточно простое решение пока есть то и другое.
Крайности: Закончилось что-то, можно сделать довыборку того что еще осталось. Как-то так.
P.S сейчас почитал, что написал Champion я действовал также.
 
 Top
DeepVarvar Супермодератор
Отправлено: 14 Января, 2014 - 16:50:46
Post Id



Активный участник


Покинул форум
Сообщений всего: 10377
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




Champion, дал спасибку, но поковыряю потом, щас уже другая задача в приоритете.
 
 Top
Страниц (1): [1]
Сейчас эту тему просматривают: 0 (гостей: 0, зарегистрированных: 0)
« SQL и Архитектура БД »


Все гости форума могут просматривать этот раздел.
Только зарегистрированные пользователи могут создавать новые темы в этом разделе.
Только зарегистрированные пользователи могут отвечать на сообщения в этом разделе.
 



Powered by PHP  Powered By MySQL  Powered by Nginx  Valid CSS  RSS

 
Powered by ExBB FM 1.0 RC1. InvisionExBB