Sql server SQL Server在where子句中包含搜索筛选器列
我有一个SQL Server数据库,其中有一个包含客户数据的Person表。在我的web应用程序中,弹出式搜索框jqGrid允许用户指定搜索中使用的字段/过滤器以及操作和数据。因此,字段类似于名称、上次访问日期等。这些字段映射到Person表,尽管并非所有字段都与列名匹配,并且操作都包含、以开头、以结尾、大于等。每个字段都作为两个字母的代码传入,如“cn”、“bw”、“ew”、“gt”。总共有15个不同的字段可以进行筛选,12个操作 表中大约有50列,所以我在这里只列出了一些:Sql server SQL Server在where子句中包含搜索筛选器列,sql-server,tsql,stored-procedures,Sql Server,Tsql,Stored Procedures,我有一个SQL Server数据库,其中有一个包含客户数据的Person表。在我的web应用程序中,弹出式搜索框jqGrid允许用户指定搜索中使用的字段/过滤器以及操作和数据。因此,字段类似于名称、上次访问日期等。这些字段映射到Person表,尽管并非所有字段都与列名匹配,并且操作都包含、以开头、以结尾、大于等。每个字段都作为两个字母的代码传入,如“cn”、“bw”、“ew”、“gt”。总共有15个不同的字段可以进行筛选,12个操作 表中大约有50列,所以我在这里只列出了一些: CREATE T
CREATE TABLE [dbo].[Person]
(
[PersonId] [int] IDENTITY(1,1) NOT NULL,
[PersonTypeId] [int] NULL,
[Firstname] [nvarchar](200) NULL,
[Middlename] [nvarchar](200) NULL,
[Lastname] [nvarchar](200) NULL,
[Name] [nvarchar](200) NULL,
)
INSERT INTO [dbo].[Person]([PersonTypeId],[Firstname],[Middlename],[Lastname],[Name])
VALUES (1, 'James', 'Joseph', 'Martin', ''),
(1, 'Jim', 'Joseph', 'Martyn', ''),
(1, 'James', 'John', 'Martine', ''),
(2, 'James', 'Sean', 'Martin', 'Martin & Co'),
(2, 'John', 'Joseph', 'Martin', 'Martin & Martin')
我向存储过程中添加了一个表值参数,以传递搜索条件,该条件工作良好,因此现在在搜索存储过程中,我有一个udt,其中包含用户输入的所有字段/操作/数据
CREATE TYPE [dbo].[udt_Filter] AS TABLE
(
[Field] [nvarchar](50),
[Op] [nvarchar](50),
[Data] [nvarchar](50)
)
通过udt传入的过滤器如下所示:
Field Op Data
'Name', 'bw', 'Mart'
'LastVisit', 'gt', '01.07.2017'
现在我想知道在搜索中包含过滤器的最佳方式是什么。我知道我可以使用动态SQL使其工作,但如果可能的话,我更喜欢使用联接,但是我看不到任何将字段名筛选器与Person表中的数据联接的方法
ALTER PROCEDURE [dbo].[pr_GetPersons]
(
@Filters dbo.udt_Filter READONLY,
@Operation nvarchar(3) -- AND or OR
)
AS
BEGIN
SELECT [PersonTypeId], [Firstname], [Middlename], [Lastname], [Name]
FROM [dbo].[Person]
WHERE PersonTypeId IN (1, 2, 3)
-- @Operation can I somehow join to @Filters and filter the Select result using each row in @Filters??
END
所有过滤条件都与AND或or组合,AND或or作为@操作传递给存储过程 不可能通过Person表和@Filters表参数之间的联接来解决此动态筛选问题。如果没有动态SQL,使用15个字段和12个运算符,那么最终将得到一个怪物where子句。我怀疑存储过程在这种情况下是否能正常运行 相反,我建议使用动态SQL。以下是一个可以用作起点的存储过程:
CREATE PROCEDURE [dbo].[pr_GetPersons]
(@Filters dbo.udt_Filter READONLY)
AS
BEGIN
DECLARE
@SelectList nvarchar(max),
@WhereClause nvarchar(max),
@SqlCommand nvarchar(max)
SET @SelectList = ISNULL(STUFF((SELECT ', ' + QUOTENAME(Field) AS [text()] FROM @Filters FOR XML PATH('')), 1, 1, ''), '*')
SET @WhereClause = STUFF((
SELECT
' AND ' +
-- Translate opcodes into SQL Server expressions.
CASE Op
WHEN 'cn' THEN QUOTENAME(Field) + ' LIKE ' + QUOTENAME('%' + Data + '%', '''')
WHEN 'bw' THEN QUOTENAME(Field) + ' LIKE ' + QUOTENAME(Data + '%', '''')
WHEN 'ew' THEN QUOTENAME(Field) + ' LIKE ' + QUOTENAME('%' + Data, '''')
WHEN 'gt' THEN QUOTENAME(Field) + '>' + QUOTENAME(Data, '''')
WHEN 'lt' THEN QUOTENAME(Field) + '<' + QUOTENAME(Data, '''')
END AS [text()]
FROM
@Filters
FOR XML PATH(''), type).value('.', 'nvarchar(max)'),
1, 5, '')
SET @SqlCommand = 'SELECT ' + @SelectList + ' FROM dbo.Person WHERE PersonTypeId IN (1, 2, 3)' + ISNULL(NULLIF(' AND ', @WhereClause) + @WhereClause, '')
EXEC(@SqlCommand)
END
它只返回筛选器中的列。如果希望它返回表中的所有列,而不管过滤器中有什么,请将@SelectList变量设置为*
所有筛选准则都与AND运算符联接。在@Fields变量中,没有指示如何组合它们。所以我想和运算符就是所需要的
解释@Filters表中的操作代码以生成where子句的CASE表达式可以移动到它自己的函数中。QUOTENAMEData,用于在数据为字符串且包含单引号的情况下形成正确的字符串
该过程不会检查Person表中是否存在筛选器字段。应特别注意筛选值数据,尤其是日期/时间和十进制类型,因为在形成where子句或检查筛选值类型是否与Person表中的列类型匹配时,没有类型转换,所有筛选值都用作字符串
ALTER PROCEDURE [dbo].[pr_GetPersons]
(
@Filters dbo.udt_Filter READONLY,
@Operation nvarchar(3) -- AND or OR
)
AS
BEGIN
SELECT [PersonTypeId], [Firstname], [Middlename], [Lastname], [Name]
FROM [dbo].[Person]
WHERE PersonTypeId IN (1, 2, 3)
-- @Operation can I somehow join to @Filters and filter the Select result using each row in @Filters??
END
希望有帮助。不可能通过Person表和@Filters表参数之间的联接来解决此动态筛选问题。如果没有动态SQL,使用15个字段和12个运算符,那么最终将得到一个怪物where子句。我怀疑存储过程在这种情况下是否能正常运行 相反,我建议使用动态SQL。以下是一个可以用作起点的存储过程:
CREATE PROCEDURE [dbo].[pr_GetPersons]
(@Filters dbo.udt_Filter READONLY)
AS
BEGIN
DECLARE
@SelectList nvarchar(max),
@WhereClause nvarchar(max),
@SqlCommand nvarchar(max)
SET @SelectList = ISNULL(STUFF((SELECT ', ' + QUOTENAME(Field) AS [text()] FROM @Filters FOR XML PATH('')), 1, 1, ''), '*')
SET @WhereClause = STUFF((
SELECT
' AND ' +
-- Translate opcodes into SQL Server expressions.
CASE Op
WHEN 'cn' THEN QUOTENAME(Field) + ' LIKE ' + QUOTENAME('%' + Data + '%', '''')
WHEN 'bw' THEN QUOTENAME(Field) + ' LIKE ' + QUOTENAME(Data + '%', '''')
WHEN 'ew' THEN QUOTENAME(Field) + ' LIKE ' + QUOTENAME('%' + Data, '''')
WHEN 'gt' THEN QUOTENAME(Field) + '>' + QUOTENAME(Data, '''')
WHEN 'lt' THEN QUOTENAME(Field) + '<' + QUOTENAME(Data, '''')
END AS [text()]
FROM
@Filters
FOR XML PATH(''), type).value('.', 'nvarchar(max)'),
1, 5, '')
SET @SqlCommand = 'SELECT ' + @SelectList + ' FROM dbo.Person WHERE PersonTypeId IN (1, 2, 3)' + ISNULL(NULLIF(' AND ', @WhereClause) + @WhereClause, '')
EXEC(@SqlCommand)
END
它只返回筛选器中的列。如果希望它返回表中的所有列,而不管过滤器中有什么,请将@SelectList变量设置为*
所有筛选准则都与AND运算符联接。在@Fields变量中,没有指示如何组合它们。所以我想和运算符就是所需要的
解释@Filters表中的操作代码以生成where子句的CASE表达式可以移动到它自己的函数中。QUOTENAMEData,用于在数据为字符串且包含单引号的情况下形成正确的字符串
该过程不会检查Person表中是否存在筛选器字段。应特别注意筛选值数据,尤其是日期/时间和十进制类型,因为在形成where子句或检查筛选值类型是否与Person表中的列类型匹配时,没有类型转换,所有筛选值都用作字符串
ALTER PROCEDURE [dbo].[pr_GetPersons]
(
@Filters dbo.udt_Filter READONLY,
@Operation nvarchar(3) -- AND or OR
)
AS
BEGIN
SELECT [PersonTypeId], [Firstname], [Middlename], [Lastname], [Name]
FROM [dbo].[Person]
WHERE PersonTypeId IN (1, 2, 3)
-- @Operation can I somehow join to @Filters and filter the Select result using each row in @Filters??
END
希望有帮助。过滤器总是与and、or或用户指定的选项结合使用吗?一些建议的背景阅读是。嗨@HABO-我没有提到所有的语句都与“或”和“或”结合,与第一句WHERE PersonTypeId结合在一起。你如何处理子表达式,例如A和B或C,notD或e,而不是F?不清楚如何在字段/Op/Data中表示,更不用说和/或了。@HABO没有任何子查询-所有筛选条件都将与and或or组合。这是使用操作参数确定的,因此Op可以大于或包含?对于年龄>=65且姓氏如“%adelia”或ShoeSize包含“EEE”,筛选表将包含什么内容?如果需要,请随意选择。过滤器是否总是与and、or或用户指定的选项结合使用?一些建议
ted背景阅读是。Hi@HABO-我没有提到所有的语句都与AND或or结合,与第一句WHERE PersonTypeId结合在一起。你如何处理子表达式,例如A和B或C和notD或e而不是F?不清楚如何在字段/Op/Data中表示,更不用说和/或了。@HABO没有任何子查询-所有筛选条件都将与and或or组合。这是使用操作参数确定的,因此Op可以大于或包含?对于年龄>=65且姓氏如“%adelia”或ShoeSize包含“EEE”,筛选表将包含什么内容?如果需要,请随时联系。