Question

Je travaille sur une application pour le travail qui va interroger notre base de données d'employés. Les utilisateurs finaux souhaitent pouvoir effectuer des recherches sur la base des critères de nom / service standard, mais ils souhaitent également pouvoir interroger toutes les personnes portant le prénom "James". cela fonctionne dans le département de la santé. Ce que je veux éviter, c’est que la procédure stockée prenne une liste de paramètres et génère une instruction SQL à exécuter, ce qui ouvrirait la porte à l’injection SQL au niveau interne.

Cela peut-il être fait?

Était-ce utile?

La solution

Bien que l'astuce COALESCE soit simple, ma méthode préférée est:

CREATE PROCEDURE ps_Customers_SELECT_NameCityCountry
    @Cus_Name varchar(30) = NULL
    ,@Cus_City varchar(30) = NULL
    ,@Cus_Country varchar(30) = NULL
    ,@Dept_ID int = NULL
    ,@Dept_ID_partial varchar(10) = NULL
AS
SELECT Cus_Name
       ,Cus_City
       ,Cus_Country
       ,Dept_ID
FROM Customers
WHERE (@Cus_Name IS NULL OR Cus_Name LIKE '%' + @Cus_Name + '%')
      AND (@Cus_City IS NULL OR Cus_City LIKE '%' + @Cus_City + '%')
      AND (@Cus_Country IS NULL OR Cus_Country LIKE '%' + @Cus_Country + '%')
      AND (@Dept_ID IS NULL OR Dept_ID = @DeptID)
      AND (@Dept_ID_partial IS NULL OR CONVERT(varchar, Dept_ID) LIKE '%' + @Dept_ID_partial + '%')

Ce type de SP peut facilement être généré par un code (et régénéré pour les modifications de table).

Vous disposez de quelques options pour gérer les nombres. Vous pouvez choisir une sémantique exacte ou une sémantique de recherche.

Autres conseils

Le moyen le plus efficace d'implémenter ce type de recherche est d'utiliser une procédure stockée. L'instruction présentée ici crée une procédure qui accepte les paramètres requis. Lorsqu'une valeur de paramètre n'est pas fournie, elle est définie sur NULL.

CREATE PROCEDURE ps_Customers_SELECT_NameCityCountry
@Cus_Name varchar(30) = NULL,
@Cus_City varchar(30) = NULL,
@Cus_Country varchar(30) =NULL
AS
SELECT Cus_Name,
       Cus_City,
       Cus_Country
FROM Customers
WHERE Cus_Name = COALESCE(@Cus_Name,Cus_Name) AND
      Cus_City = COALESCE(@Cus_City,Cus_City) AND
      Cus_Country = COALESCE(@Cus_Country,Cus_Country)

Extrait de cette page: http://www.sqlteam.com / article / implementation-a-dynamic-where-clause

Je l'ai déjà fait Cela fonctionne bien.

L'article d'Erland Sommarskog Les conditions de recherche dynamique dans T-SQL constituent une bonne référence. sur comment faire cela. Erland présente un certain nombre de stratégies permettant de le faire sans utiliser de code SQL dynamique (blocs IF, OR, COALESCE, etc.) et répertorie même les caractéristiques de performance de chaque technique.

Au cas où vous auriez à mordre la balle et parcourir le chemin du SQL dynamique, vous devriez également lire le Curse d'Erland. et bénédictions de SQL dynamique où il donne quelques conseils sur la façon d'écrire correctement des SQL dynamiques

Cela peut être fait, mais ces procédures d’évier de cuisine génèrent généralement des plans de requête médiocres.

Cela dit, voici la tactique la plus couramment utilisée pour "facultatif". paramètres. L’approche normale consiste à traiter NULL comme étant "omis".

SELECT
  E.EmployeeID,
  E.LastName,
  E.FirstName
WHERE
  E.FirstName = COALESCE(@FirstName, E.FirstName) AND
  E.LastName = COALESCE(@LastName, E.LastName) AND
  E.DepartmentID = COALESCE(@DepartmentID, E.DepartmentID)

