SQL оператор для получения данных без дубликатов для комбинации столбцов - PullRequest
1 голос
/ 14 апреля 2020

У нас есть таблица захвата событий, которая содержит различные типы событий (на основе EventTypeId) для нескольких активов (на основе assetId).

В нашем коде была ошибка приложения, которую мы недавно исправили где время окончания записывалось неправильно. И время окончания фиксируется, когда изменяется «Серьезность» для данного типа события и ресурса. Но это было сделано неправильно.

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

The SQL, который я в настоящее время сформулировал (взято из: Рассчитать время между строками состояния включения и выключения SQL Сервер )

WITH ReportData
AS (SELECT e.Id [EventId]
          ,e.AssetId
          ,e.StartTime
          ,e.Severity
          ,e.EventTypeId
          ,a.Name [AssetName]
          ,ROW_NUMBER() OVER (PARTITION BY e.AssetId ORDER BY e.StartTime) RowNum
          ,ROW_NUMBER() OVER (PARTITION BY e.AssetId ORDER BY e.StartTime)
           - ROW_NUMBER() OVER (PARTITION BY e.AssetId, e.Severity ORDER BY e.StartTime) AS [Group]
    FROM dbo.Event e
        JOIN dbo.Asset a
            ON a.Id = e.AssetId)
SELECT state1.AssetName
      ,state1.AssetId
      ,MIN(state1.StartTime) [START]
      ,MAX(state2.StartTime) [END]
      ,DATEDIFF(SS, MIN(state1.StartTime), MAX(state2.StartTime)) [Duration]
      ,state1.Severity
      ,state1.EventId
FROM ReportData state1
    LEFT JOIN ReportData state2
        ON state1.RowNum = state2.RowNum - 1
WHERE state1.Severity = 'Extreme'
      AND state2.StartTime IS NOT NULL
      AND state1.EventTypeId = 27
GROUP BY state1.AssetName
        ,state1.AssetId
        ,state1.Severity
        ,state1.EventId
        ,state1.[Group]
ORDER BY MIN(state1.StartTime) DESC;

Дубликаты выглядят примерно так

Event data

Может ли кто-нибудь подсказать мне время начала и окончания на основе изменения статуса (тип события и изменение актива для серьезности), игнорируя дубликаты.

Также, если бы вы могли дать мне запрос, чтобы определить дубликаты, чтобы мы могли удалить его, было бы здорово!

Ответы [ 2 ]

0 голосов
/ 14 апреля 2020

Таким образом, если вы можете иметь одинаковую серьезность для одного и того же актива и события несколько раз, а не подряд (что на самом деле усложняет дело), ​​то сначала мы должны узнать, какое поле даты (StartDate или CreatedAt) мы можем определить как поле для заказа вашего выбора. В моем запросе ниже я предполагаю, что это id CreatedAt, и именно так я подготовил бы набор данных для хранения (мы не просто сохраняем некоторые строки и удаляем некоторые, мы также должны обновить EndTime или StartTime в строке, которую мы оставляем), пожалуйста, обратите внимание на комментарии:

--First we order the selection by entry time and for each line we want to know which severity will be next and previous 
WITH PrevAndNext AS 
(
    SELECT
        Id,
        AssetId,
        EventTypeId,
        Severity,
        StartTime,
        EndTime,
        CreatedAt,
        LAG(Severity) OVER (PARTITION BY AssetId, EventTypeId ORDER BY CreatedAt ASC) AS PrevSeverity, -- date field for ORDER BY depends on your logic!
        LEAD(Severity) OVER (PARTITION BY AssetId, EventTypeId ORDER BY CreatedAt ASC) AS NextSeverity -- date field for ORDER BY depends on your logic!
    FROM
        Table
)

