Как правильно использовать Row_Number () (разбиение) для пула данных - PullRequest
3 голосов
/ 08 июня 2011

у нас есть следующая таблица (выходные данные уже упорядочены и разделены для понимания):

| PK | FK1 | FK2 |   ActionCode |         CreationTS  | SomeAttributeValue |
+----+-----+-----+--------------+---------------------+--------------------+
|  6 | 100 | 500 |       Create | 2011-01-02 00:00:00 |                  H |
----------------------------------------------------------------------------
|  3 | 100 | 500 |       Change | 2011-01-01 02:00:00 |                  Z |
|  2 | 100 | 500 |       Change | 2011-01-01 01:00:00 |                  X |
|  1 | 100 | 500 |       Create | 2011-01-01 00:00:00 |                  Y |
----------------------------------------------------------------------------
|  4 | 100 | 510 |       Create | 2011-01-01 00:30:00 |                  T |
----------------------------------------------------------------------------
|  5 | 100 | 520 | CreateSystem | 2011-01-01 00:30:00 |                  A |
----------------------------------------------------------------------------

что такое ActionCode?мы используем это в c#, и там оно представляет собой enum-значение

чего я хочу достичь?

ну, мне нужен следующий вывод:

| FK1 | FK2 |   ActionCode | SomeAttributeValue |
+-----+-----+--------------+--------------------+
| 100 | 500 |       Create |                  H |
| 100 | 500 |       Create |                  Z |
| 100 | 510 |       Create |                  T |
| 100 | 520 | CreateSystem |                  A |
-------------------------------------------------

хорошо, какова реальная логика?у нас есть несколько логических групп для составного ключа (FK1 + FK2).каждая из этих групп может быть разбита на разделы, которые начинаются с Create или CreateSystem.каждый раздел заканчивается на Create, CreateSystem или Change.Фактическое значение SomeAttributeValue для каждого раздела должно быть значением из последней строки раздела.

невозможно иметь следующий пул данных:

| PK | FK1 | FK2 |   ActionCode |         CreationTS  | SomeAttributeValue |
+----+-----+-----+--------------+---------------------+--------------------+
|  7 | 100 | 500 |       Change | 2011-01-02 02:00:00 |                  Z |
|  6 | 100 | 500 |       Create | 2011-01-02 00:00:00 |                  H |
|  2 | 100 | 500 |       Change | 2011-01-01 01:00:00 |                  X |
|  1 | 100 | 500 |       Create | 2011-01-01 00:00:00 |                  Y |
----------------------------------------------------------------------------

, а затем ожидать PK7, чтобы повлиять на PK 2 или PK 6, чтобы повлиять на PK 1.

Я даже не знаю, как / с чего начать ... Как я могу этого достичь?мы работаем в mssql 2005 +

