Сложная логика SQL Server для возврата строк на основе дней между датами и возврата строк для каждого дня в SSMS 2017 - PullRequest
0 голосов
/ 22 марта 2019

У меня очень сложный сценарий, который я пытаюсь реализовать на сервере SQL, чтобы избавить от необходимости вводить много повторяющихся строк данных. Я постараюсь объяснить это в меру своих возможностей. Пожалуйста, прочитайте все внимательно. Несмотря на то, что это довольно длинный пост, я обещаю вам ALL того, что я собираюсь объяснить ниже, в вопросах, касающихся правильного решения этой проблемы.

Каждый день я получаю ежедневный отчет, в котором сообщается обо всех открытых делах. Я создаю ежемесячный подсчет этих событий, который просто добавляет все открытые дела за все эти дни в конец открытого файла дел за эти месяцы. Так что в конце каждого месяца у меня будут данные за 20-22 дня, объединенные для всех выходных и праздничных дней.

Случаи содержат поле с именем [Last_Updated_On], которое является датой его последнего обновления. Есть около 5000 открытых дел каждый день. Однако из этих 5000 примерно 3800 дел каждый день имеют одинаковую дату [Last_Updated_On], что означает, что они являются точными дубликатами всех полей, отображаемых несколько дней подряд, поскольку это ЕДИНСТВЕННОЕ поле, которое когда-либо изменяется, когда дело открыто ,

Как вы можете видеть, если бы я загружал все эти точные дубликаты на сервер, база данных имела бы приблизительно 75 000–80 000 дополнительных записей каждый месяц без какой-либо реальной причины, поскольку подробности дела точно такие же. Поэтому, прежде чем загружать данные каждый месяц, я удаляю дубликаты записей в Excel, оставляя мне только около 20 000 записей с [Case_ID] с уникальной датой [Last_Updated_On].

Допустим, у меня есть следующие столбцы в таблице данных: [Case_ID], [Last_Updated_On] и [Contact_Date]. Кроме того, я хочу, чтобы было 3 виртуальных вычисляемых столбца - [Дата отчета], [Дата решения] и [Возраст].

Возраст определяется как количество дней, открытых между [Contact_Date] и [Report_Date]. Теперь я хочу передать в Хранимую процедуру две даты: Report_Start_Date и Report_End_Date. [Report_Date] начинается с [Report_Start_Date] и заканчивается [Report_End_Date], увеличиваясь один раз для каждой возвращаемой строки данных. [Resolved_Date] запускает sub_query для проверки таблицы [Resolved_Case] ​​на предмет соответствия [Case_ID], чтобы определить, на какую дату (если она вообще была) разрешен случай.

У меня есть 3 примера того, как это должно работать ниже. Для простоты в этих примерах, скажем, я передаю одну неделю (только M-F) для диапазона дат Report_Date_Start и Report_Date_End (18.03.2009 - 22.03.2009).

В первом случае, допустим, этот случай не разрешен, поэтому он был открыт все 5 дней, и в [Last_Updated_On] изменений не было. Это означает, что он будет хранить только 1 ROW в базе данных, поскольку остальные 4 дня будут точными дубликатами. Однако, когда я возвращаю запрос, мне нужно, чтобы он возвращал 5 ROWS данных, по 1 на каждый день в диапазоне от [Report_Start_Date] до [Report_End_Date]. Единственная разница будет в полях [Age] и [Report_Date], поскольку каждый день, когда дело открывается, возраст увеличивается на 1.

Так что я знаю, что должен быть способ сделать что-то подобное, но я действительно не знаю, с чего начать. Как вернуть несколько строк данных из одной строки? Единственный опыт, который у меня был в этом случае, был, когда я делал Полное соединение, но я не уверен, насколько это применимо к этой ситуации.

Так, например, вот карта из того, что я описал выше:

Data in server:

    [Case_ID]     [Last_Updated_On]   [Contact_Date]   [Resolved_Date] 
    US2309-2323       3/15/2019         3/15/2019          NULL

Query_Returns:

    [Case_ID]     [Last_Updated_On]   [Contact_Date]   [Report_Date]  [Age]
    US2309-2323       3/15/2019         3/15/2019        3/18/2019      1
    US2309-2323       3/15/2019         3/15/2019        3/19/2019      2
    US2309-2323       3/15/2019         3/15/2019        3/20/2019      3
    US2309-2323       3/15/2019         3/15/2019        3/21/2019      4
    US2309-2323       3/15/2019         3/15/2019        3/22/2019      5

Как видите, запрос будет возвращать 5 строк - по одной на каждую дату в диапазоне report_date, где report_date меньше resolved_date (или всех дат, если resolved_date имеет значение NULL).

Теперь здесь был бы другой экземпляр с дополнительной строкой для этого случая, основанной на различных датах [Last_Contacted_On], присутствующих там и имеющих несколько строк в базе данных:

Data in server:

    [Case_ID]     [Last_Updated_On]   [Contact_Date]   [Resolved_Date] 
    US2309-2323       3/15/2019         3/15/2019          NULL
    US2309-2323       3/19/2019         3/15/2019          NULL
    US2309-2323       3/21/2019         3/15/2019          NULL