EDIT: Une approche bien meilleure serait des requêtes paramétrées. Voici un article de blog d'une des plus grandes autorités mondiales dans ce domaine, Frans Bouma de LLBLGen Pro fame:

Procédures stockées et requêtes dynamiques

L'utilisation de la méthode COALESCE pose un problème, car si votre colonne a une valeur NULL, le fait de passer une condition de recherche NULL (ce qui signifie ignorer la condition de recherche) ne renverra pas la ligne dans de nombreuses bases de données.

Par exemple, essayez le code suivant sur SQL Server 2000:

CREATE TABLE dbo.Test_Coalesce (
    my_id   INT NOT NULL IDENTITY,
    my_string   VARCHAR(20) NULL )
GO
INSERT INTO dbo.Test_Coalesce (my_string) VALUES (NULL)
INSERT INTO dbo.Test_Coalesce (my_string) VALUES ('t')
INSERT INTO dbo.Test_Coalesce (my_string) VALUES ('x')
INSERT INTO dbo.Test_Coalesce (my_string) VALUES (NULL)
GO
DECLARE @my_string  VARCHAR(20)
SET @my_string = NULL
SELECT * FROM dbo.Test_Coalesce WHERE my_string = COALESCE(@my_string, my_string)
GO

Vous ne récupérerez que deux lignes car dans les lignes où la colonne my_string est NULL, vous obtenez efficacement:

my_string = COALESCE(@my_string, my_string) =>
my_string = COALESCE(NULL, my_string) =>
my_string = my_string =>
NULL = NULL

Mais bien entendu, NULL n'est pas égal à NULL.

J'essaie de rester avec:

SELECT
     my_id,
     my_string
FROM
     dbo.Test_Coalesce
WHERE
     (@my_string IS NULL OR my_string = @my_string)

Bien sûr, vous pouvez ajuster cela en utilisant des caractères génériques ou tout ce que vous voulez faire.

Copier ceci à partir de mon article de blog:

USE [AdventureWorks]
GO

CREATE PROCEDURE USP_GET_Contacts_DynSearch
(
    -- Optional Filters for Dynamic Search
    @ContactID          INT = NULL, 
    @FirstName          NVARCHAR(50) = NULL, 
    @LastName           NVARCHAR(50) = NULL, 
    @EmailAddress       NVARCHAR(50) = NULL, 
    @EmailPromotion     INT = NULL, 
    @Phone              NVARCHAR(25) = NULL
)
AS
BEGIN
    SET NOCOUNT ON

    DECLARE
        @lContactID         INT, 
        @lFirstName         NVARCHAR(50), 
        @lLastName          NVARCHAR(50), 
        @lEmailAddress      NVARCHAR(50), 
        @lEmailPromotion    INT, 
        @lPhone             NVARCHAR(25)

    SET @lContactID         = @ContactID
    SET @lFirstName         = LTRIM(RTRIM(@FirstName))
    SET @lLastName          = LTRIM(RTRIM(@LastName))
    SET @lEmailAddress      = LTRIM(RTRIM(@EmailAddress))
    SET @lEmailPromotion    = @EmailPromotion
    SET @lPhone             = LTRIM(RTRIM(@Phone))

    SELECT
        ContactID, 
        Title, 
        FirstName, 
        MiddleName, 
        LastName, 
        Suffix, 
        EmailAddress, 
        EmailPromotion, 
        Phone
    FROM [Person].[Contact]
    WHERE
        (@lContactID IS NULL OR ContactID = @lContactID)
    AND (@lFirstName IS NULL OR FirstName LIKE '%' + @lFirstName + '%')
    AND (@lLastName IS NULL OR LastName LIKE '%' + @lLastName + '%')
    AND (@lEmailAddress IS NULL OR EmailAddress LIKE '%' + @lEmailAddress + '%')
    AND (@lEmailPromotion IS NULL OR EmailPromotion = @lEmailPromotion)
    AND (@lPhone IS NULL OR Phone = @lPhone)
    ORDER BY ContactID

