我有一个查询,它返回结果,包含混合了空值的日期. 我的目标是通过合并值从所有列中删除空值.

注意:确保DateFrom和DateTo两列中的日期数量相等.

目前,我从我的查询中得到了以下信息

Client_Id   DateFrom         DateTo          
----------- ---------------- ----------------
          9             NULL             NULL
          9       2023-11-23             NULL
          9             NULL             NULL
          9             NULL       2023-12-22
          9             NULL             NULL
          9       2024-01-05             NULL
          9             NULL       2024-01-12
          9       2024-01-13       2024-02-03

我想得到的是:

Client_Id   DateFrom         DateTo          
----------- ---------------- ----------------
          9       2023-11-23      2023-12-22
          9       2024-01-05      2024-01-12
          9       2024-01-13      2024-02-03

我不知道如何做到这一点.

下面是返回结果的数据集、表和SELECT.

CREATE SCHEMA [TestDoc]
CREATE TABLE [TestDoc].[Contracts]
(
[Id] Int NOT NULL IDENTITY(1,1),
[Type_Id] int NOT NULL,
[Client_Id] int NOT NULL,
[DocNo] NVarChar(50) NOT NULL,
[DateFrom] Date NOT NULL,
[DateTo] Date NULL,

PRIMARY KEY CLUSTERED([Id])
)
GO

INSERT INTO TestDoc.Contracts
VALUES
(2, 9, '#dc4887311699','2023-11-08', null),
(2, 9, '#dc4887311699','2023-11-10', '2023-11-20'), --should not be selected
(2, 9, '#dc4887311699','2023-12-10', '2023-12-14'),
(2, 9, '#dc4887311699','2023-12-12', '2023-12-20'),
(2, 9, '#dc4887311699','2023-12-18', '2023-12-22'),
(2, 9, '#dc4887311699','2023-12-23', '2024-01-11'),
(2, 9, '#dc4887311699','2024-01-05', '2024-01-08'),
(2, 9, '#dc4887311699','2024-01-09', '2024-01-12'),
(2, 9, '#dc4887311699','2024-01-13', '2024-01-24')

declare 
  @Type_Id int = 2,
  @DateBegin date = '20231123',
  @DateEnd date = '20240203'

  select Client_Id, 

  DateFrom = case when lag(a.DateTo) over(partition by Client_Id order by DateTo) is null then @DateBegin 
    else
      case when datediff(day, lag(a.DateTo, 1, @DateBegin) over(partition by a.Client_Id order by a.DateTo), a.DateFrom) >= 1 then a.DateFrom end
    end,
    
  DateTo = case when lead(a.DateFrom) over(partition by Client_Id order by DateFrom) is null then @DateEnd
   else
      case when datediff(day, a.DateTo, lead(a.DateFrom, 1, @DateEnd) over(partition by a.Client_Id order by DateTo)) >= 1 then a.DateTo end
   end

  from 
  (
    select Client_Id,

    DateFrom = case when DateFrom < @DateBegin then @DateBegin else DateFrom end,
    DateTo = case when (DateTo > @DateEnd) or (DateTo is null) then @DateEnd else DateTo end

    from TestDoc.Contracts
    where (DateFrom < @DateEnd) and (isnull(DateTo, @DateEnd) > @DateBegin) and Type_Id = @Type_Id
  ) a
  order by a.Client_Id, a.DateFrom, a.DateTo

我正在使用:

Microsoft SQL Server 2022 16.0.4095.4

对于完整的逻辑,让我们考虑一下这张图.蓝色矩形表示介于ds(日期开始)和de(日期结束)之间的日期范围.

graphical representation

因此,我必须返回介于@DateStart@DateEnd之间的不相交和不相邻的范围(至少相差一天).在这种情况下,退货范围应为: (@DateStart,DE4),(DS5-@DateEnd). 一百零二 还要注意,DateTo可以为空.在这种情况下,它意味着无穷大.

推荐答案

如果您可以确定总是有相同数量的值DateFromDateTo,您可以 for each 值分配一个行号,然后在行号上自联接,例如

declare 
  @Type_Id int = 2
  , @DateBegin date = '20231123'
  , @DateEnd date = '20240203';

with cte1 as (
  select Client_Id
    , case when lag(a.DateTo) over (partition by Client_Id order by DateTo) is null then @DateBegin 
      else case when datediff(day, lag(a.DateTo, 1, @DateBegin) over (partition by a.Client_Id order by a.DateTo), a.DateFrom) >= 1 then a.DateFrom end
      end DateFrom
    , case when lead(a.DateFrom) over (partition by Client_Id order by DateFrom) is null then @DateEnd
     else case when datediff(day, a.DateTo, lead(a.DateFrom, 1, @DateEnd) over (partition by a.Client_Id order by DateTo)) >= 1 then a.DateTo end
     end DateTo
  from (
    select Client_Id
      , case when DateFrom < @DateBegin then @DateBegin else DateFrom end DateFrom
      , case when (DateTo > @DateEnd) or (DateTo is null) then @DateEnd else DateTo end DateTo
    from TestDoc.Contracts
    where (DateFrom < @DateEnd) and (isnull(DateTo, @DateEnd) > @DateBegin) and [Type_Id] = @Type_Id
  ) a -- Should use a more meanfingful alias here e.g. c for Contracts
), cte2 as (
  select Client_Id, DateFrom, DateTo
    , case when DateFrom is not null then row_number() over (order by case when DateFrom is not null then 0 else 1 end, DateFrom) else null end DateFromRowNumber
    , case when DateTo is not null then row_number() over (order by case when DateTo is not null then 0 else 1 end, DateTo) else null end DateToRowNumber
  from cte1
  where DateFrom is not null or DateTo is not null
)
select c2.Client_Id, c2.DateFrom, c3.DateTo
from cte2 c2
join cte2 c3 on c3.DateToRowNumber = c2.DateFromRowNumber
where c2.DateFromRowNumber is not null
order by c2.Client_Id, c2.DateFrom, c3.DateTo;

按要求返回

Client_Id DateFrom DateTo
9 2023-11-23 2023-12-22
9 2024-01-05 2024-01-12
9 2024-01-13 2024-02-03

然而,如果我们能知道你的完整逻辑,可能会有一个更简单的解决方案.

Sql相关问答推荐

Postgresql:从jsons数组到单个id索引的json

如何将资源密集型自连接转换为更快的查询?

BigQuery`喜欢ANY‘和`不喜欢ANY’

提高写密集型表的查询性能

如何在SQL中更新Json字符串

如何用客户名称计算sum(dr)和sum(cr)

动态组/转置

SQL Athena/prest判断值是否在嵌套的json数组中

从选定记录中提取摘要作为值的划分

确定小数中使用的精度位数

如何将输出转换为二维格式?

PostgreSQL如何将Unix纪元时间戳转换为日期时间并进行拼接

正则表达式忽略特定数据部分的分隔符

获取 SQL Server 中每一行的两个-之间的文本

使用 GROUP BY 时如何创建其他组?

PostgresQL-根据另一列找到 3 个最低值

在 MySql 数据库中的两个日期之间搜索

在 SQL 的每行选项中 Select 最大值

根据开始/结束标记将 GROUP_ID 分配给行

Oracle SQL 查询自行运行,但在包装到select count(*) from ()时失败