При разработке очередного интернет-магазина на 1С-Битрикс, нужно было вывести горизонтальное меню, состоящее из >250 категорий каталога. В Битриксе с этим проблем нет. Подключаем компонент menu.sections и получаем массив пунктов меню в нужном формате для компонента menu.

Но если выключить кеш и включить отладку, то увидим что компонент формирует >250 SQL запросов. Конечно, когда будет включен кеш на сайте, столько не будет и всё будет браться из кэша.
Но всё таки, когда будет сбрасываться кеш, это будет ощутимо. Попробуем разобраться в чем же дело.

Идем в компонент и видим, что категории выбираются функцией CIBlockSection::GetList, которая возвращает CIBlockResult. Далее функцией CIBlockResult::GetNext получаем список полей. GetNext возвращаем элементы, приведенные в HTML безопасный вид и так же преобразует в DETAIL_PAGE_URL, SECTION_PAGE_URL в реальные урлы элементов и категорий.

Тут-то и оно, если добавить в выборку SECTION_PAGE_URL, то на каждую категорию для постраения урла будет сделано столько подзапросов, сколько категорий.

Проверим на практике.


CIBlockSection::GetList и GetNext

<?php
require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/prolog_before.php");
 
// подключаем модуль «Информационные блоки»
CModule::IncludeModule('iblock');
 
// определяем настройки
$arParams = array(
    'IBLOCK_ID' => 2,
);
 
$sections = array();
 
$resCatalog = CIBlockSection::GetList(
    array(
        'LEFT_MARGIN' => 'ASC'
    ), 
    array(
        'IBLOCK_ID' => $arParams['IBLOCK_ID'], 
        'ACTIVE' => 'Y', 
        'GLOBAL_ACTIVE' => 'Y',
    ), 
    false, 
    array(
        'IBLOCK_ID', 
        'IBLOCK_SECTION_ID', 
        'NAME', 
        'SECTION_PAGE_URL', 
        'UF_*'
    )
);
 
while($arCatalog = $resCatalog->GetNext()){
    $sections[$arCatalog['ID']] = $arCatalog;
}

Включаем отладку в битриксе и видим кучу таких запросов:

SELECT IBLOCK_ID, CODE FROM b_iblock_section WHERE ID = 213;  
SELECT BS.ID AS ID, BS.IBLOCK_SECTION_ID AS IBLOCK_SECTION_ID, BS.CODE AS CODE 
FROM b_iblock_section BS 
INNER JOIN b_iblock B ON B.ID = BS.IBLOCK_ID 
WHERE BS.ID=213 AND BS.IBLOCK_ID=2;

Лог: Получаем: 0.2510; Запросов: 252


CIBlockSection::GetList и Fetch

Попробуем выкинуть из выборки SECTION_PAGE_URL и построить урлы с помощью своего способа без подзапросов.

<?php
require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/prolog_before.php");
 
// подключаем модуль «Информационные блоки»
CModule::IncludeModule('iblock');
 
// определяем настройки
$arParams = array(
    'IBLOCK_ID' => 2,
);
 
// сюда будем складывать все категории
global $sections;
 
// этой функцией будем стоить урл
function makeUrl($parentId = 0){
 
    global $sections;
 
    $sefFolder = '/katalog/';       
 
    $codes = array();
 
    while (isset($parentId)) {
        $codes[] = $sections[$parentId]['CODE'];
        if(isset($sections[$parentId])){
            $parentId = $sections[$parentId]['IBLOCK_SECTION_ID'];
        }else{
            $parentId = null;
        }
    }
 
    if (sizeof($codes)) {
        return $sefFolder . implode('/', array_reverse($codes));
    } else {
        return null;
    }   
}
 
$sections = array();
 
$resCatalog = CIBlockSection::GetList(
    array(
        'LEFT_MARGIN' => 'ASC'
    ), 
    array(
        'IBLOCK_ID' => $arParams['IBLOCK_ID'], 
        'ACTIVE' => 'Y', 
        'GLOBAL_ACTIVE' => 'Y',
    ), 
    false, 
    array(
        'IBLOCK_ID', 
        'IBLOCK_SECTION_ID', 
        'NAME', 
        'SECTION_PAGE_URL', 
        'UF_*'
    )
);
 
while($arCatalog = $resCatalog->Fetch()){
    $sections[$arCatalog['ID']] = $arCatalog;
}
 
foreach($sections as &$section){
    $section['SECTION_PAGE_URL'] = makeUrl($section['ID']);
}

Получаем: 0.0263 с; Запросов: 1


Результат

Не делая лишних подзапросов, и заменив GetNext на Fetch мы получили прирост в скорости в 10 раз.