Pergunta

Uma das "melhores práticas" é acessar dados através de procedimentos armazenados. Eu entendo porque é que este cenário bom. Minha motivação é banco de dados de divisão e lógica da aplicação (as mesas me pode alterado, se o comportamento de procedimentos armazenados são os mesmos), a defesa de injeção de SQL (os usuários não podem executar "SELECT * FROM some_tables", eles só podem chamar procedimentos armazenados), e segurança (em procedimento armazenado pode ser "qualquer coisa" que assegurem, que o usuário não pode selecionar / insert / update / dados de exclusão, que não é para eles).

O que eu não sei é como dados de acesso com filtros dinâmicos.

Eu estou usando MSSQL 2005.

Se eu tiver tabela:

CREATE TABLE tblProduct (
   ProductID uniqueidentifier -- PK
   , IDProductType uniqueidentifier -- FK to another table
   , ProductName nvarchar(255) -- name of product
   , ProductCode nvarchar(50) -- code of product for quick search
   , Weight decimal(18,4)
   , Volume decimal(18,4)
)

então eu deveria criar 4 procedimentos armazenados (criar / ler / update / delete).

O procedimento armazenado para "criar" é fácil.

CREATE PROC Insert_Product ( @ProductID uniqueidentifier, @IDProductType uniqueidentifier, ... etc ... ) AS BEGIN
   INSERT INTO tblProduct ( ProductID, IDProductType, ... etc .. ) VALUES ( @ProductID, @IDProductType, ... etc ... )
END

O procedimento armazenado para "apagar" é muito fácil.

CREATE PROC Delete_Product ( @ProductID uniqueidentifier, @IDProductType uniqueidentifier, ... etc ... ) AS BEGIN
    DELETE tblProduct WHERE ProductID = @ProductID AND IDProductType = @IDProductType AND ... etc ...
END

O procedimento armazenado de "atualização" é semelhante ao de "apagar", mas não estou certo de que este é o caminho certo, como fazê-lo. Eu acho que atualizar todas as colunas não é eficiente.

CREATE PROC Update_Product( @ProductID uniqueidentifier, @Original_ProductID uniqueidentifier, @IDProductType uniqueidentifier, @Original_IDProductType uniqueidentifier, ... etc ... ) AS BEGIN
   UPDATE tblProduct SET ProductID = @ProductID, IDProductType = @IDProductType, ... etc ...
      WHERE ProductID = @Original_ProductID AND IDProductType = @Original_IDProductType AND ... etc ...
END

E a última - procedimento armazenado para "ler" é mistério littlebit para mim. Como passar valores de filtro para condições complexas? Eu tenho uma sugestão alguns:

Usando o parâmetro XML para passar onde condição:

CREATE PROC Read_Product ( @WhereCondition XML ) AS BEGIN
    DECLARE @SELECT nvarchar(4000)
    SET @SELECT = 'SELECT ProductID, IDProductType, ProductName, ProductCode, Weight, Volume FROM tblProduct'

    DECLARE @WHERE nvarchar(4000)
    SET @WHERE = dbo.CreateSqlWherecondition( @WhereCondition ) --dbo.CreateSqlWherecondition is some function which returns text with WHERE condition from passed XML

    DECLARE @LEN_SELECT int
    SET @LEN_SELECT = LEN( @SELECT )
    DECLARE @LEN_WHERE int
    SET @LEN_WHERE = LEN( @WHERE )
    DECLARE @LEN_TOTAL int
    SET @LEN_TOTAL = @LEN_SELECT + @LEN_WHERE
    IF @LEN_TOTAL > 4000 BEGIN
        -- RAISE SOME CONCRETE ERROR, BECAUSE DYNAMIC SQL ACCEPTS MAX 4000 chars
    END

    DECLARE @SQL nvarchar(4000)
    SET @SQL = @SELECT + @WHERE

    EXEC sp_execsql @SQL
END

Mas, eu acho que a limitação de "4000" caracteres para uma consulta é feio.

A próxima sugestão é o uso de tabelas de filtro para cada coluna. Inserir valores de filtro na tabela de filtro e, em seguida, chamar procedimento armazenado com ID de filtros:

CREATE TABLE tblFilter (
   PKID uniqueidentifier -- PK
   , IDFilter uniqueidentifier -- identification of filter
   , FilterType tinyint -- 0 = ignore, 1 = equals, 2 = not equals, 3 = greater than, etc ...
   , BitValue bit , TinyIntValue tinyint , SmallIntValue smallint, IntValue int
   , BigIntValue bigint, DecimalValue decimal(19,4), NVarCharValue nvarchar(4000)
   , GuidValue uniqueidentifier, etc ... )

