查找缺少的联系日期详细信息

时间:2019-12-06 11:13:22

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

我有下表,其中包含“联系人”详细信息:

表格:tblContacts

CREATE TABLE tblContacts
(
    Series INT,
    ContacNumber INT,
    CDate DATETIME
);

样本数据:

INSERT INTO tblContacts VALUES(12,123456,'2019-01-01');
INSERT INTO tblContacts VALUES(3,3456,'2019-01-01');
INSERT INTO tblContacts VALUES(12,123560,'2019-01-02');
INSERT INTO tblContacts VALUES(12,123459,'2019-01-05');
INSERT INTO tblContacts VALUES(3,3446,'2019-01-02');
INSERT INTO tblContacts VALUES(3,3486,'2019-01-03');
INSERT INTO tblContacts VALUES(3,34861,'2019-01-05');
INSERT INTO tblContacts VALUES(3,34862,'2019-01-07');
INSERT INTO tblContacts VALUES(12,127456,'2019-01-21');
INSERT INTO tblContacts VALUES(12,129456,'2019-02-03');
INSERT INTO tblContacts VALUES(12,126456,'2019-02-06');
INSERT INTO tblContacts VALUES(94,941256,'2019-01-01');
INSERT INTO tblContacts VALUES(94,944356,'2019-01-03');
INSERT INTO tblContacts VALUES(94,941356,'2019-01-07');
INSERT INTO tblContacts VALUES(94,943356,'2019-01-09');

我想找到那些从未在打电话之前和之后的日期和1或2天打电话的联系人。

注意:我可能会在调用1,2,3,4之类的任何级别之前和之后将其折痕。

预期的产量:以下数据仅在通话前后1天出现。

ContacNumber    CDate
----------------------------------------
3486            2019-01-03
NULL            2019-01-04
34861           2019-01-05
NULL            2019-01-06
34862           2019-01-07
123560          2019-01-02
NULL            2019-01-03 - 2019-01-04
123459          2019-01-05
NULL            2019-01-06 - 2019-01-20
127456          2019-01-21
NULL            2019-01-22 - 2019-02-02
129456          2019-02-03
NULL            2019-02-04 - 2019-02-05
126456          2019-02-06
941256          2019-01-01
NULL            2019-01-02
944356          2019-01-03
NULL            2019-01-04 - 2019-01-06
941356          2019-01-07
NULL            2019-01-08
943356          2019-01-09

我的尝试:以下查询仅在通话前后1天内有效,但不适用于1以外的查询。

查询:

; WITH Stage_1_CTE AS
(
    SELECT  Series,
            ContacNumber,
            CAST(CDate AS DATE) CDate,
            ROW_NUMBER() OVER (PARTITION BY Series ORDER BY CAST(CDate AS DATE)) rnk1,
            (ROW_NUMBER() OVER (PARTITION BY Series ORDER BY CAST(CDate AS DATE)))/2 rnk2,
            (ROW_NUMBER() OVER (PARTITION BY Series ORDER BY CAST(CDate AS DATE)) + 1)/2 rnk3
    FROM tblContacts
    GROUP BY ContacNumber,CDate,Series
)
,
Stage_2_CTE AS
(
    SELECT *,
           CASE WHEN rnk1%2=1 THEN MAX(CASE WHEN rnk1%2=0 THEN CDate END) OVER (PARTITION BY Series,rnk2) 
                ELSE MAX(CASE WHEN rnk1%2=1 THEN CDate END) OVER (PARTITION BY Series,rnk3)
           END AS CDate_Prev,
           CASE WHEN rnk1%2=1 THEN MAX(CASE WHEN rnk1%2=0 THEN CDate END) OVER (PARTITION BY Series,rnk3) 
                ELSE MAX(CASE WHEN rnk1%2=1 THEN CDate END) OVER (PARTITION BY Series,rnk2)
           END AS CDate_Next
    FROM Stage_1_CTE
) 
,Stage_Final_CTE AS
(
    SELECT c.Series,
           c.ContacNumber, 
           c.CDate, 
           EndDate = ''
    FROM Stage_2_CTE c
    WHERE c.CDate <> DATEADD(DAY, +1, CDate_Prev) OR c.CDate <> DATEADD(DAY, -1, CDate_Next)
    UNION ALL
    SELECT Series,
           ContacNumber = NULL,
           CDate = DATEADD(DAY, 1, c.CDate),
           EndDate = ' - '+CAST(DATEADD(DAY, -1, CDate_Next) AS VARCHAR(10))
    FROM Stage_2_CTE c
    WHERE c.CDate <> DATEADD(DAY, -1, CDate_Next)
)
SELECT  ContacNumber, 
        CASE WHEN CAST(CDate AS VARCHAR(10)) =  REPLACE(EndDate,' - ','')  
        THEN CAST(CDate AS VARCHAR(10))
        ELSE
            CAST(CDate AS VARCHAR(10)) + CAST(EndDate AS VARCHAR(13))
        END CDate
