Также проверьте BabySmash Скотта Гензельмана (www.codeplex.com/babysmash). Он использовал метод «грубой силы», скрывающий курсор Windows и показывающий его новый курсор на холсте, а затем перемещение курсора в «реальный» курсор был бы
. Подробнее здесь: http://www.hanselman.com/blog/DeveloperDesigner.aspx
Один подход был бы ниже.
WITH T1( Code, Date, Flag) AS
(
SELECT Code, DateFrom, 1 AS Flag
FROM dbo.Test
UNION ALL
SELECT Code, dateTo, -1 AS Flag
FROM dbo.Test
), T2 AS
(
SELECT *, SUM(Flag) OVER (PARTITION BY Code ORDER BY Date ROWS UNBOUNDED PRECEDING) AS ActiveMembershipCount
FROM T1
), T3 AS
(
SELECT *, LAG(ActiveMembershipCount,1,0) OVER (PARTITION BY Code ORDER BY Date) AS PrevActiveMembershipCount
FROM T2
), T4 AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY Code ORDER BY Date)-1 AS RN
FROM T3
WHERE 0 IN (ActiveMembershipCount, PrevActiveMembershipCount)
)
SELECT Code, MIN(Date), MAX(Date)
FROM T4
GROUP BY Code, RN/2
HAVING MIN(Date) <= '2018-07-01' AND MAX(Date) >= '2018-12-31'
NB. Я не проверял это строго по сценарию, когда членство заканчивается и перезапускается в один и тот же день, поэтому может потребоваться некоторые настройки, но базовый подход будет работать.
Решение с DimDate выглядит очень красиво и легко читаемо: Tx all again.
/*
SELECT TOP (DATEDIFF(DAY, '20180701', '20190101'))
DATEADD(dd,ROW_NUMBER() OVER (ORDER BY (SELECT NULL))-1,'20180701') dt
INTO dbo.Calendar
FROM sys.all_columns sc1, sys.all_columns sc2; -- drop table Calendar
-- select * from Calendar --184
*/
with cte as (
SELECT t.*, c.dt
FROM test t
join Calendar c on c.dt between t.DateFrom and t.DateTo
)
select Code, count(Distinct dt) from cte
group by Code
Having count(Distinct dt) <> 184