Pergunta

Eu escrevi um procedimento armazenado de pesquisa paginada usando o SQL Server 2005. É preciso vários parâmetros e os critérios de pesquisa são moderadamente complexos.

Devido à arquitetura front-end, preciso devolver o número de resultados que voltariam sem realmente retornando os resultados. O front -end chamaria o procedimento armazenado pela segunda vez para obter os resultados reais.

Por um lado, posso escrever dois procedimentos armazenados - um para lidar com a contagem e outro para lidar com os dados reais, mas preciso manter a lógica de pesquisa em pelo menos dois lugares diferentes. Como alternativa, posso escrever o procedimento armazenado para que seja necessário um parâmetro de bit e com base nisso, eu retorno dados ou apenas uma contagem. Talvez preencha uma tabela temporária com os dados e, se for a contagem, basta contar com isso, caso contrário, faça uma seleção. O problema aqui é que o processo de contagem pode ser otimizado, de modo que isso é uma sobrecarga extra que parece (precisa obter colunas desnecessárias etc.). Além disso, o uso desse tipo de lógica em um procedimento armazenado pode resultar em planos de consulta ruim, à medida que remonta entre os dois usos.

A quantidade de dados no sistema não é muito alta (apenas alguns milhões de linhas para as tabelas maiores). Pode haver muitos usuários simultâneos.

Quais são os pensamentos das pessoas sobre essas abordagens? Alguém já resolveu esse problema antes de uma maneira que eu não pensei?

Elas NÃO PODES Pegue os resultados e conte ao mesmo tempo a partir de uma única chamada.

Obrigado!

Foi útil?

Solução

Pessoalmente, eu vou com a abordagem de duas consultas, sim, você precisa manter a lógica de pesquisa em dois lugares, mas descobri que o benefício de otimização de desempenho e a limpeza geral do código compensa no final.

O uso de um sinalizador passado para um único procedimento é uma solução em potencial, mas acho isso muito difícil de manter, especialmente para a lógica de pesquisa complexa.

A rota de uso de tabelas temporárias etc, que adiciona muito mais sobrecarga do que o necessário.

Assim, por que eu aterrei com o método de duas consultas. Tudo o que acho on -line recomenda essa abordagem também.

Outras dicas

Este não é um problema normal e você geralmente deseja a contagem total ao mesmo tempo em que você obteve uma página.

Dito isto, use dois procedimentos diferentes. O motivo é que você tem duas ações muito diferentes que se parecem superficialmente.

Tenho certeza de que você considerou isso: se os dados estão alterando a contagem e qualquer paginação real subsequente poderá ser diferente (se linhas adicionadas / removidas)

Você pode ter uma função definida pelo usuário que retornou os PKs das linhas correspondentes, relativamente fácil de fazer um

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

para obter a contagem, e então

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

Para obter os dados.

Não sei o quão bem isso fica com Row_Number para a paginação subsequente, mas manteria a "lógica de consulta" real contida no MyQueryFunction - você ainda terá todas as junções para que qualquer coluna seja recuperada duplcia no sproc e a função.

Pode não ajudar com seu problema específico, mas o SQL 2005 apresenta a função row_number, que é útil para verificação de paginação

Exemplo row_number

Muito mais fácil do que as mesas temporárias.

Encontrei esse tópico pesquisando outra coisa e pensei que mencionaria que é possível devolver o conjunto de resultados e a contagem de registros com uma consulta. Você só precisa de um parâmetro 'Out' para carregar o valor. Abaixo está uma cópia/pasta de um exemplo do Oracle, mas a técnica é muito semelhante para o SQL Server (não tenho acesso ao SQL Server ATM).

A grande coisa com o SQL Server é que você pode precisar usar row_number () vs 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;

Eu sei disso uma pergunta antiga (que já foi marcada), mas você pode retornar um conjunto de registros (também conhecido como os resultados) e ter um valores de saída (ou múltiplos), o que significa que você precisa apenas de uma viagem de ida e volta ao banco de dados.

Isso é algo que estou pensando em voz alta (e já passou da minha hora da minha cama ...)

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

Hth.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top