Учитывая следующие данные:
create table #histories
(
username varchar(10),
account varchar(10),
assigned date
);
insert into #histories
values
('PHIL','ACCOUNT1','2017-01-04'),
('PETER','ACCOUNT1','2017-01-15'),
('DAVE','ACCOUNT1','2017-03-04'),
('ANDY','ACCOUNT1','2017-05-06'),
('DAVE','ACCOUNT1','2017-05-07'),
('FRED','ACCOUNT1','2017-05-08'),
('JAMES','ACCOUNT1','2017-08-05'),
('DAVE','ACCOUNT2','2017-01-02'),
('PHIL','ACCOUNT2','2017-01-18'),
('JOSH','ACCOUNT2','2017-04-08'),
('JAMES','ACCOUNT2','2017-04-09'),
('DAVE','ACCOUNT2','2017-05-06'),
('PHIL','ACCOUNT2','2017-05-07') ;
... который представляет, когда данный пользователь был назначен на учетную запись.
Я пытаюсь установить, кому принадлежала данная учетная запись в последний день каждого месяца (назначенная дата - это дата, когда учетная запись перешла в собственность), с заполненными пропущенными окончаниями месяца (возможно, созданными из удобной dates
таблицы, которая у меня есть, с полезными колоннами DateKey
, Date
и LastDayOfMonth
, [любезно @AaronBertrand]) 1 .
Желаемые результаты будут:
PETER, ACCOUNT1, 2017-01-31
PETER, ACCOUNT1, 2017-02-28
DAVE, ACCOUNT1, 2017-03-31
DAVE, ACCOUNT1, 2017-04-30
FRED, ACCOUNT1, 2017-05-31
FRED, ACCOUNT1, 2017-06-30
FRED, ACCOUNT1, 2017-07-31
JAMES, ACCOUNT1, 2017-08-31
PHIL, ACCOUNT2, 2017-01-31
PHIL, ACCOUNT2, 2017-02-28
PHIL, ACCOUNT2, 2017-03-31
JAMES, ACCOUNT2, 2017-04-30
PHIL, ACCOUNT2, 2017-05-31
Выполнение начальной части этого с помощью оконной функции тривиально, это добавление «пропущенных» строк, с которыми я борюсь.
2017-05
потому что он был на нем,2017-05-07
а последующего держателя не было?Ответы:
Один из подходов к этой проблеме заключается в следующем:
LEAD
на SQL Server 2008. Вы можете использоватьAPPLY
или suquery для этого.Я немного изменил ваши тестовые данные, чтобы сделать результаты детерминированными. Также добавлен индекс:
Вот самая ленивая таблица измерений за все время:
Для шага 1 есть много способов подражать
LEAD
. Вот один из методов:Для шага 2 нам нужно изменить значения NULL на что-то другое. Вы хотите включить последний месяц для каждой учетной записи, поэтому достаточно добавить один месяц к начальной дате:
На шаге 3 мы можем присоединиться к таблице измерений даты. Столбец из таблицы измерений - это именно тот столбец, который необходим для набора результатов:
Мне не понравился запрос, который я получил, когда сложил все вместе. Могут быть проблемы с порядком соединения при объединении
OUTER APPLY
иINNER JOIN
. Чтобы получить порядок соединения, который я хотел, я переписал его с помощью подзапроса:Я не знаю, сколько у вас данных, так что это может не иметь значения для вас. Но план выглядит так, как я хочу:
Результаты совпадают с вашими:
источник
Здесь я не использую таблицу календаря, а таблицу натуральных чисел nums.dbo.nums (надеюсь, она у вас тоже есть, если нет, то ее можно легко сгенерировать)
У меня есть ответ, немного отличающийся от вашего ('JOSH' <-> 'JAMES'), потому что ваши данные содержат эти 2 строки:
с той же учетной записью и назначенной датой, и вы не указали, какой из них следует принять в этой ситуации
источник
Это ни в коем случае не чистое решение, но, похоже, оно дает результаты, которые вы ищете (я уверен, что у других будут хорошие, чистые, полностью оптимизированные запросы для вас).
источник
Я использовал таблицу измерений даты от Аарона Бертрана, как вы также упомянули в своем вопросе (которая является очень удобной таблицей для таких сценариев), и я написал следующий код:
Я добавил
EndOfMonth
столбец в#dim
таблицу (сразу послеFirstOfMonth
столбца), используя следующий код:И решение:
источник
Треугольник ПРИСОЕДИНЯЙТЕСЬ к победе!
Результаты:
Интерактивный план выполнения здесь.
Статистика ввода / вывода и ВРЕМЕНИ (усеченные все нулевые значения после логического чтения):
Запрос на создание необходимых временных таблиц и тестирование оператора T-SQL, который я предлагаю:
источник