РЕДАКТИРОВАТЬ:
есть дамп доступно:

  • instanceId: мой PK
  • tenantId: FK 1
  • campaignId: FK 2
  • callId: FK 3
  • refillCounter: FK 4
  • ticketType: ActionCode (1 &4 и 6: Create, 5 - Change, 3 необходимо игнорировать)
  • ticketType, profileId, contactPersonId, ownerId, processingStartTime, processingEndTime, memo, callWasPreselected, creatorId, creationTS, changerId, changeTS должны бытьвзято из Create (первая строка в разделе в группах)
  • CallState, reasonId, followUpDate, CallAttempts и CallAttemptsConsecutivelyNotReached должны быть взяты из последнего Create (который тогда будет "однострочным разделом"-in-group "/ то же, что и верхний) или Change (последняя строка в разделе в группах)

Ответы [ 2 ]

2 голосов
/ 09 июня 2011

Я предполагаю, что каждый раздел может содержать только один Create или CreateSystem, в противном случае ваши требования плохо определены.Следующее не проверено, так как у меня нет ни образца таблицы, ни образца данных в легко используемом формате:

;With Partitions as (
     Select
         t1.FK1,
         t1.FK2,
         t1.CreationTS as StartTS,
         t2.CreationTS as EndTS
     From
         Table t1
             left join
         Table t2
             on
                  t1.FK1 = t2.FK1 and
                  t1.FK2 = t2.FK2 and
                  t1.CreationTS < t2.CreationTS and
                  t2.ActionCode in ('Create','CreateSystem')
             left join
         Table t3
             on
                  t1.FK1 = t3.FK1 and
                  t1.FK2 = t3.FK2 and
                  t1.CreationTS < t3.CreationTS and
                  t3.CreationTS < t2.CreationTS and
                  t3.ActionCode in ('Create','CreateSystem')
       where
           t1.ActionCode in ('Create','CreateSystem') and
           t3.FK1 is null
), PartitionRows as (
     SELECT
         t1.FK1,
         t1.FK2,
         t1.ActionCode,
         t2.SomeAttributeValue,
         ROW_NUMBER() OVER (PARTITION_FRAGMENT_ID BY t1.FK1,T1.FK2,t1.StartTS ORDER BY t2.CreationTS desc) as rn
     from
         Partitions t1
             inner join
         Table t2
             on
                t1.FK1 = t2.FK1 and
                t1.FK2 = t2.FK2 and
                t1.StartTS <= t2.CreationTS and
                (t2.CreationTS < t1.EndTS or t1.EndTS is null)
)
select * from PartitionRows where rn = 1

(Обратите внимание, что здесь я использую все виды зарезервированных имен)

Основная логика: Разделы CTE используются для определения каждого раздела в терминах FK1, FK2, включающей временной отметки начала и исключительной конечной отметки времени.Это делается путем тройного соединения с базовой таблицей.строки из t2 выбираются так, чтобы они встречались после строк из t1, затем строки из t3 выбираются так, чтобы они находились между совпадающими строками из t1 и t2.Затем в предложении WHERE мы исключаем все строки из результирующего набора, в котором было найдено совпадение из t3 - в результате строка из t1 и строка из t2 представляют начало двух смежных разделов.

Затем второй CTE извлекает все строки из Table для каждого раздела, но присваивает оценку ROW_NUMBER() в каждом разделе на основе отсортированного по убыванию значения CreationTS, в результате чего ROW_NUMBER() 1 в пределахкаждый раздел является последней строкой.

Наконец, в пределах выбора мы выбираем те строки, которые встречаются последними в их соответствующих разделах.

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

0 голосов
/ 08 июня 2011

Это разрешимо с рекурсивным CTE. Здесь (при условии, что строки внутри разделов упорядочены по CreationTS):

WITH partitioned AS (
  SELECT
    *,
    rn = ROW_NUMBER() OVER (PARTITION BY FK1, FK2 ORDER BY CreationTS)
  FROM data
),
subgroups AS (
  SELECT
    PK, FK1, FK2, ActionCode, CreationTS, SomeAttributeValue, rn,
    Subgroup = 1,
    Subrank  = 1
  FROM partitioned
  WHERE rn = 1
  UNION ALL
  SELECT
    p.PK, p.FK1, p.FK2, p.ActionCode, p.CreationTS, p.SomeAttributeValue, p.rn,
    Subgroup = s.Subgroup + CASE p.ActionCode WHEN 'Change' THEN 0 ELSE 1 END,
    Subrank  = CASE p.ActionCode WHEN 'Change' THEN s.Subrank ELSE 0 END + 1
  FROM partitioned p
    INNER JOIN subgroups s ON p.FK1 = s.FK1 AND p.FK2 = s.FK2
      AND p.rn = s.rn + 1
),
finalranks AS (
  SELECT
    PK, FK1, FK2, ActionCode, CreationTS, SomeAttributeValue, rn,
    Subgroup, Subrank,
    rank = ROW_NUMBER() OVER (PARTITION BY FK1, FK2, Subgroup ORDER BY Subrank DESC)
    /* or: rank = MAX(Subrank) OVER (PARTITION BY FK1, FK2, Subgroup) - Subrank + 1 */
  FROM subgroups
)
SELECT PK, FK1, FK2, ActionCode, CreationTS, SomeAttributeValue
FROM finalranks
WHERE rank = 1
...