END
GO

Nous pouvons utiliser le paramètre @Search générique et lui transmettre toute valeur pour la recherche.

GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: --
-- Create date:
-- Description: --
-- =============================================
CREATE PROCEDURE [dbo].[usp_StudentList]
    @PageNumber INT    = 1, -- Paging parameter
    @PageSize   INT    = 10,-- Paging parameter
    @Search  VARCHAR(MAX) = NULL, --Generic Search Parameter
    @OrderBy VARCHAR(MAX) = 'FirstName', --Default Column Name 'FirstName' for records ordering
    @SortDir VARCHAR(MAX) = 'asc' --Default ordering 'asc' for records ordering
AS
BEGIN
    SET NOCOUNT ON;

    --Query required for paging, this query used to show total records
    SELECT COUNT(StudentId) AS RecordsTotal FROM Student

    SELECT Student.*, 
        --Query required for paging, this query used to show total records filtered
        COUNT(StudentId) OVER (PARTITION BY 1) AS RecordsFiltered 
    FROM Student
    WHERE 
    --Generic Search 
    -- Below is the column list to add in Generic Serach
    (@Search IS NULL OR Student.FirstName LIKE '%'+ @Search +'%')
    OR (@Search IS NULL OR Student.LastName LIKE '%'+ @Search +'%')
    --Order BY
    -- Below is the column list to allow sorting
    ORDER BY 
    CASE WHEN @SortDir = 'asc' AND @OrderBy = 'FirstName' THEN Student.FirstName END,
    CASE WHEN @SortDir = 'desc' AND @OrderBy = 'FirstName' THEN Student.FirstName  END DESC,
    CASE WHEN @SortDir = 'asc' AND @OrderBy = 'LastName' THEN Student.LastName END,
    CASE WHEN @SortDir = 'desc' AND @OrderBy = 'LastName' THEN Student.LastName  END DESC,
    OFFSET @PageSize * (@PageNumber - 1) ROWS FETCH NEXT @PageSize ROWS ONLY;
END

Ma première pensée a été d’écrire une requête comme celle-ci ...

SELECT EmpId, NameLast, NameMiddle, NameFirst, DepartmentName
  FROM dbo.Employee
       INNER JOIN dbo.Department ON dbo.Employee.DeptId = dbo.Department.Id
 WHERE IdCrq IS NOT NULL
       AND
       (
          @bitSearchFirstName = 0
          OR
          Employee.NameFirst = @vchFirstName
       )
       AND
       (
          @bitSearchMiddleName = 0
          OR
          Employee.NameMiddle = @vchMiddleName
       )
       AND
       (
          @bitSearchFirstName = 0
          OR
          Employee.NameLast = @vchLastName
       )
       AND
       (
          @bitSearchDepartment = 0
          OR
          Department.Id = @intDeptID
       )

... qui demanderait alors à l'appelant de fournir un indicateur de bit s'il souhaite effectuer une recherche dans un champ particulier, puis de fournir la valeur s'il souhaite le rechercher, mais je ne sais pas si cela crée un relâchement O clause ou si je peux sortir avec une déclaration CASE dans la clause WHERE.

Comme vous pouvez le constater, ce code est en T-SQL, mais je regarderai volontiers certains codes PL-SQL / MySQL et les adapterai en conséquence.

Je préférerais utiliser la méthode NULL / COALESCE sur les requêtes AdHoc, puis effectuer un test pour vous assurer que vous n’avez pas de problèmes de performances.

S'il s'avère que les requêtes sont lentes, car elles effectuent une analyse de table lorsque vous effectuez une recherche sur les colonnes indexées, vous pouvez toujours compléter la procédure stockée de recherche générique par des procédures spécifiques supplémentaires permettant la recherche dans ces champs indexés. . Par exemple, vous pouvez avoir un SP spécial qui effectue des recherches par ID client ou par nom / prénom.

Ecrivez une procédure pour insérer toutes les données d'employé dont le nom commence par A dans la table ??

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