Изменение запроса VIEWS

Всем доброго времени суток.

Для своих целей я поставил модуль: http://drupal.org/project/family
Код не идеальный но функционал меня в принципе устраивает. ПОэтому есть желание его отрефакторить и исползовать на сайте. Насколько могу судить, модуль этот сделан с помощью Views.

Но проблема заключается в том, что после того, как я экспортнул в него свои данные (окло 60 тыс записей), то он начал тормозить. Тормозят, главным образом несколько запросов, которые я и хочу заменить, но не знаю как это сделать. В это и прошу Вашей помощи.

Запрос первый:
SELECT node.nid AS nid, family_individual.lastname AS family_individual_lastname, family_individual.firstname AS family_individual_firstname, family_individual.middlename AS family_individual_middlename, family_individual.gender AS family_individual_gender, family_individual.birthdate AS family_individual_birthdate, family_individual.deathdate AS family_individual_deathdate
FROM dru_node node
INNER JOIN dru_family_individual family_individual ON node.nid = family_individual.nid
WHERE (
node.type
IN (
'family_individual'
)
)
AND (
node.STATUS <>0
)
ORDER BY family_individual_lastname DESC
LIMIT 41840 , 40

В принципе он уже мною оптимизирован и выполняется 1 сек. Но это все равно чересчур много.
//До оптимизации там был LEFT JOIN и не было индексов некоторых.

Запрос, который я хотел бы использовать вместо него:

SELECT
family_individual.nid AS nid,
family_individual.lastname AS family_individual_lastname,
family_individual.firstname AS family_individual_firstname,
family_individual.middlename AS family_individual_middlename,
family_individual.gender AS family_individual_gender,
family_individual.birthdate AS family_individual_birthdate,
family_individual.deathdate AS family_individual_deathdate
FROM (
SELECT `nid`
FROM `dru_family_individual` FORCE INDEX(`lastname`)
JOIN `dru_node` FORCE INDEX(`type`) USING(`nid`)
WHERE `type` = 'family_individual' AND `status` <> 0
ORDER BY `lastname` DESC
LIMIT 40000, 40
) AS `fi_order`
JOIN `dru_family_individual` AS `family_individual` USING(`nid`)
ORDER BY `family_individual_lastname` DESC

Этот запрос в несколько раз быстрее + скорость его выполнения практически не будет расти по мере добавления данных, т.к. filesort будет происходить лишь с 40 записями.

Второй запрос:
SELECT COUNT(*) FROM (SELECT node.nid AS nid FROM dru_node node INNER JOIN dru_family_individual family_individual ON node.nid = family_individual.nid WHERE (node.type IN ('family_individual')) AND (node.STATUS <> 0) ) count_alias
Хотелось бы сделать без подзапроса, что позволило бы ему просто летать. Т.е.:

SELECT COUNT(*) FROM dru_node node INNER JOIN dru_family_individual family_individual USING (`nid`) WHERE (node.type IN ('family_individual')) AND (node.STATUS <> 0)

Очень надеюсь на Вашу помощь,
Заранее спасибо.

Коментарів:

6

Коментувати

Увійдіть або зареєструйтесь, щоб додати коментар

Коментарі

есть hook_views_pre_execute(&$view)
с его помощью можно перехватить и изменить запрос.
Получается в функции обработки нужно с начало выяснить это тот ВИЕВС который нам нужен или нет
после этого составить новый запрос или изменить текущий и заменить оригинальный
$view->build_info['count_query'] = $query;
$view->build_info['query'] = $query;

Описание проблемы:
На последнем этапе проблема формулировалась следующим образом. Я уже умел подменить запрос VIEWS с помощью function hook_views_pre_execute(&$view), но при этом Друпал вставлял в конец моего запроса ЛИМИТ, который сводил на нет все усилия по оптимизации. Поэтому задачи стояло 2:
1. Найти способ сделать выборку именно нужного участка запроса (т.е. поставить правильный ЛИМИТ для внутреннего подзапроса)
2. Обойти ЛИМИТ, навязанный Друпалом.

Если первый пункт был довольно несложен, т.к. $view->pager['items_per_page'] содержит количество элементов на странице, а $_GET['page'] - номер страницы. То останавливало меня лишь вера в то, что нехорошо напрямую лезть в суперглобальный массив, и что это же значение должно быть в каком-нибудь системном объекте. Но после того, как я увидел, что сам модуль VIEWS лезет в $_GET за этим значением - я успокоился и со спокойной совестью сделал то же.

Со вторым пунктом сложностей у меня возникло больше, но тем не менее:

function family_views_pre_execute(&$view)
{

if($view && $view->name === 'family')
{
switch($view->current_display){
case 'page_1' :

break;
case 'page_2' :

$cnt=db_result(db_query("SELECT count(*) AS `cnt` FROM dru_node node WHERE (node.type in ('family_individual')) AND (node.status <> 0)"));

$curr_page = intval($_GET['page']);
$per_page = $view->pager['items_per_page'];
$offset = $curr_page*$per_page;
$view->pager['offset']=($offset>$cnt)?0:-$offset;

$view->build_info['query'] ="SELECT
`family_individual`.`nid` AS nid,
`family_individual`.`lastname` AS family_individual_lastname,
`family_individual`.`firstname` AS family_individual_firstname,
`family_individual`.`middlename` AS family_individual_middlename,
`family_individual`.`gender` AS family_individual_gender,
`family_individual`.`birthdate` AS family_individual_birthdate,
`family_individual`.`deathdate` AS family_individual_deathdate
FROM (
SELECT `nid`
FROM `dru_family_individual` FORCE INDEX(`lastname`)
JOIN `dru_node` FORCE INDEX(`type`) USING(`nid`)
WHERE `type` IN ('%s') AND `status` <> 0
ORDER BY `lastname` DESC
LIMIT $offset, $per_page
) AS `tmp`
JOIN `dru_family_individual` AS `family_individual` USING(`nid`)
ORDER BY `family_individual_lastname` DESC";

$view->build_info['count_query'] ="SELECT nid FROM dru_node node WHERE (node.type in ('family_individual')) AND (node.status <> 0)";
break;

case 'page_1' :
echo 'hi';
break;

case 'page_1' :
echo 'hi';
break;

}
}

}

проблема решилась таким вот образом. Идея заключается в том, что VIEWS смотрит номер страницы прямо из $_GET['page'], а из $view->pager используется только 2 эелемента:
items_per_page & offset. При чем последний используется подобным образом:

$offset = $this->pager['current_page'] * $this->pager['items_per_page'] + $this->pager['offset'];
$result = db_query_range($query, $args, $offset, $this->pager['items_per_page']);

//файл view.inc

Так вот оффсетом я и решил воспользоваться, чтобы обнулить отступ в моей выборке (т.е. ЛИМИТ, который припишет Друпал в конец моего запроса). При этом нужную часть выборки выбираю сам, беря номер страницы из $_GET['page']

У данного способа есть проблема со ссылкой на последнюю страницу, которую я не знаю как побороть...

$diff=$cnt-$offset;
$view->build_info['count_query'] ="SELECT nid FROM dru_node node WHERE (node.type IN ('%s')) LIMIT $diff";

Если заменить код подсчета количества записей на этот, при этом сохранив весь предыдущий, то проблема с количеством страниц пропадет, но останется значительно бОльшая производительность. что не может не радовать :).

хакерский метод
в запросе нужно писать вместо
FROM dru_node
в таком виде:
FROM {node}
друпал сам префиксы в таблицы добавит

А как можно нехакерским сделать? Ведь Друпал ЛИМИТ обязательно на внешний запрос вешает...

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