SQL-查找列日期是否至少部分包含日期范围

时间:2018-06-20 09:39:23

标签: sql sql-server sql-server-2008

我需要创建一个报告,并且正在使用SQL脚本。 我要查询的表是company_status_history表,其中包含类似以下的条目(我不知道这些条目)

表company_status_history

列:

nextDueDate: Sequelize.VIRTUAL

数据:

| id | company_id | status_id | effective_date |

我想回答以下问题:“获取在01/01/2017到2017年12月31日这段时间内至少处于状态1的所有公司”

上面是我不知道如何处理的情况,因为我需要添加一些类型的逻辑:

  • “如果此行的状态为1,并且其日期早于日期范围,请检查下一行是否具有日期范围内的日期。”
  • “如果该行的状态为1,并且其日期在日期范围之后,请检查该行是否在日期范围内。”

4 个答案:

答案 0 :(得分:1)

尝试一下,不言自明。回答问题的这一部分:

  

我想回答以下问题:“让所有一直在   至少在时间段01/01/2017内处于状态1的某个点-   2017年12月31日”

  1. 如果您要查找状态为1的任何时刻的ID,并在请求的时期内有记录,则为这种情况:
      SELECT *
FROM company_status_history
WHERE id IN
    ( SELECT Id
     FROM company_status_history
     WHERE status_id=1 )
  AND effective_date BETWEEN '2017-01-01' AND '2017-12-31'
  1. 要在状态1和时段内查找ID的情况:
     SELECT *
FROM company_status_history
WHERE status_id=1
  AND effective_date BETWEEN '2017-01-01' AND '2017-12-31'

答案 1 :(得分:1)

也许这就是您想要的?对于此类问题,您需要连接表的两个实例,在这种情况下,我只是与Id的下一个记录进行连接,这可能并不完全正确。为了做得更好,您可以使用诸如row_number之类的窗口函数来创建新的ID,并根据您的要求标准对表进行排序

  

如果此行的状态为1,并且其日期早于日期范围检查   如果日期在日期范围内,则显示下一行

declare @range_st date = '2017-01-01'
declare @range_en date = '2017-12-31'

select 
  case
    when csh1.status_id=1 and csh1.effective_date<@range_st 
      then 
        case
          when csh2.effective_date between @range_st and @range_en then true
          else false
        end
    else NULL
  end
from company_status_history csh1
  left join company_status_history csh2
    on csh1.id=csh2.id+1

执行第二个条件:

  

”如果此行的状态为1,并且其日期在日期范围检查之后   如果日期在日期范围内,则该行之前的行。”

declare @range_st date = '2017-01-01'
declare @range_en date = '2017-12-31'

select 
  case
    when csh1.status_id=1 and csh1.effective_date<@range_st 
      then 
        case
          when csh2.effective_date between @range_st and @range_en then true
          else false
        end
    when csh1.status_id=1 and csh1.effective_date>@range_en 
      then 
        case
          when csh3.effective_date between @range_st and @range_en then true
          else false
        end
    else null -- ¿?
  end
from company_status_history csh1
  left join company_status_history csh2
    on csh1.id=csh2.id+1
  left join company_status_history csh3
    on csh1.id=csh3.id-1

答案 2 :(得分:1)

我建议使用cte和窗口函数ROW_NUMBER。这样您可以找到所需的记录。一个例子:

DECLARE @t TABLE(
  id INT
  ,company_id INT
  ,status_id INT
  ,effective_date DATETIME
)

INSERT INTO @t VALUES
(1, 10, 1, '2016-12-30 00:00:00.000')
,(2, 10, 5, '2017-02-04 00:00:00.000')
,(3, 11, 5, '2017-06-05 00:00:00.000')
,(4, 11, 1, '2018-04-30 00:00:00.000')


DECLARE @StartDate DATETIME = '2017-01-01';
DECLARE @EndDate DATETIME = '2017-12-31';