CREATE TABLE Read_Product ( @Filter_ProductID uniqueidentifier, @Filter_IDProductType uniqueidentifier, @Filter_ProductName uniqueidentifier, ... etc ... ) AS BEGIN
   SELECT ProductID, IDProductType, ProductName, ProductCode, Weight, Volume
   FROM tblProduct
   WHERE ( @Filter_ProductID IS NULL
            OR ( ( ProductID IN ( SELECT GuidValue FROM tblFilter WHERE IDFilter = @Filter_ProductID AND FilterType = 1 ) AND NOT ( ProductID IN ( SELECT GuidValue FROM tblFilter WHERE IDFilter = @Filter_ProductID AND FilterType = 2 ) )
      AND ( @Filter_IDProductType IS NULL
            OR ( ( IDProductType IN ( SELECT GuidValue FROM tblFilter WHERE IDFilter = @Filter_IDProductType AND FilterType = 1 ) AND NOT ( IDProductType IN ( SELECT GuidValue FROM tblFilter WHERE IDFilter = @Filter_IDProductType AND FilterType = 2 ) )
      AND ( @Filter_ProductName IS NULL OR ( ... etc ... ) ) 
END

Mas esta sugestão é littlebit complicado eu acho.

Existe algum "melhores práticas" para fazer este tipo de procedimentos armazenados?

Foi útil?

Solução

Primeiro:. Para a sua rotina de exclusão, o seu onde cláusula deve incluir apenas a chave primária

Segundo: para sua rotina de atualização, não tentar otimizar antes de você ter o código de trabalho. Na verdade, não tentar otimizar até que você pode perfil a aplicação e ver quais são os gargalos. Eu posso dizer com certeza que a atualização uma coluna de uma linha e atualizar todas as colunas de uma linha são quase idênticas na velocidade. O que leva tempo num DBMS é (1) encontrar o bloco de disco onde você vai escrever os dados e (2) o bloqueio dos outros escritores para que sua gravação será consistente. Finalmente, escrever o código necessário para atualização apenas as colunas que necessidade de mudança será geralmente mais difícil de fazer e mais difícil de manter. Se você realmente queria obter exigente, você tem que comparar a velocidade de descobrir quais colunas mudou em comparação com apenas atualizando a cada coluna. Se você atualizar todos eles, você não tem que ler qualquer um deles.

Em terceiro lugar: Eu tendem a escrever um procedimento armazenado para cada caminho de recuperação. No seu exemplo, eu faria uma a chave primária, um por cada chave estrangeira e, em seguida, eu acrescentaria um para cada novo caminho de acesso como eu precisava deles na aplicação. Seja ágil; Não escreva código que você não precisa. Concordo também com o uso de pontos de vista, em vez de procedimentos armazenados, no entanto, você pode usar um procedimento armazenado para retornar vários conjuntos de resultados (em alguma versão do MSSQL) ou para linhas de mudança em colunas, que pode ser útil.

Se você precisa para obter, por exemplo, 7 linhas de chave primária, você tem algumas opções. Você pode chamar o procedimento armazenado que recebe uma linha por vezes sete chaves primárias. Isso pode ser rápido o suficiente, se você manter a conexão aberta entre todas as chamadas. Se você sabe que nunca precisa mais do que um determinado número (digamos, 10) de IDs de cada vez, você pode escrever um procedimento armazenado que inclui uma cláusula WHERE como "e ID no (arg1, arg2, arg3 ...)" e make Certifique-se que os argumentos não utilizados estão definidos para NULL. Se você decidir que precisa para gerar SQL dinâmica, eu não me incomodaria com um procedimento armazenado porque TSQL é tão fácil cometer um erro como qualquer outra língua. Além disso, você ganha nenhum benefício de usar o banco de dados para fazer a manipulação de cadeia. - É quase sempre o gargalo, então não há nenhum ponto em dar o DB mais trabalho do que o necessário

Outras dicas

Para a leitura de dados, você não precisa de um procedimento armazenado para a segurança ou para separar a lógica, você pode usar pontos de vista.

Apenas conceder apenas seleccionar na vista.

Você pode limitar os registros mostrado, nomes de campos mudança, juntar muitas tabelas em uma "mesa" lógico, etc.

Eu discordo que criar Insert / Update / Escolha procedimentos armazenados são uma "melhor prática". A menos que todo o seu aplicativo é escrito em SPs, use uma camada de banco de dados em seu aplicativo para lidar com essas atividades CRUD. Melhor ainda, use uma tecnologia ORM para lidar com eles para você.

A minha sugestão é que você não tentar criar um procedimento armazenado que faz tudo o que você pode agora nem nunca precisa fazer. Se você precisar recuperar uma linha com base na chave primária da tabela, em seguida, escrever um procedimento armazenado para fazer isso. Se você precisa procurar por todas as linhas que atendem um conjunto de critérios, em seguida, descobrir o que esse critério pode ser e escrever um procedimento armazenado para fazer isso.

Se você tentar escrever software que resolve todos os problemas possíveis ao invés de um conjunto específico de problemas que geralmente falham em fornecer qualquer coisa útil.

seu procedimento armazenado seleciona pode ser feito da seguinte forma a exigir apenas um proc armazenado, mas qualquer número de diferentes itens na cláusula onde. Passe em qualquer um ou uma combinação dos parâmetros e você vai obter todos os itens que jogo -. Assim você só precisa de um proc armazenados

Create sp_ProductSelect
(
 @ProductID int = null,
 @IDProductType int = null,
 @ProductName varchar(50) = null,
 @ProductCode varchar(10) = null,
 ...
 @Volume int = null
)
AS
SELECT ProductID, IDProductType, ProductName, ProductCode, Weight, Volume FROM tblProduct'  
Where
  ((@ProductID is null) or (ProductID = @ProductID)) AND
  ((@ProductName is null) or (ProductName = @ProductName)) AND
  ...
  ((@Volume is null) or (Volume= @Volume))

No SQL 2005, ele suporta nvarchar (max), que tem um limite de 2G, mas praticamente aceitar todas as operações de cadeia em cima nvarchar normal. Você pode querer testar se isso pode caber em que você precisa na primeira abordagem.

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