SQL Server 中带条件的 Where 子句
Posted
技术标签:
【中文标题】SQL Server 中带条件的 Where 子句【英文标题】:Where Clause with Conditions in SQL Server 【发布时间】:2015-10-20 19:08:11 【问题描述】:我有一个用于网站搜索页面的 SQL 存储过程,用户可以在其中搜索两个日期字段之间的记录:initiatedDateStart 和 InitialDateEnd。 SQL查询的逻辑应该如下:
如果 startDate 有值,但 endDate 为空,或者等于 startDate,返回在 startDate 上创建的记录。 如果 startDate 有值,并且 endDate 值大于 startDate,返回两者之间创建的记录 日期。由于我不是 SQL 专家,我的存储过程不是很优雅或聪明,但它确实返回了我期望它返回的记录,日期字段除外。我已经对此进行了研究,但语法和逻辑让我很困惑。这是我的存储过程的 sn-p。如果在 initStartDate texbox 中输入了一个值,但 initEndDate 为空,则返回在开始日期之后创建的所有记录。我很感激这方面的任何帮助。
DECLARE @initStartDate datetime = NULL
DECLARE @initEndDate datetime = NULL
SELECT DISTINCT
d.[DCRId],
d.[Title],
d.[FiscalYear]
FROM [dbo].[tblDCR] d
LEFT OUTER JOIN [dbo].[tblWorkflow] orig ON (d.[DCRId] = orig.[DCRId] AND orig.StepName = 'Draft')
WHERE 1 = 1
AND (orig.BadgeDate >= @initStartDate OR @initStartDate IS NULL)
AND (orig.BadgeDate <= @initEndDate OR @initEndDate IS NULL)
【问题讨论】:
【参考方案1】:我能想到的最好的处理方法是使用动态 sql
DECLARE @initStartDate datetime = NULL;
DECLARE @initEndDate datetime = NULL;
Declare @Sql NVARCHAR(MAX);
SET @Sql = N'SELECT DISTINCT
d.[DCRId],
d.[Title],
d.[FiscalYear]
FROM [dbo].[tblDCR] d
LEFT OUTER JOIN [dbo].[tblWorkflow] orig
ON (d.[DCRId] = orig.[DCRId] AND orig.StepName = ''Draft'')
WHERE 1 = 1 '
+ CASE WHEN @initStartDate IS NOT NULL THEN
N' AND orig.BadgeDate >= @initStartDate ' ELSE N'' END
+ CASE WHEN @initEndDate IS NOT NULL THEN
N' AND orig.BadgeDate <= @initEndDate ' ELSE N'' END
Exec sp_executesql @Sql
,N'@initStartDate datetime, @initEndDate datetime'
,@initStartDate
,@initEndDate
【讨论】:
M.Ali,谢谢。我确实考虑过动态SQL,但我从来没有使用过它,存储过程比这点复杂一点,所以我没有尝试实现动态SQL。如果没有动态 SQL,我将如何包含 CASE?【参考方案2】:我认为解决您的问题的方法是将当前日期分配给 end 参数,当它为空时。通过这样做,用户可以不输入参数并获取当前日期值,仅输入开始日期并获取开始日期的数据,或者输入结束日期并获取当前日期和结束日期之间的数据。最后,如果由于某种原因用户输入的开始日期高于结束日期,您可以告诉该过程仅返回今天的数据。
CREATE PROCEDURE test_proc
@initStartDate date = null,
@initEndDate date = null
AS
IF @initStartDate IS NULL AND @initEndDate IS NULL
BEGIN
SET @initStartDate = (SELECT GETDATE())
SET @initEndDate = @initStartDate
END
ELSE IF @initStartDate IS NULL AND @initEndDate IS NOT NULL
BEGIN
SET @initStartDate = @initEndDate
END
ELSE IF @initEndDate IS NULL AND @initStartDate IS NOT NULL
BEGIN
SET @initEndDate = @initStartDate
END
IF @initStartDate > @initEndDate
BEGIN
SET @initStartDate = (SELECT GETDATE())
SET @initEndDate = @initStartDate
END
SELECT DISTINCT
d.[DCRId],
d.[Title],
d.[FiscalYear]
FROM [dbo].[tblDCR] d
LEFT OUTER JOIN [dbo].[tblWorkflow] orig ON (d.[DCRId] = orig.[DCRId] AND orig.StepName = 'Draft')
WHERE orig.BadgeDate BETWEEN @initStartDate AND @initEndDate
GO
我修改的 SQL
DECLARE @initStartDate date = '10/21/15'
DECLARE @initEndDate date = NULL
IF @initStartDate IS NULL AND @initEndDate IS NOT NULL
BEGIN
SET @initStartDate = @initEndDate
END
ELSE IF @initEndDate IS NULL AND @initStartDate IS NOT NULL
BEGIN
SET @initEndDate = @initStartDate
END
SELECT DISTINCT
d.[DCRId],
d.[Title],
d.[FiscalYear]
FROM [dbo].[tblDCR] d
LEFT OUTER JOIN [dbo].[tblWorkflow] orig ON (d.[DCRId] = orig.[DCRId] AND orig.StepName = 'Draft')
WHERE 1 = 1
AND (orig.BadgeDate BETWEEN @initStartDate AND @initEndDate) OR (@initStartDate IS NULL OR @initEndDate IS NULL)
【讨论】:
dfundako,谢谢;我明白你的目标是什么。问题是如果用户不想按日期搜索,日期字段很可能为空。因此,如果任一日期字段中有值,则需要进行测试,然后返回在该特定日期创建的记录。如果两个日期字段都有值,则结果将在日期之间。 如果只为 initStart 或 initEnd 提供了一个日期,我已编辑查询以仅查看一个日期的记录。 dfundako,我在您的帖子中添加了修改后的 SQL,只要开始日期和结束日期不同,它就可以工作。由于某种原因,如果日期相同,BETWEEN 函数不会返回记录。日期也可能都为空;在这种情况下,用户不想按日期搜索,因此该字段不应返回任何内容。 我相信您的 BETWEEN 不起作用的原因是因为您将参数设置为 DATETIME 而不是 DATE。当您运行查找 2015-10-21 00:00:00 和 2015-10-21 00:00:00 之间的记录的 BETWEEN 语句时,它不会返回任何内容。但是,如果您在 2015-10-21 AND 2015-10-21 之间运行它,则无论附加时间戳如何,您都将在 2015-10-21 获得所有结果。 感谢您在这方面的坚持,dfundako,在您的帮助下,我终于能够让整个存储过程正常工作。【参考方案3】:但是如果@initStartDate 没有值怎么办? 假设如果没有 startDate 条件,您希望它一直返回,那么,将日期条件作为 Nullable 参数传递,就像您一样: 然后,(假设数据库字段只有日期,没有时间),使用以下 where 子句:
where orig.BadgeDate Between
Coalesce(@initStartDate, orig.BadgeDate) and
Coalesce(@initEndDate, @initStartDate, orig.BadgeDate)
【讨论】:
谢谢你,查尔斯。由于此存储过程返回搜索网页的结果,用户可能会或可能不会在搜索字段中输入任何值,因此将为任何未选择的选项传递空值。日期字段很可能为空。但是数据库中的 orig.BadgeDate 值不会为空。所以即使用户不想按日期搜索,Coalesce 也会始终返回一个值,是这样吗? Coalesce 将返回第一个不为空的参数,所以是的,如果最后一个参数是orig.BadgeDate
,那么合并将始终返回一个非空值,但这实际上是,与在那一端没有边界相同,因为orig.BadgeDate
将始终等于orig.BadgeDate
,以上是关于SQL Server 中带条件的 Where 子句的主要内容,如果未能解决你的问题,请参考以下文章
SQL HELP - 基于 BIT 变量的条件 where 子句 - SQL Server
为啥以及何时在 WHERE 子句中带有条件的 LEFT JOIN 不等于在 ON 中的相同 LEFT JOIN? [复制]