我有下表,其中包含“联系人”详细信息:
表格: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;
答案 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上进行的测试