Очистка данных SQL - перекрывающийся диапазон дат - PullRequest
0 голосов
/ 09 апреля 2019

Я надеюсь, что кто-нибудь может помочь мне решить эту проблему наилучшим образом.

В настоящее время наша организация использует циклы продаж для оценки эффективности наших ритейлеров по дате их первой отгрузки. Вот бизнес-правила:

Nurture Stage - 1st year
Graduate Stage - 2nd year
Ongoing Stage - 3rd year and on
Inactive Stage - stop doing business
Restart Stage - do business with us after an Inactive Stage
Change Owner Stage - sell their business and new owner does business with us

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

StageNo   ProgramNo   CustomerNo    ProgramType   StageDescription  StartDate   EndDate

CAPS041835  CAP010611   RL023238    Packaged            Nurture     2019-04-04  2019-04-04    
CAPS041836  CAP010611   RL023238    Packaged            Inactive    2019-04-05  2999-01-01
CAPS041837  CAP010612   RL023238    Pre-Made in Bulk    Nurture     2019-04-04  2999-01-01

Выше приведен пример аномалии в данных. 01.01.2999 просто означает, что в нашей ERP это пустая дата.

04/04/2019 пользователь создал упакованную программу и решил, что розничный продавец должен был настроить Pre-Made in Bulk вместо Packaged.

ERP заканчивает текущую стадию в последнюю дату Счета, и, если она не существует, завершит ее с сегодняшней датой и запустит Неактивную Стаду с Сегодня + 1.

Итак, если я запустил аналитику, любые поставки 04.04.199 будут применяться как к Пакетным, так и к Готовым программам.

В идеале, я ХОЧУ полностью избавиться от упакованной программы, но если это невозможно, я бы хотел ее почистить:

StageNo   ProgramNo   CustomerNo    ProgramType   StageDescription  StartDate   EndDate

CAPS041835  CAP010611   RL023238    Packaged            Nurture     2019-04-04  2019-04-04    
CAPS041836  CAP010611   RL023238    Packaged            Inactive    2019-04-04  2019-04-04
CAPS041837  CAP010612   RL023238    Pre-Made in Bulk    Nurture     2019-04-04  2999-01-01

Я могу пройти и исправить это, как только это будет так. Даже если я оставлю это, это не будет концом света, потому что я могу назначить дату отгрузки как DateTime, а затем +1 секунду, что означает, что продажа упадет только в 1 Программе.

Я начал с написания запроса, чтобы найти промежутки между диапазонами дат, чтобы найти пробелы, где разница в датах меньше 0.

Это то, что я имею до сих пор ...

WITH CustomerProgram AS 
(
    SELECT
         ROW_NUMBER() OVER (ORDER BY [CustomerNo] ASC, [ProgramGroupId] ASC, [StageStartDate] ASC, [StageEndDate] ASC, [StagePrecedence] ASC, [CustomerProgramStageNo] ASC) AS [RowId]
        ,*
        ,COUNT([CustomerProgramStageNo]) OVER (PARTITION BY [ProgramGroupId]) AS [StageCount]
    FROM
    (
        SELECT
             --RANK() OVER (ORDER BY [CustomerNo] ASC, [ProgramDescription] ASC) AS [ProgramGroupId]
             RANK() OVER (ORDER BY [CustomerNo] ASC, [CustomerProgramNo] ASC) AS [ProgramGroupId]
            ,[CustomerProgramNo]
            ,[CustomerProgramStageNo]
            ,[CustomerNo]
            ,[ProgramCode]
            ,[ProgramStageCode]
            ,[ProgramStageDescription]
            ,CASE [ProgramStageDescription]
                WHEN 'Nurture'          THEN 1
                WHEN 'Graduate'         THEN 2
                WHEN 'Change Ownership' THEN 3
                WHEN 'Restart'          THEN 3
                WHEN 'Ongoing'          THEN 4
                WHEN 'Inactive'         THEN 5
                ELSE NULL
            END                                 AS [StagePrecedence]
            ,CAST([StageStartDate] AS DATETIME) AS [StageStartDate]
            ,CAST([StageEndDate] AS DATETIME)   AS [StageEndDate]
        FROM
            [CustomerProgramAndStage]
    )   CustomerProgram
)

