Wie erstelle ich eine gespeicherte Prozedur, die Suche Spalten optional wird?
-
03-07-2019 - |
Frage
Ich arbeite an einer Anwendung für die Arbeit, die unsere Mitarbeiter Datenbank abzufragen wird. Die Endbenutzer wollen, dass die Fähigkeit, auf der Basis des Standardnamen / Abteilung Kriterien suchen, aber sie wollen auch die Flexibilität für alle Menschen mit dem Vornamen „James“ abzufragen, die in der Abteilung Gesundheit arbeitet. Das einzige, was ich vermeiden wollen, ist einfach die gespeicherte Prozedur eine Liste von Parametern treffen und eine SQL-Anweisung zu generieren, auszuführen, da die Türen zu SQL-Injection auf interner Ebene öffnen würde.
Kann dies geschehen?
Lösung
Während der COALESCE
Trick ordentlich ist, meine bevorzugte Methode ist:
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 + '%')
Diese Art von SPs leicht Code erzeugt werden kann (und erneut generiert für Tabellenänderungen).
Sie haben ein paar Optionen für den Umgang mit Zahlen - je nachdem, ob Sie genaue Semantik oder Such Semantik wollen
.Andere Tipps
Der effizienteste Weg, um diese Art der Suche zu implementieren, ist mit einer gespeicherten Prozedur. Die folgende Anweisung erstellt eine Prozedur, die die erforderlichen Parameter akzeptiert. Wenn ein Parameterwert nicht angegeben wird es auf NULL gesetzt wird.
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)
auf dieser Seite genommen: http://www.sqlteam.com / article / Implementierung-a-dynamic-where-Klausel
Ich habe es vorher getan. Es funktioniert gut.
Erland Sommarskog des Artikel dynamische Suchbedingungen in T-SQL ist eine gute Referenz auf, wie dies zu tun. Erland präsentiert eine Reihe von Strategien, wie dies zu tun, ohne Verwendung von dynamischem SQL (einfach nur IF-Blöcke, OR, COALESCE, usw.) und sogar listet die Leistungsmerkmale jeder Technik aus.
Falls Sie haben den sauren Apfel beißen und die dynamische SQL-Weg zu gehen, sollten Sie auch Erland Fluch lesen und Segen von dynamischen SQL wo er einige Tipps, wie Sie gibt, um richtig dynamische SQLs schreiben
Es kann getan werden, aber in der Regel diese Küchenspüle Verfahren führen in einigen armen Abfragepläne.
Nachdem alles gesagt, dass, hier ist die Taktik am häufigsten für „optional“ Parameter verwendet. Der normale Ansatz ist NULL zu behandeln, als „weggelassen“.
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: Ein weitaus besserer Ansatz wäre parametrisierte Abfragen sein. Hier ist eine Blog-Post von einem der weltweit führenden Autoritäten auf diesem Gebiet, Frans Bouma von LLBLGen Pro Ruhm:
die COALESCE-Methode verwendet, hat ein Problem, dass, wenn Ihre Spalte einen NULL-Wert hat, in einer NULL-Suchbedingung vorbei (dh die Suchbedingung ignoriert) wird die Zeile nicht in vielen Datenbanken zurück.
Zum Beispiel, versuchen Sie den folgenden Code auf 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
Sie werden erst wieder zwei Reihen erhalten, weil in den Zeilen, in denen die Spalte my_string NULL Sie sind wirksam bekommen:
my_string = COALESCE(@my_string, my_string) =>
my_string = COALESCE(NULL, my_string) =>
my_string = my_string =>
NULL = NULL
Aber natürlich NULL ist nicht gleich NULL.
Ich versuche, mit zu halten:
SELECT
my_id,
my_string
FROM
dbo.Test_Coalesce
WHERE
(@my_string IS NULL OR my_string = @my_string)
Natürlich können Sie einstellen, dass Wildcards zu verwenden, oder was auch immer Sie tun mögen.
Das Kopieren dieses von meiner Blog-Post:
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
Wir Generisches @Search Parameter verwenden und jeden Wert, um es passieren für die Suche.
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
Mein erster Gedanke war, eine Abfrage, so etwas zu schreiben ...
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
)
... die dann müsste der Anrufer eine Bit-Flag bereitstellen, wenn sie ein bestimmtes Feld gesucht werden soll und dann den Wert liefern, wenn sie danach suchen, aber ich weiß nicht, ob dies eine schlampige WHERE zu schaffen Klausel oder wenn ich mit einer CASE-Anweisung in der WHERE-Klausel weg erhalten kann.
Wie Sie dieser bestimmte Code in T-SQL sehen ist, aber ich werde schauen gerne irgend PL-SQL / MySQL-Code als auch und entsprechend anpassen.
würde ich mit der NULL / COALESCE Methode über AdHoc Abfragen bleiben, und dann sicher, dass Sie keine Probleme haben, die Leistung zu machen testen.
Wenn es sich herausstellt, dass Sie langsam laufende Abfragen haben, weil es einen Table-Scan tut, wenn man auf Spalt ist, die indiziert sind, können Sie immer ergänzen die generische Suche gespeicherte Prozedur mit zusätzlich spezifisch denjenigen, die auf diesen indizierten Feldern ermöglichen die Suche . Zum Beispiel könnten Sie eine spezielle SP haben die Suche nach CustomerID der Fall ist, oder Last / Vorname.
eine Prozedur schreibt alle Mitarbeiterdaten, dessen Anfang mit A in Tabelle einfügen ??