SQLSRV Параметризованный запрос выполняется вечно в предложении GROUP BY - PullRequest
0 голосов
/ 04 мая 2018

Я использую SQLSRV 4.3 PDO с PHP 7.1. Я уже проверял это на SQLSRV 3.2 с PHP 5.6 тоже. Итак, у меня есть следующий запрос:

$sql = "select [ID], [Year], [Month], sum(Value) as Value, 
MIN(FirstDateTime) as FirstDateTime, MAX(SecondDateTime) as 
SecondDateTime from [ValuesTable] where [FirstDateTime] >= ? and 
[SecondDateTime] <= ? and [Year] in (?) GROUP BY [ID], 
[Year], [Month] order by [ID] offset 0 rows fetch next 30 rows only";

И его параметры:

$param1 = '2017-10-01 00:00:00';
$param2 = '2017-10-31 23:59:59';
$param3 = '2017';

Когда я выполняю запрос со следующим кодом:

$result = $db->prepare($sql)
$result->bindParam(1, $param1);
$result->bindParam(2, $param2);
$result->bindParam(3, $param3);
$result->execute();

Требуется вечность, чтобы закончить. Если вместо этого я выполняю следующий небезопасный запрос:

$sql = "select [ID], [Year], [Month], sum(Value) as Value, 
MIN(FirstDateTime) as FirstDateTime, MAX(SecondDateTime) as 
SecondDateTime from [ValuesTable] where [FirstDateTime] >= '2017-10-01 00:00:00' and 
[SecondDateTime] <= '2017-10-31 23:59:59' and [Year] in (2017) GROUP BY [ID], 
[Year], [Month] order by [ID] offset 0 rows fetch next 30 rows only";

Это не займет много времени, чтобы завершить. Я проверил много и исследовал много, и я не мог найти ответ на свою проблему.

Я уже установил атрибуты для PDO, например

$db->setAttribute( \PDO::SQLSRV_ATTR_ENCODING, \PDO::SQLSRV_ENCODING_SYSTEM );
$db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);

Кроме того, я помещаю типы параметров в функцию bindParam(), как в

$result->bindParam(1, $param1, PDO::PARAM_STR);
$result->bindParam(2, $param2, PDO::PARAM_STR);
$result->bindParam(3, $param3, PDO::PARAM_INT);

Я также изменил тип курсора с помощью следующего кода

$result = $db->prepare($sql, array(
   \PDO::ATTR_CURSOR => \PDO::CURSOR_SCROLL, 
   \PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE => \PDO::SQLSRV_CURSOR_BUFFERED
   )
);

Параметризованный запрос все еще выполняется вечно, ничего не изменилось. Кроме того, выполнили следующие тесты только для того, чтобы предотвратить кэш-память запросов SQL Server или что-то еще:

  1. Перезапущенный Sql-сервер
  2. выполнил параметризованный запрос, и для его завершения потребовалось целое время
  3. Перезапущенный Sql-сервер
  4. Выполнил небезопасный запрос и быстро его выполнил

Кто-нибудь догадывается, что я делаю не так?

О, я забыл упомянуть одну вещь: таблица "ValuesTable" - это секционированное представление, в котором есть таблица для каждого года. Как ValuesTable2016, ValuesTable2017. Каждая таблица имеет проверочное ограничение, поэтому оптимизатор запросов будет знать, какие таблицы включить в запрос. Представление создается с помощью следующего сценария:

CREATE View [dbo].[ValuesTable] as 

    SELECT * FROM ValuesTable2016 UNION ALL 

    SELECT * FROM ValuesTable2017 UNION ALL 

    SELECT * FROM ValuesTable2018

Если я выполню параметризованный запрос, используя TABLE, а не секционированный VIEW, как

$sql = "select [ID], [Year], [Month], sum(Value) as Value, 
    MIN(FirstDateTime) as FirstDateTime, MAX(SecondDateTime) as 
    SecondDateTime from [ValuesTable2017] where [FirstDateTime] >= ? and 
    [SecondDateTime] <= ? and [Year] in (?) GROUP BY [ID], 
    [Year], [Month] order by [ID] offset 0 rows fetch next 30 rows only";

Запрос выполняется быстро, как в небезопасном. Но, очевидно, мне нужно использовать разделенное представление, так что это не вариант.

Небезопасный запрос всегда выполняется быстро, используя секционированное представление или таблицу. Так что нет смысла предлагать мне изменить его структуру. Дело в том, что непараметризованный запрос выполняется быстро, а параметризованный - навсегда.

Еще одна вещь: я уже тестировал его без pdo, используя sqlsrv_prepare с параметрами связывания и выполняя его с помощью sqlsrv_execute. Он также работает очень медленно.

1 Ответ

0 голосов
/ 05 мая 2018

Я нашел обходной путь: если я выполняю следующий запрос, включая объединение всех, ТОЛЬКО для таблиц, содержащих данные (а не это представление с объединением всех для всех таблиц), он выполняется так же быстро, как и непараметризованный запрос :

$sql = "select [ID], [Year], [Month], sum(Value) as Value, 
MIN(FirstDateTime) as FirstDateTime, MAX(SecondDateTime) as 
SecondDateTime from 
(select * FROM [ValuesTable2016] UNION ALL select * FROM [ValuesTable2017]) 
as ValuesTable
where [FirstDateTime] >= ? and 
[SecondDateTime] <= ? and [Year] in (?, ?) GROUP BY [ID], 
[Year], [Month] order by [ID] offset 0 rows fetch next 30 rows only";

С параметрами

$param1 = '2016-10-01 00:00:00';
$param2 = '2017-01-01 23:59:59';
$param3 = '2016';
$param3 = '2017';

$result->bindParam(1, $param1);
$result->bindParam(2, $param2);
$result->bindParam(3, $param3);
$result->bindParam(4, $param4);

Так что я предполагаю, что по какой-то причине, когда я выполняю параметризованный запрос с группой по клаузуле И объединением всех всех связанных таблиц с проверочным ограничением, для его завершения требуется вечность. Если я делаю это без группы по клаусуле, это быстро. Если я делаю это только с таблицами, к которым я обращаюсь, это быстро. Если я небезопасно выполняю его с параметрами внутри запроса, это быстро.

ТАК, это своего рода обходной путь, и он работает. Но я был бы ОЧЕНЬ признателен, если бы кто-то мог объяснить, почему это происходит. Я знаю, что это особенность, которую мало кто заметит, и, возможно, если это действительно проблема с параметризованными запросами, люди не потрудятся ее исправить, потому что я не так важен. Кроме того, судьба всегда делает мою жизнь труднее, заставляя меня сталкиваться с такой необычной проблемой. Но если у кого-то есть время и доброта, мне бы очень хотелось узнать, что происходит.

...