,StagesAndGaps AS 
(
    SELECT
         ROW_NUMBER() OVER (ORDER BY [CustomerNo] ASC, [ProgramGroupId] ASC, [StageStartDate] ASC, [StageEndDate] ASC) AS [RowId]
        ,[ProgramGroupId]
        ,[StageCount]
        ,[CustomerNo]
        ,[DateRangeType]
        ,[StageStartDate]
        ,[StageEndDate]
        ,DATEDIFF(DAY,[StageStartDate],[StageEndDate])  AS [StageDateDayDiff]
        ,DATEDIFF(YEAR,[StageStartDate],[StageEndDate]) AS [StageDateYearDiff]
        ,[StartDateRowId]
        ,[EndDateRowId]
        ,[PreviousProgramCode]
        ,[NextProgramCode]
        ,[PreviousStagePrecedence]
        ,[NextStagePrecedence]
        ,[PreviousStageNo]
        ,[NextStageNo]
    FROM
    (
        SELECT
             [ProgramGroupId]                           AS [ProgramGroupId]
            ,[StageCount]                               AS [StageCount]
            ,[CustomerNo]                               AS [CustomerNo]
            ,[DateRangeType]                            AS [DateRangeType]
            ,ISNULL([StageStartDate],'1800-01-01')      AS [StageStartDate]
            ,ISNULL([StageEndDate],'3999-01-01')        AS [StageEndDate]
            ,ISNULL([StartDateRowId],0)                 AS [StartDateRowId]
            ,ISNULL([EndDateRowId],9999999)             AS [EndDateRowId]
            ,ISNULL([PreviousProgramCode],'Start')      AS [PreviousProgramCode]
            ,ISNULL([NextProgramCode],'End')            AS [NextProgramCode]
            ,ISNULL([PreviousStagePrecedence],0)        AS [PreviousStagePrecedence]
            ,ISNULL([NextStagePrecedence],999)          AS [NextStagePrecedence]
            ,ISNULL([PreviousStageNo],'Start')          AS [PreviousStageNo]
            ,ISNULL([NextStageNo],'End')                AS [NextStageNo]
        FROM
        (
            SELECT --  Gaps include time period before the start of a Program
                 NextStage.[ProgramGroupId]                 AS [ProgramGroupId]
                ,NextStage.[StageCount]                     AS [StageCount]
                ,NextStage.[CustomerNo]                     AS [CustomerNo]
                ,'Gap'                                      AS [DateRangeType]
                ,PreviousStage.[StageEndDate]               AS [StageStartDate]
                ,NextStage.[StageStartDate]                 AS [StageEndDate]
                ,PreviousStage.[RowId]                      AS [StartDateRowId]
                ,NextStage.[RowId]                          AS [EndDateRowId]
                ,PreviousStage.[ProgramCode]                AS [PreviousProgramCode]
                ,NextStage.[ProgramCode]                    AS [NextProgramCode]
                ,PreviousStage.[StagePrecedence]            AS [PreviousStagePrecedence]
                ,NextStage.[StagePrecedence]                AS [NextStagePrecedence]
                ,PreviousStage.[CustomerProgramStageNo]     AS [PreviousStageNo]
                ,NextStage.[CustomerProgramStageNo]         AS [NextStageNo]
            FROM
            (
                SELECT
                     [RowId]
                    ,[ProgramGroupId]
                    ,[StageCount]
                    ,[CustomerProgramStageNo]
                    ,[CustomerNo]
                    ,[ProgramCode]
                    ,[StagePrecedence]
                    ,[StageStartDate]
                FROM
                    CustomerProgram
            )   NextStage    
            LEFT JOIN
            (
                SELECT
                     [RowId]
                    ,[ProgramGroupId]
                    ,[StageCount]
                    ,[CustomerProgramStageNo]
                    ,[CustomerNo]
                    ,[ProgramCode]
                    ,[StagePrecedence]
                    ,[StageEndDate]
                FROM
                    CustomerProgram
            )   PreviousStage
                    ON NextStage.[ProgramGroupId] = PreviousStage.[ProgramGroupId]
                    AND NextStage.[RowId] - 1 = PreviousStage.[RowId]

            UNION

            SELECT --  Gaps include time period after the end of a Program (year 2999 if Stage is active)
                 PreviousStage.[ProgramGroupId]             AS [ProgramGroupId]
                ,PreviousStage.[StageCount]                 AS [StageCount]
                ,PreviousStage.[CustomerNo]                 AS [CustomerNo]
                ,'Gap'                                      AS [DateRangeType]
                ,PreviousStage.[StageEndDate]               AS [StageStartDate]
                ,NextStage.[StageStartDate]                 AS [StageEndDate]
                ,PreviousStage.[RowId]                      AS [StartDateRowId]
                ,NextStage.[RowId]                          AS [EndDateRowId]
                ,PreviousStage.[ProgramCode]                AS [PreviousProgramCode]
                ,NextStage.[ProgramCode]                    AS [NextProgramCode]
                ,PreviousStage.[StagePrecedence]            AS [PreviousStagePrecedence]
                ,NextStage.[StagePrecedence]                AS [NextStagePrecedence]
                ,PreviousStage.[CustomerProgramStageNo]     AS [PreviousStageNo]
                ,NextStage.[CustomerProgramStageNo]         AS [NextStageNo]
            FROM
            (
                SELECT
                     [RowId]
                    ,[ProgramGroupId]
                    ,[StageCount]
                    ,[CustomerProgramStageNo]
                    ,[CustomerNo]
                    ,[ProgramCode]
                    ,[StagePrecedence]
                    ,[StageEndDate]
                FROM
                    CustomerProgram
            )   PreviousStage
            LEFT JOIN
            (
                SELECT
                     [RowId]
                    ,[ProgramGroupId]
                    ,[StageCount]
                    ,[CustomerProgramStageNo]
                    ,[CustomerNo]
                    ,[ProgramCode]
                    ,[StagePrecedence]
                    ,[StageStartDate]
                FROM
                    CustomerProgram
            )   NextStage
                    ON PreviousStage.[ProgramGroupId] = NextStage.[ProgramGroupId]
                    AND PreviousStage.[RowId] + 1 = NextStage.[RowId]

            UNION

            SELECT --  Stage data
                 [ProgramGroupId]           AS [ProgramGroupId]
                ,[StageCount]               AS [StageCount]
                ,[CustomerNo]               AS [CustomerNo]
                ,'Stage'                    AS [DateRangeType]
                ,[StageStartDate]           AS [StageStartDate]
                ,[StageEndDate]             AS [StageEndDate]
                ,[RowId]                    AS [StartDateRowId]
                ,[RowId]                    AS [EndDateRowId]
                ,[ProgramCode]              AS [PreviousProgramCode]
                ,[ProgramCode]              AS [NextProgramCode]
                ,[StagePrecedence]          AS [PreviousStagePrecedence]
                ,[StagePrecedence]          AS [NextStagePrecedence]
                ,[CustomerProgramStageNo]   AS [PreviousStageNo]
                ,[CustomerProgramStageNo]   AS [NextStageNo]
            FROM
                CustomerProgram
        )   StagesAndGaps
    )   StagesAndGaps
)