FROM Stage_Final_CTE
GROUP BY ContacNumber,CDate,EndDate,Series
ORDER BY Series,CDate;  

2 个答案:

答案 0 :(得分:1)

您可以生成数字,然后添加行。像这样:

select contactnumber, cdate, 1 as isvalid
from tblcontacts
union all
select c.contactnumber, dateadd(day, v.n, c.contacctdate), 0
from tblcontacts c cross join
     (values (-1), (1)) v(n)
where not exists (select 1
                  from tblcontacts c2
                  where c2.contactnumber = c.contactnumber and
                        c2.date = dateadd(day, v.n, c.contacctdate)
                 );

注意:我添加了一个新列,以确定该行是否有效。在第一列中重复NULL值没有意义,因为您不知道该行适用于哪个联系电话。

答案 1 :(得分:1)

您可以在Series中获得下一个日期。

可用于查找日期之间的间隔。

然后将这些间隙粘贴到结果上。

对于Sql Server 2008,首先将数据加载到临时表中。

IF OBJECT_ID('tempdb..#tmpContacts', 'U') IS NOT NULL
    DROP TABLE #tmpContacts; 

CREATE TABLE #tmpContacts
(
 Series INT NOT NULL,
 Rn INT NOT NULL,
 ContacNumber INT NOT NULL,
 CDate DATE NOT NULL,
 PRIMARY KEY (Series, Rn)
); 

INSERT INTO #tmpContacts (Series, ContacNumber, CDate, Rn)
SELECT Series, ContacNumber
, CAST(CDate AS DATE)
, ROW_NUMBER() OVER (PARTITION BY Series ORDER BY CAST(CDate AS DATE)) Rn
FROM tblContacts
WHERE CDate >=  CAST('2019-01-01' AS DATE)
    AND CAST(CDate AS DATE) <=  EOMONTH(EOMONTH(CAST('2019-01-01' AS DATE)))
  GROUP BY Series, ContacNumber, CAST(CDate AS DATE);

WITH CTE_CONTACTS AS
(
  SELECT Series, ContacNumber
  , CDate
  , CDate AS nextCDate
  , CAST(0 AS BIT) AS IsGap
  FROM #tmpContacts

  UNION ALL

  SELECT t1.Series, null
  , DATEADD(day,1,t1.CDate)
  , DATEADD(day,-1,t2.CDate)
  , 1
  FROM #tmpContacts t1
  LEFT JOIN #tmpContacts t2
    ON t2.Series = t1.Series
   AND t2.Rn = t1.Rn + 1
   AND t1.CDate < DATEADD(day,-1,t2.CDate)
)
SELECT c.Series, ContacNumber
, CONCAT(CONVERT(varchar,c.CDate,23), 
        CASE 
        WHEN c.IsGap=1
         AND DATEDIFF(day,c.CDate,c.nextCDate) > 0 
        THEN ' - ' + CONVERT(varchar,c.nextCDate,23) 
        END) AS Cdates
FROM CTE_CONTACTS c
ORDER BY c.Series, c.CDate;

测试妊娠here

在Sql Server 2012+中,可以改为使用窗口函数LEAD

WITH CTE_CONTACTS AS
(
  SELECT Series, ContacNumber
  , CAST(CDate AS DATE) AS CDate
  , LEAD(CAST(CDate AS DATE)) OVER (PARTITION BY Series ORDER BY CAST(CDate AS DATE)) AS nextCDate
  FROM tblContacts
  WHERE CDate >=  CAST('2019-01-01' AS DATE)
    AND CAST(CDate AS DATE) <=  EOMONTH(EOMONTH(CAST('2019-01-01' AS DATE)))
  GROUP BY Series, ContacNumber, CAST(CDate AS DATE)
)
, CTE_CONTACTS2 AS
(
  SELECT Series, ContacNumber
  , CDate
  , nextCDate
  , CAST(0 AS BIT) AS IsGap
  FROM CTE_CONTACTS

  UNION ALL

  SELECT Series, null
   , DATEADD(day,1,CDate)
   , DATEADD(day,-1,nextCDate)
   , 1
  FROM CTE_CONTACTS c
  WHERE CDate < DATEADD(day,-1,nextCDate)
)
SELECT c.Series, ContacNumber
, CONCAT(CONVERT(varchar,c.CDate,23), 
        CASE 
        WHEN c.IsGap=1 
         AND DATEDIFF(day,c.CDate,c.nextCDate) > 0 
        THEN ' - ' + CONVERT(varchar,c.nextCDate,23) 
        END) AS Cdates
FROM CTE_CONTACTS2 c
ORDER BY c.Series, c.CDate;

在妊娠here上进行的测试