--From the selection above we define the first and last occurence of each severity event
,FirstAndLast AS
(
    SELECT 
        Id,
        AssetId,
        EventTypeId,
        Severity,
        StartTime,
        EndTime,
        CreatedAt,
        CASE 
            WHEN PrevSeverity IS NULL OR PrevSeverity <> Severity THEN 'FirstOccurence'
            WHEN NextSeverity IS NULL OR NextSeverity <> Severity THEN 'LastOccurence'
            ELSE 'MiddleOccurence'
        END AS Occurence 
    FROM 
        PrevAndNext
)

--Then we suppose we want to keep only the first occurence for each severity event, but we need to pick the EndDate from the last occurence
,MergeStartAndEndTime AS
(
    SELECT 
        Id,
        AssetId,
        EventTypeId,
        Severity,
        StartTime,
        CASE 
            WHEN Occurence = 'FirstOccurence' AND LEAD(Occurence) OVER (PARTITION BY AssetId, EventTypeId ORDER BY CreatedAt ASC) = 'LastOccurence' THEN LEAD(EndTime) OVER (PARTITION BY AssetId, EventTypeId ORDER BY CreatedAt ASC) AS KeepIt -- date field for ORDER BY depends on your logic!
            ELSE EndTime
        END AS EndTime,
        CreatedAt
    FROM 
        FirstAndLast 
    WHERE 
        Occurence IN ('FirstOccurence', 'LastOccurence')
)

--Here is the dataset you want to keep. You can use it to update the EndDate field for Id-s, and then remove all the other Id-s which are not in the dataset. Please check it carefully and first try it on some test dataset with duplicates. Feel free to adjust it for your logic if necessary.
SELECT 
    Id,
    AssetId,
    EventTypeId,
    Severity,
    StartTime,
    EndTime,
    CreatedAt
FROM 
    MergeStartAndEndTime 
WHERE 
    Occurence = 'FirstOccurence';   
0 голосов
/ 14 апреля 2020

Вы можете определить CTE, чтобы сначала удалить дубликаты, а затем запустить ваш запрос, используя этот CTE:

with e as (
  select min(Id) as Id,  -- We return the first ID for every duplicate
         AssetId, 
         StartTime, 
         Severity, 
         EventTypeId
  from dbo.Event
  group by AssetId, StartTime, Severity, EventTypeId
),
--- Here comes your Query, using e instead of Event

Итак, это будет:

with e as (
  select min(Id) as Id,  -- We return the first ID for every duplicate
         AssetId, 
         StartTime, 
         Severity, 
         EventTypeId
  from dbo.Event
  group by AssetId, StartTime, Severity, EventTypeId
),
ReportData as (
  SELECT e.Id [EventId]
         ,e.AssetId
         ,e.StartTime
         ,e.Severity
         ,e.EventTypeId
         ,a.Name [AssetName]
         ,ROW_NUMBER() OVER (PARTITION BY e.AssetId ORDER BY e.StartTime) RowNum
         ,ROW_NUMBER() OVER (PARTITION BY e.AssetId ORDER BY e.StartTime)
          - ROW_NUMBER() OVER (PARTITION BY e.AssetId, e.Severity ORDER BY e.StartTime) AS [Group]
  FROM e
       JOIN dbo.Asset a
            ON a.Id = e.AssetId
)
SELECT state1.AssetName
      ,state1.AssetId
      ,MIN(state1.StartTime) [START]
      ,MAX(state2.StartTime) [END]
      ,DATEDIFF(SS, MIN(state1.StartTime), MAX(state2.StartTime)) [Duration]
      ,state1.Severity
      ,state1.EventId
FROM ReportData state1
    LEFT JOIN ReportData state2
         ON state1.RowNum = state2.RowNum - 1
WHERE state1.Severity = 'Extreme'
      AND state2.StartTime IS NOT NULL
      AND state1.EventTypeId = 27
GROUP BY state1.AssetName
        ,state1.AssetId
        ,state1.Severity
        ,state1.EventId
        ,state1.[Group]
ORDER BY MIN(state1.StartTime) DESC;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...