MS SQL:如果不为空,请选择其许多日期列都在范围内的行



我有一个我认为相当复杂的查询要做,但还没有找到解决方案,我想知道是否可以通过复杂的查询来完成,或者我需要对结果进行后处理。

我有一个预算表,带有预算代码和几个日期,如enterDate、deliveryDate、desiredDeliveryDate、shippingDate等。

某些日期可以为空。其他可以设置。

我需要一个查询来返回任何日期都在日期范围间隔内的预算行。但我不想考虑日期是空值。更具体地说,我将举一个例子。

BudgetID  |  D1  |  D2  |  D3  |  D5  | 
a         | NULL | NULL | NULL | NULL |
b         | NULL |  IR  |  OR  |  OR  |
c         | NULL | NULL | NULL |  IR  |
d         |  OR  | NULL | NULL | NULL |

NULL当然意味着该值为NULL。IR表示日期在范围内。OR表示日期超出范围。

结果应该如下:

BudgetID  |  D1  |  D2  |  D3  |  D5  | 
b         | NULL |  IR  |  OR  |  OR  |
c         | NULL | NULL | NULL |  IR  |

预算a不匹配,因为所有日期列都为NULL。预算b不匹配,因为范围内至少有一个NOT NULL Date。预算案c确实因为avobe的同样原因而配对。预算d不匹配,因为所有not NULL日期都超出范围。

提前谢谢。

编辑:

考虑到创建查询的性质,我想到了一个可能的解决方案。因为我事先知道范围,所以我使用字符串参数化地构造查询。在C#格式中,我决定在与范围进行比较之前,使用每个date列的日期值在范围之外的ISNULL。这会使NULL日期总是失败。

试试这个:

SELECT BudgetId, D1, D2, D4, D4, D5
FROM YourTable
WHERE  ISNULL(D1, DATEADD(DD, 1, @max)) BETWEEN @min AND @max
OR ISNULL(D2, DATEADD(DD, 1, @max)) BETWEEN @min AND @max
OR ISNULL(D3, DATEADD(DD, 1, @max)) BETWEEN @min AND @max
OR ISNULL(D4, DATEADD(DD, 1, @max)) BETWEEN @min AND @max
OR ISNULL(D5, DATEADD(DD, 1, @max)) BETWEEN @min AND @max

根据NULL在这四个DT列中的分布和/或这些条件的选择性,我将使用以下查询之一:

SELECT  b.BudgetID 
FROM    dbo.Budget b
WHERE   b.D1 BETWEEN @a AND @b
OR      b.D2 BETWEEN @a AND @b
OR      b.D3 BETWEEN @a AND @b
OR      b.D4 BETWEEN @a AND @b

--或

SELECT  DISTINCT b.BudgetID 
FROM    dbo.Budget b
CROSS APPLY (
SELECT D1 WHERE D1 IS NOT NULL
UNION ALL 
SELECT D2 WHERE D2 IS NOT NULL
UNION ALL 
SELECT D3 WHERE D3 IS NOT NULL
UNION ALL 
SELECT D4 WHERE D4 IS NOT NULL
) ca (AllDt)
WHERE ca.AllDt BETWEEN @a AND @b

--或

SELECT  b.BudgetID 
FROM    dbo.Budget b
WHERE   b.D1 BETWEEN @a AND @b
UNION -- UNION removes duplicated values
SELECT  b.BudgetID 
FROM    dbo.Budget b
WHERE   b.D2 BETWEEN @a AND @b
UNION 
SELECT  b.BudgetID 
FROM    dbo.Budget b
WHERE   b.D3 BETWEEN @a AND @b
UNION 
SELECT  b.BudgetID 
FROM    dbo.Budget b
WHERE   b.D4 BETWEEN @a AND @b

这可能适用于您的情况。。

CREATE TABLE #temp   (BudgetID  VARCHAR(3),  D1 DATETIME ,  D2 DATETIME ,  D3 DATETIME ,  D5 DATETIME ) 
INSERT #temp (BudgetID  ,  D1  ,  D2  ,  D3  ,  D5  ) 
VALUES
('a' , NULL , NULL , NULL , NULL ),
('b' , NULL , 'Apr 01 2016 12:00:00:000AM',  'May 30 2016 12:00:00:000AM', 'Jun 10 2016 12:00:00:000AM' ),
('c' , NULL , NULL , NULL , 'Apr 15 2016 12:00:00:000AM' ),
('d' ,  'Jan 30 2016 12:00:00:000AM', NULL , NULL , NULL )
/*INSERT #temp (BudgetID  ,  D1  ,  D2  ,  D3  ,  D5  ) 
VALUES('e' , 'Apr 30 2016 12:00:00:000AM' , 'Apr 10 2016 12:00:00:000AM' , NULL , NULL )*/
SELECT BudgetID,D1 as DATE FROM (
select BudgetID,D1 from #temp
UNION
select BudgetID,D2 from #temp
UNION
select BudgetID,D3 from #temp
UNION 
select BudgetID,D5 from #temp
)a WHERE a.D1 between '2016-04-01' AND '2016-04-30' 
--(OR)
SELECT BudgetID,ColumnName,DATES
FROM
(
SELECT ROW_NUMBER () OVER(ORDER BY (BudgetID) ) Row_NO,BudgetID , D1  ,  D2  ,  D3  ,  D5    
FROM #TEMP
) AS a
UNPIVOT
(DATES FOR ColumnName IN ( D1  ,  D2  ,  D3  ,  D5  )) AS up
WHERE DATES between '2016-04-01' AND '2016-04-30' 

但需要看到另一个场景。。。如果同一行有多个日期,并且日期在给定范围内,则它将返回多行(要查看此场景,只需取消注释并将insertbudjetID'e'行放入临时表中)。如果您的要求只是找到预算ID,那么只需选择distinct budgetID

最新更新