L'une des «meilleures pratiques» consiste à accéder aux données via des procédures stockées. Je comprends pourquoi ce scénario est bon. Ma motivation est de diviser la base de données et la logique applicative (les tables peuvent être changées, si le comportement des procédures stockées est identique), défense pour l'injection SQL (les utilisateurs ne peuvent pas exécuter "select * from some_tables" et la sécurité (dans la procédure stockée peut être "n'importe quoi" qui sécurisé, cet utilisateur ne peut pas sélectionner/insérer/mettre à jour/supprimer des données, ce qui n'est pas pour eux).Accès aux données avec des procédures stockées
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 le 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 devrait créer 4 procédures stockées (création/lecture/mise à jour/supprimer).
La procédure stockée pour "créer" 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 également facile.
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 « mise à jour » est similaire pour « supprimer », mais je ne suis pas sûr que ce soit la bonne façon, comment le faire. 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 "lire" est un petit mystère pour moi. Comment passer les valeurs de filtre pour des conditions complexes? J'ai quelques suggestions:
l'aide du paramètre XML pour passer où la condition:
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 des caractères « 4000 » pour une requête est laid.
La suggestion suivante consiste à utiliser des tables de filtres pour chaque colonne. Insérer des valeurs de filtre dans la table de filtre, puis appeler la procédure stockée avec ID de 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 littlebit compliqué je pense.
Existe-t-il une «meilleure pratique» pour effectuer ce type de procédures stockées?
Première: Vous avez propably vérité. La vérification pour "un autre utilisateur a changé des données après que vous ayez reçu des données et avant que vous ayez sauvegardé des données" ne vaut rien pour la suppression. Deuxièmement: je pense à la mise à jour d'une grande colonne. Comme "ntext". Dans certains cas, il peut y avoir un texte RTF avec des images. – TcKs
Troisième: Quand je veux obtenir 7 lignes de tblProduct. Est-ce bon moyen d'appeler "Read_Product" 7 fois avec divers "@ProductID" valeur ou devrais-je faire quelque chose d'autre? – TcKs
Pour ntext, les données sont souvent stockées dans un bloc différent du reste de la ligne, donc oui, dans ce cas, vous pouvez avoir une procédure stockée dans laquelle vous ne passez pas la colonne ntext. Cependant, comparer une valeur ntext à une valeur passée sera plus lent que simplement écraser. –