Query_Returns:

    [Case_ID]     [Last_Updated_On]   [Contact_Date]   [Report_Date]  [Age]
    US2309-2323       3/15/2019         3/15/2019        3/18/2019      1
    US2309-2323       3/19/2019         3/15/2019        3/19/2019      2
    US2309-2323       3/19/2019         3/15/2019        3/20/2019      3
    US2309-2323       3/21/2019         3/15/2019        3/21/2019      4
    US2309-2323       3/21/2019         3/15/2019        3/22/2019      5

Как вы можете видеть, какая дата [Last_Updated_On] будет возвращена для каждой строки, зависит от [Report_Date]. Здесь нужно сделать 3 проверки даты, чтобы определить, какую из них вернуть. 15.03.2009, 19.03.2009 и 21.03.2009. Должно быть понятно, почему даты [Last_Updated_On] являются такими, какие они есть.

[Report_Date] принимает только самую последнюю доступную дату [Last_Updated_On], которая меньше или равна самой себе. 3/18 строка вернет дату [Last_Updated_On] 3/15, поскольку 3/19 еще не произошло, и ее нельзя было обновить завтра днем ​​ранее. 3/19, последний раз обновлялся в тот же день, поэтому теперь возвращается 3/19 для даты [Last_Updated_On]. То же самое для 3/20, так как [Last_Updated_On] Date использовал максимальный день, который меньше или равен [Report_Date]. 3/21 он был обновлен снова, поэтому теперь [Last_Updated_On] дата составляет 3/21 для [Report_date] 3/21 и 3 / 22.

Теперь для 3-го сценария, где дело разрешается где-то в середине диапазона [Report_Start_Date] и [Report_End_Date].

Данные на сервере:

[Case_ID]     [Last_Updated_On]   [Contact_Date]   [Resolved_Date] 
US2309-2323       3/15/2019         3/15/2019       3/21/2019
US2309-2323       3/19/2019         3/15/2019       3/21/2019
US2309-2323       3/21/2019         3/15/2019       3/21/2019

Query_Returns:

[Case_ID]     [Last_Updated_On]   [Contact_Date]   [Report_Date]  [Age]
US2309-2323       3/15/2019         3/15/2019        3/18/2019      1
US2309-2323       3/19/2019         3/15/2019        3/19/2019      2
US2309-2323       3/19/2019         3/15/2019        3/20/2019      3
US2309-2323       3/21/2019         3/15/2019        3/21/2019      4

Обратите внимание, что, хотя отчетные даты были установлены с 18.03.2009 по 22.03.2009, поскольку дело было решено 21.03.2009, по состоянию на 22.03.2009 строка не возвращается по состоянию на 21.03.2009 дело больше не было открыто.

Я надеюсь, что это подробно объясняет то, что я пытаюсь выполнить, и я знаю, что должен быть способ сделать это, но это действительно намного более сложный запрос, чем я когда-либо пытался. Я даже не знаю, с чего начать, поэтому любая помощь, которая может быть оказана, будет принята с благодарностью.

Вот некоторый SQL, чтобы помочь:

CREATE TABLE Open_Cases (
  [Case_ID] varchar(15),
  [Last_Updated_On] datetime,
  [Contact_Date] datetime,
)
CREATE TABLE Resolved_Cases(
[Case_ID] varchar(15),
[Resolved_Date] datetime
)

INSERT INTO Open_Cases VALUES ('US2309-2323',  '3/15/2019',   '3/15/2019 ')
INSERT INTO Open_Cases VALUES ('US2309-2323',  '3/19/2019',   '3/15/2019 ')
INSERT INTO Open_Cases VALUES ('US2309-2323',  '3/20/2019',   '3/15/2019 ')

INSERT INTO Resolved_Cases VALUES ('US2309-2323',  '3/21/2019')

DECLARE @Report_Start_Date date;
DECLARE @Reprot_End_Date date;
SET @Report_Start_Date = '3/18/2019';
SET @Report_End_Date = '3/22/2019';

BEGIN 
  WITH GetReportDate AS (
  --***SQL Code to generate each day based on the start and end date range 
),
   SELECT o.Case_ID, o.Last_Updated_On, o.Contact_Date, r.Resolved_Date,
   (SELECT CalcAge(@Report_Start_Date, @Report_End_Date)  AS Age,
    FROM Open_Cases AS o
   LEFT JOIN Resolved_Cases as r
   ON r.Case_ID = o.Case_ID
   EXCEPT
   SELECT r.Resolved_Date, FROM Resolved_Cases as r
   LEFT JOIN GetReportDate  as Rep
   ON Rep.Report_Date > r.Resolved_Date
END

Это должно быть все необходимое, чтобы помочь с этой проблемой. Примечание. CalcAge - это скалярная табличная функция, возвращающая значение INT, которое дает нам количество дней между двумя датами, вычитающими выходные и выходные. Я не уверен, что CTE - это то, что я хочу создать для каждой Report_Date, основываясь на диапазоне дат начала и окончания, но я предполагаю, что это может сработать? Я знаю, что не могу сделать сравнение в операторе EXCEPT между Report_Date и Resolved_Date, потому что Report_Date - это вычисляемый столбец, и я знаю, что использование CTE позволит выполнить сравнение, но, опять же, я не уверен, что это правильный метод для получения это работает.

Также предполагаю, что я должен использовать Исключение, исходя из того, что значение report_date больше, чем Resolved_Date ... не стесняйтесь исправлять и / или изменять любое из этих операторов, так как я делаю все возможное, чтобы угадать, что должно произойти. ..посещение кем-то может заполнить остальное для меня.

...