3
04.12.201814:2004.12.2018 14:20:38
Собрались тут кадры запустить большой опрос - больше сотни вопросов.
Сказал, делайте на портале, есть же возможность, зачем вам сторонние системы... теперь даже как-то жалею. Очередная жесть. То, что неудобно и устарело, ладно, фиг с ним. Прикрутил визуалку ранжирования в шаблон, прикрутил вывод в админке в результатах голосования вывод данных авторизованного пользователя, это мелочи.
Но когда сегодня попытался посмотреть результаты голосования в админке и страница отвалилась по таймауту, это окончательно высадило. А кадрам надо выгружать результаты в эксель из админки для дальнейшей работы...
Как это сделано:
vote_user_votes_table.php
foreach ($arAllQuestions as $arQuestion)
{
$txt = '';
foreach($arQuestion["ANSWERS"] as $arAnswer)
{
if ($msg = CVoteEvent::GetAnswer($res['ID'], $arAnswer['ID']))
{
if (
($arAnswer['FIELD_TYPE'] < 4) // not a string
&& (intval($msg) > 0)
)
$msg = $arAnswer['MESSAGE'];
$txt .= htmlspecialcharsbx($msg).'<br />';
}
}
$row->AddViewField("Q".$arQuestion["ID"], $txt);
}
|
CVoteEvent::GetAnswer()
function GetAnswer($EVENT_ID, $ANSWER_ID)
{
$err_mess = (self::err_mess())."<br>Function: GetAnswer<br>Line: ";
global $DB;
$EVENT_ID = intval($EVENT_ID);
$ANSWER_ID = intval($ANSWER_ID);
$strSql = "
SEL ECT
A.ANSWER_ID,
A.MESSAGE
FR OM
b_vote_event E,
b_vote_event_answer A,
b_vote_event_question Q
WHERE
E.ID = '$EVENT_ID'
and Q.EVENT_ID = E.ID
and A.EVENT_QUESTION_ID = Q.ID
and A.ANSWER_ID = '$ANSWER_ID'
";
$z = $DB->Query($strSql, false, $err_mess.__LINE__);
if ($zr = $z->Fetch())
{
if (strlen($zr["MESSAGE"])>0) return $zr["MESSAGE"]; else return $zr["ANSWER_ID"];
}
return false;
} |
Комментарии, наверное, излишни, но всё же.
На каждый потенциальный ответ каждого юзера делается sql запрос (ответа может и не быть, если вопрос не обязательный, но запрос будет).
Проголосовало пока всего 50 человек, это 5 000+ ответов и столько же запросов на получение ответа.
Конечно, это легко оптимизируется и вместо тысяч запросов будет один и выполняться считанные миллисекунды, но тем, у кого такой возможности нет - имейте ввиду при попытках организовать большие опросы с выводом детальных результатов в штатном функционале. Возможно, не стоит.
P.S.Кому нужно быстрых грязных хаков - в клоне vote_user_votes_table.php :
/************** Initial list - Get data ****************************/
$rsData = CVoteEvent::GetList($by, $order, $arFilter, $is_filtered, "Y");
// делаем массив ответов
// выборка нужных ответов, одна штука
$tmpSql = 'SEL ECT e.ID, a.ANSWER_ID, a.MESSAGE FR OM b_vote_event e
LEFT JOIN b_vote_event_question q ON q.event_id = e.id
LEFT JOIN b_vote_event_answer a ON a.event_question_id = q.id
WH ERE e.vote_id = '.$VOTE_ID.';';
$aRes = $DB->Query($tmpSql);
$answers = array(); // массив ответов
while ($answer = $aRes->Fetch())
$answers[$answer["ID"]][$answer["ANSWER_ID"]] = $answer["MESSAGE"];
// готово
$rsData = new CAdminResult($rsData, $sTableID);
$rsData->NavStart();
|
убиваем жесть
foreach ($arAllQuestions as $arQuestion)
{
$txt = '';
foreach($arQuestion["ANSWERS"] as $arAnswer)
{
$msg = false;
// достаём из массива ответов при наличии, а не ломимся в базу с запросом
if (array_key_exists($arAnswer['ID'], $answers[$arRes['ID']]))
$msg = (strlen($answers[$arRes['ID']][$arAnswer['ID']])>0) ? $answers[$arRes['ID']][$arAnswer['ID']] : $arAnswer['ID'];
if ($msg)
{
if (
($arAnswer['FIELD_TYPE'] < 4) // not a string
&& (intval($msg) > 0)
)
$msg = $arAnswer['MESSAGE'];
$txt .= htmlspecialcharsbx($msg).'<br />';
}
}
$row->AddViewField("Q".$arQuestion["ID"], $txt);
}
|
Запрос № 20:
SELECT e.ID, a.ANSWER_ID, a.MESSAGE
FR OM
b_vote_event e
LEFT JOIN b_vote_event_question q ON q.event_id = e.id
LEFT JOIN b_vote_event_answer a ON a.event_question_id = q.id
WHERE
e.vote_id = 32;
Время выполнения: 0.01849 сек. |
И никаких таймаутов, ессно.
ЗЫ. Ну и да, это не единственное место, где здесь всё плохо, извлечение списка вариантов ответов для вопросов страдает такой же ерундой, просто их значительно меньше, поэтому не является основной проблемой. Но в моём случае большого количества вопросов - это лишняя сотня запросов:
$arAllQuestions = array();
$rsQuestions = CVoteQuestion::GetList($voteId, $by, $order, array(), $is_filtered);
while ($arQuestion = $rsQuestions->Fetch())
{
$headers[] = array(
"id"=>"Q".$arQuestion["ID"],
"content"=>htmlspecialcharsbx($arQuestion["QUESTION"]),
"sort"=>'',
"default"=>true);
$arAllAnswers = array();
$rsAnswers = CVoteAnswer::GetList($arQuestion["ID"]); // тот же классический отстой с отдельными запросами для получения вариантов ответов по каждому вопросу
while ($arAnswer = $rsAnswers->Fetch())
{
$arAllAnswers[$arAnswer['ID']]=$arAnswer;
}
$arAllQuestions[] = array('ID' => $arQuestion["ID"], 'ANSWERS' => $arAllAnswers);
}
|
зачистим тоже:
$arAllAnswers = array(); // массив вариантов ответов
$rsAnswers = CVoteAnswer::GetListEx(array("s_c_sort" => "ASC"), array("VOTE_ID" => $VOTE_ID)); // тут, по счастью, для выборки есть годный метод api
while ($arAnswer = $rsAnswers->Fetch())
{
$arAllAnswers[$arAnswer['QUESTION_ID']][$arAnswer['ID']]=$arAnswer;
}
$arAllQuestions = array();
$rsQuestions = CVoteQuestion::GetList($VOTE_ID, $by, $order, array(), $is_filtered);
while ($arQuestion = $rsQuestions->Fetch())
{
$headers[] = array(
"id"=>"Q".$arQuestion["ID"],
"content"=>htmlspecialcharsbx(strip_tags($arQuestion["QUESTION"])),
"sort"=>'',
"default"=>true);
// жесть вырезана
$arAllQuestions[] = array('ID' => $arQuestion["ID"]); // массив нужен для правильного порядка вопросов, но оставляем только ID, остальное уже есть в $arAllAnswers
}
|
ниже
foreach ($arAllQuestions as $arQuestion)
{
$txt = '';
foreach($arAllAnswers[$arQuestion["ID"]] as $arAnswer) // достаём варианты ответов на вопрос из массива
|
вот и минус ещё куча абсолютно ненужных запросов и буст для страницы