Получение подсчетов для хранимой процедуры поискового запроса SQL - PullRequest
2 голосов
/ 03 ноября 2008

Я написал хранимую процедуру постраничного поиска с использованием SQL Server 2005. Она принимает несколько параметров, а критерии поиска довольно сложны.

Из-за интерфейсной архитектуры мне нужно иметь возможность возвращать количество результатов, которые вернутся без фактического возврата результатов. Затем передний конец вызовет хранимую процедуру во второй раз, чтобы получить фактические результаты.

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

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

Что думают люди об этих подходах? Кто-нибудь раньше решал эту проблему так, как я не думал?

Они НЕ МОГУТ взять результаты и рассчитывать одновременно с одного звонка.

Спасибо!

Ответы [ 6 ]

4 голосов
/ 03 ноября 2008

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

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

Способ использования временных таблиц и т. Д., Который просто добавляет НАМНОГО больше накладных расходов, чем необходимо.

Итак, почему я приземлился с помощью метода двух запросов. Все, что я нахожу онлайн, также рекомендует этот подход.

2 голосов
/ 10 ноября 2009

Я уверен, что вы учли это: если данные меняются COUNT и любой последующий фактический пейджинг может отличаться (если строки добавлены / удалены)

У вас может быть Пользовательская функция, которая возвращает PK соответствующих строк, относительно просто сделать

SELECT COUNT(*) FROM dbo.MyQueryFunction(@Param1, @Param2)

чтобы получить счет, а затем

SELECT Col1, Col2, ...
FROM dbo.MyQueryFunction(@Param1, @Param2) AS FN
     JOIN dbo.MyTable AS T
         ON T.ID = FN.ID
     ... more JOINs ...

чтобы получить данные.

Не знаю, насколько хорошо это сочетается с Row_Number для последующей подкачки, но при этом будет сохраняться фактическая «логика запроса», содержащаяся в MyQueryFunction - у вас все еще будут все соединения для любого столбца, который нужно извлечь Sproc и функция.

2 голосов
/ 03 ноября 2008

Это не нормальная проблема, и вы обычно хотите, чтобы общее количество учитывалось при получении страницы.

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

1 голос
/ 10 ноября 2009

Я обнаружил, что этот поток исследует что-то еще, и подумал, что упомяну, что можно вернуть набор результатов и количество записей одним запросом. Вам просто нужен параметр 'out' для переноса значения. Ниже приведен пример копирования / вставки Oracle, но этот метод очень похож на SQL Server (у меня нет доступа к SQL Server atm).

Самое важное в SQL Server - вам может понадобиться использовать row_number () против rownum.

procedure get_sample_results (
    startrow in number default 1,
    numberofrows in number default 10,
    whereclause in varchar2,
    matchingrows out number,
    rc  out sys_refcursor
)
is
    stmnt varchar2(5000);
    endrow number;
begin

    stmnt := stmnt || 'select * from table t where 1=1';
    if whereclause is not null then
        stmnt := stmnt || ' and ' || whereclause;
    end if;

    execute immediate 'select count(*) from (' || stmnt || ')' into matchingrows;

    stmnt := 'select * from (' || stmnt || ') where rownum between :1 and :2';        

    -- must subtract one to compenstate for the inclusive between clause
    endrow := startrow + numberofrows - 1;
    open rc for stmnt using startrow, endrow;

end get_sample_results;
1 голос
/ 03 ноября 2008

Может не помочь с вашей конкретной проблемой, но SQL 2005 представляет функцию Row_Number, которая удобна для проверки подкачки

Пример Row_number

Гораздо проще, чем временные таблицы.

0 голосов
/ 15 ноября 2010

Я знаю, что это старый вопрос (который уже отмечен), но вы можете вернуть набор записей (он же результаты) И иметь значения ВЫХОДА (или нескольких выходных данных), что означает, что вам нужна только одна поездка в базу данных .

Это то, о чём я думаю вслух (и это уже позади моего сна ...)

CREATE PROCEDURE WhatEver
(
   @SomeParam1 NVARCHAR(200),
   ....
   @SomeParam_X INT,
   @NumberOfResults INTEGER OUTPUT
)
BEGIN
    SET NOCOUNT ON

    -- Do your search stuff.
    -- ....
    SELECT Whatever
    FROM WhatWhat
    ...

    -- Ok, the results/recordset has been sent prepared.
    -- Now the rowcount
    SET @NumberOfResults = @@ROWCOUNT
END

НТН.

...