SELECT 
    *
FROM 
    StagesAndGaps
WHERE 
    [DateRangeType] = 'Gap'
    AND [StageStartDate] NOT IN ('1800-01-01','2999-01-01')
ORDER BY 
    [RowId] ASC

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

1 Ответ

0 голосов
/ 09 апреля 2019

Вы можете использовать PARTITION, ORDER, чтобы разделить набор данных на упорядоченные порции, а затем определить записи, которые необходимо обновить / удалить, что вы и попробовали.Но вы можете быть более точным.

Например, вы только что использовали ORDER BY, который даст вам:

row_num customer_no stage stage_startdate 1 1 A 2019-01-01 2 2 B 2019-12-30

Здесь вы не можете сравнивать row_num 1 и 2, потому что они оба принадлежат двум разным клиентам.

Итак, сначала используйте PARTITION, чтобы разделить порции, а затем используйте ORDER.BY, чтобы упорядочить данные.

И вместо обновления вы можете пометить ненужные записи и затем удалить их.

Для этого добавьте столбец «to_be_deleted», чтобы отметить записи, которые необходимобыть удаленным.Если вы используете SQL Server 2012+, этот столбец to_be_deleted можно легко заполнить с помощью LEAD () или LAG () в верхней части вывода PARTITION.Функции LEAD () или LAG () помогают сравнивать с предыдущими или следующими строками.Таким образом, вы можете легко проверить наличие дубликатов, а затем отметить их, которые в конечном итоге могут быть удалены.

Вы можете обратиться к этому для LEAD (), LAG (): https://blog.sqlauthority.com/2011/11/15/sql-server-introduction-to-lead-and-lag-analytic-functions-introduced-in-sql-server-2012/

Надеюсь, что этопомогает вам :).Кстати, хорошая инициатива

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...