Question

L’une des "meilleures pratiques" accède aux données via des procédures stockées. Je comprends pourquoi ce scénario est bon. Ma motivation est fractionnée base de données et logique d'application (les tables peuvent changer, si le comportement des procédures stockées sont les mêmes), défense pour l'injection SQL (les utilisateurs ne peuvent pas exécuter "select * from some_tables", ils ne peuvent appeler que des procédures stockées), et la sécurité (dans une procédure stockée peut être "tout ce qui est sécurisé", cet utilisateur ne peut pas sélectionner / insérer / mettre à jour / supprimer des données, ce qui ne leur est pas destiné).

Ce que je ne sais pas, c'est comment accéder aux données avec des filtres dynamiques.

J'utilise MSSQL 2005.

Si j'ai un tableau:

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)
)

alors je devrais créer 4 procédures stockées (créer / lire / mettre à jour / supprimer).

La procédure stockée pour " create " est facile.

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

La procédure stockée pour " supprimer " est facile aussi.

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

La procédure stockée pour " mettre à jour " est similaire à "supprimer", mais je ne suis pas sûr que ce soit la bonne façon de procéder. Je pense que la mise à jour de toutes les colonnes n’est pas efficace.

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

Et la dernière procédure stockée pour "lu". est un mystère littlebit pour moi. Comment passer les valeurs de filtre pour des conditions complexes? J'ai quelques suggestions:

Utilisation du paramètre XML pour passer la condition where:

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

Mais, je pense que la limitation de & 4000; " les caractères d'une requête sont laids.

La suggestion suivante consiste à utiliser des tables de filtrage pour chaque colonne. Insérez les valeurs de filtre dans la table de filtres, puis appelez la procédure stockée avec l'ID des filtres:

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

Mais cette suggestion est un peu compliquée, je pense.

Existe-t-il des "meilleures pratiques"? faire ce type de procédures stockées?

Était-ce utile?

La solution

Premièrement: pour votre routine de suppression, votre clause where ne devrait inclure que la clé primaire.

Deuxièmement: pour votre routine de mise à jour, n'essayez pas d'optimiser avant d'avoir un code fonctionnel. En fait, n'essayez pas d'optimiser avant d'avoir pu profiler votre application et voir où se trouvent les goulots d'étranglement. Je peux vous assurer que la vitesse de mise à jour d'une colonne d'une ligne et de toutes les colonnes d'une ligne est identique. Ce qui prend du temps dans un SGBD est (1) de trouver le bloc de disque sur lequel vous allez écrire les données et (2) de verrouiller d’autres graveurs afin que votre écriture soit cohérente. Enfin, l'écriture du code nécessaire pour mettre à jour uniquement les colonnes devant être modifiées sera généralement plus difficile à effectuer et à gérer. Si vous voulez vraiment être pointilleux, il vous faudra comparer la vitesse de détermination des colonnes modifiées par rapport à la simple mise à jour de chaque colonne. Si vous les mettez tous à jour, vous ne devez en lire aucun.

Troisièmement: j’ai tendance à écrire une procédure stockée pour chaque chemin d’extraction. Dans votre exemple, je créerais une clé primaire, une clé étrangère, puis une pour chaque nouveau chemin d'accès, car j'en avais besoin dans l'application. Être agile; n'écris pas le code dont tu n'as pas besoin. Je suis également d'accord avec l'utilisation de vues au lieu de procédures stockées. Toutefois, vous pouvez utiliser une procédure stockée pour renvoyer plusieurs ensembles de résultats (dans certaines versions de MSSQL) ou pour modifier les lignes en colonnes, ce qui peut être utile.

Si vous devez obtenir, par exemple, 7 lignes par clé primaire, vous avez quelques options. Vous pouvez appeler la procédure stockée qui obtient une ligne par clé primaire sept fois. Cela peut être assez rapide si vous gardez la connexion ouverte entre tous les appels. Si vous savez que vous n’avez jamais besoin de plus d’un certain nombre (disons 10) d’ID à la fois, vous pouvez écrire une procédure stockée comportant une clause where, telle que " et ID in (arg1, arg2, arg3 ...) " et assurez-vous que les arguments non utilisés sont définis sur NULL. Si vous décidez de générer du SQL dynamique, je ne m'embarrasserai pas d'une procédure stockée, car TSQL est aussi facile à commettre une erreur que n'importe quel autre langage. En outre, vous ne tirez aucun avantage de la base de données utilisée pour la manipulation de chaînes. C’est presque toujours votre goulot d’étranglement; il est donc inutile de donner à la base de données plus de travail que nécessaire.

Autres conseils

Pour lire des données, vous n'avez pas besoin d'une procédure stockée pour la sécurité ou pour séparer la logique, vous pouvez utiliser des vues.

Il suffit d’accorder uniquement une sélection dans la vue.

Vous pouvez limiter les enregistrements affichés, modifier les noms de champs, joindre plusieurs tables en un seul "tableau" logique, etc.

.

Je ne suis pas d'accord pour dire que la création de procédures stockées Insertion / Mise à jour / Sélectionner constitue une "meilleure pratique". Sauf si votre application est entièrement écrite en SP, utilisez une couche de base de données dans votre application pour gérer ces activités CRUD. Mieux encore, utilisez une technologie ORM pour les gérer à votre place.

Je suggère que vous n'essayiez pas de créer une procédure stockée faisant tout ce que vous pourriez maintenant ou jamais avoir besoin de faire. Si vous devez extraire une ligne en fonction de la clé primaire de la table, écrivez une procédure stockée à cet effet. Si vous devez rechercher toutes les lignes répondant à un ensemble de critères, recherchez-les et écrivez une procédure stockée à cet effet.

Si vous essayez d'écrire un logiciel qui résout tous les problèmes possibles plutôt qu'un ensemble spécifique de problèmes, vous ne pourrez généralement rien fournir d'utile.

Votre procédure stockée select peut être effectuée comme suit pour ne nécessiter qu'un seul proc stocké mais un nombre quelconque d'éléments différents dans la clause where. Passez n'importe lequel des paramètres ou une combinaison de ceux-ci et vous obtiendrez TOUS les articles qui correspondent. Vous n'avez donc besoin que d'un seul processus.

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))

Dans SQL 2005, il prend en charge nvarchar (max), qui est limité à 2G, mais accepte pratiquement toutes les opérations sur les chaînes avec nvarchar normal. Vous voudrez peut-être vérifier si cela peut correspondre à ce dont vous avez besoin lors de la première approche.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top