WITH cte AS(
SELECT *
      ,ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY effective_date) AS rn
  FROM @t
),
cteLeadLag AS(
SELECT c.*, ISNULL(c2.effective_date, c.effective_date) LagEffective, ISNULL(c3.effective_date, c.effective_date)LeadEffective
  FROM cte c
  LEFT JOIN cte c2 ON c2.company_id = c.company_id AND c2.rn = c.rn-1
  LEFT JOIN cte c3 ON c3.company_id = c.company_id AND c3.rn = c.rn+1
)
SELECT 'Included' AS RangeStatus, *
  FROM cteLeadLag
  WHERE status_id = 1
    AND effective_date BETWEEN @StartDate AND @EndDate
UNION ALL
SELECT 'Following' AS RangeStatus, *
  FROM cteLeadLag
  WHERE status_id = 1
    AND effective_date > @EndDate
    AND LagEffective BETWEEN @StartDate AND @EndDate
UNION ALL
SELECT 'Trailing' AS RangeStatus, *
  FROM cteLeadLag
  WHERE status_id = 1
    AND effective_date < @EndDate
    AND LeadEffective BETWEEN @StartDate AND @EndDate

我首先选择所有记录的前导日期和滞后日期,然后在所需的时间范围内对包含的内容进行检查。

答案 3 :(得分:1)

我认为这可以作为缺口和孤岛问题解决。考虑以下输入数据:(与OP的样本数据相同,外加两行)

id  company_id  status_id   effective_date
-------------------------------------------
1   10          1           2016-12-15
2   10          1           2016-12-30 
3   10          5           2017-02-04
4   10          4           2017-02-08
5   11          5           2017-06-05
6   11          1           2018-04-30

您可以使用以下查询:

SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
FROM company_status_history AS t
OUTER APPLY 
(
   SELECT COUNT(*) AS cnt
   FROM company_status_history AS c
   WHERE c.status_id = 1 
         AND c.company_id  = t.company_id 
         AND c.effective_date < t.effective_date
) AS x
ORDER BY company_id, effective_date

获得:

id  company_id  status_id   effective_date  grp
-----------------------------------------------
1   10          1           2016-12-15      0
2   10          1           2016-12-30      1
3   10          5           2017-02-04      2
4   10          4           2017-02-08      2
5   11          5           2017-06-05      0
6   11          1           2018-04-30      0

现在,您可以使用以下命令来识别status = 1个岛屿:

;WITH CTE AS 
(
    SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
    FROM company_status_history AS t
    OUTER APPLY 
    (
       SELECT COUNT(*) AS cnt
       FROM company_status_history AS c
       WHERE c.status_id = 1 
             AND c.company_id  = t.company_id 
             AND c.effective_date < t.effective_date
    ) AS x
)
SELECT id, company_id, status_id, effective_date,
       ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY effective_date) - 
       cnt AS grp
FROM CTE 

输出:

id  company_id  status_id   effective_date  grp
-----------------------------------------------
1   10          1           2016-12-15      1
2   10          1           2016-12-30      1
3   10          5           2017-02-04      1
4   10          4           2017-02-08      2
5   11          5           2017-06-05      1
6   11          1           2018-04-30      2

计算字段grp将帮助我们识别这些岛屿:

;WITH CTE AS 
(
    SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
    FROM company_status_history AS t
    OUTER APPLY 
    (
       SELECT COUNT(*) AS cnt
       FROM company_status_history AS c
       WHERE c.status_id = 1 
             AND c.company_id  = t.company_id 
             AND c.effective_date < t.effective_date
    ) AS x
), CTE2 AS 
(
   SELECT id, company_id, status_id, effective_date,
          ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY effective_date) - 
          cnt AS grp
   FROM CTE
)
SELECT company_id, 
       MIN(effective_date) AS start_date, 
       CASE 
          WHEN COUNT(*) > 1 THEN DATEADD(DAY, -1, MAX(effective_date))
          ELSE MIN(effective_date)
       END AS end_date
FROM CTE2 
GROUP BY company_id, grp
HAVING COUNT(CASE WHEN status_id = 1 THEN 1 END) > 0

输出:

company_id  start_date  end_date
-----------------------------------
10          2016-12-15  2017-02-03 
11          2018-04-30  2018-04-30 

所有您想知道的是那些从上方以指定间隔重叠的记录。

Demo here和更复杂的用例。