SQL Сервер - сопоставление групп и номеров непрерывных значений - PullRequest
0 голосов
/ 14 апреля 2020

У меня есть список операций с акциями, и я использую Over (Partition By) для расчета промежуточных итогов (позиций) по ценным бумагам. Со временем удержание в конкретной ценной бумаге может быть длинным, коротким или плоским. Я пытаюсь найти эффективный способ извлечь только транзакции, относящиеся к текущей позиции для каждой ценной бумаги.

Я создал упрощенный sqlfiddle , чтобы показать, что у меня есть до сих пор. Запрос cte генерирует промежуточный итог для каждой ценной бумаги (code_id) и определяет, когда авуары являются длинными (L), короткими (s) или плоскими (f). Что мне нужно, так это сгруппировать и сопоставить номера смежных значений L, S или F для каждого code_id.

На данный момент у меня есть следующее:

; WITH RunningTotals as
(
SELECT 
    *,
    RunningTotal = sum(qty) OVER (Partition By code_id Order By id) 
FROM
    TradeData
), LongShortFlat as
(
    SELECT 
        *,
        LSF = CASE 
                WHEN RunningTotal > 0 THEN 'L'
                WHEN RunningTotal < 0 THEN 'S'
                ELSE 'F'
            END
    FROM
        RunningTotals
)
SELECT 
    *
FROM 
    LongShortFlat r

Я думаю, что мне нужно сделать, это создать столбец GroupNum путем применения row_number для каждой группы L, S и F в каждом code_id, чтобы результаты выглядели так:

id code_id     qty  RunningTotal     LSF    GroupNum
  1       1       5             5      L            1
  2       1       2             7      L            1
  3       1       7            14      L            1
  4       1      -3            11      L            1
  5       1      -5             6      L            1
  6       1      -6             0      F            2
  7       1       5             5      L            3
  8       1       5            10      L            3
  9       1      -2             8      L            3
 10       1      -4             4      L            3
 11       2       5             5      L            1
 12       2       3             8      L            1
 13       2      -4             4      L            1
 14       2      -2             2      L            1
 15       2      -2             0      F            2
 16       2       6             6      L            3
 17       2      -5             1      L            3
 18       2      -5            -4      S            4
 19       2       2            -2      S            4
 20       2       4             2      L            5
 21       2      -5            -3      S            6
 22       2      -2            -5      S            6
 23       3       5             5      L            1
 24       3       2             7      L            1
 25       3       1             8      L            1

Я изо всех сил пытаюсь создать столбец GroupNum.

Заранее спасибо за помощь.

Ответы [ 2 ]

1 голос
/ 15 апреля 2020

[Исправлено]

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

;WITH cteBaseAgg
 as (
     --  Build the "sum increases over time" data
     SELECT
        row_number() over (partition by td.code_id order by td.code_id, td.Id)  RecurseKey
       ,td.code_id
       ,td.id
       ,td.qty
       ,sum(tdPrior.qty)  RunningTotal
       ,case
          when sum(tdPrior.qty) > 0 then 'L'
          when sum(tdPrior.qty) < 0 then 'S'
          else 'F'
        end  LSF
      from dbo.TradeData  td
       inner join dbo.TradeData  tdPrior
        on tdPrior.code_id = td.code_id  --  All for this code_id
         and tdPrior.id <= td.Id         --  For this and any prior Ids
      group by
        td.code_id
       ,td.id
       ,td.qty
    )
,cteRecurse
  as (
      --  "Set" the first row for each code_id
      SELECT
        RecurseKey
       ,code_id
       ,id
       ,qty
       ,RunningTotal
       ,LSF
       ,1  GroupNum

       from cteBaseAgg
       where RecurseKey = 1
      --  For each succesive row in each set, check if need to increment GroupNum
      UNION ALL SELECT
        agg.RecurseKey
       ,agg.code_id
       ,agg.id
       ,agg.qty
       ,agg.RunningTotal
       ,agg.LSF
       ,rec.GroupNum + case when rec.LSF = agg.LSF then 0 else 1 end
      from cteBaseAgg  agg
       inner join cteRecurse  rec
        on rec.code_id = agg.code_id
         and agg.RecurseKey - 1 = rec.RecurseKey
     )
--  Show results
SELECT
   id
  ,code_id
  ,qty
  ,RunningTotal
  ,LSF
  ,GroupNum
 from cteRecurse
 order by
   code_id
  ,id
0 голосов
/ 15 апреля 2020

Извините за то, что сделал этот вопрос немного сложнее, чем нужно, но ради закрытия я нашел решение, использующее функцию задержки. выше со следующим:

, a as
(
SELECT 
    *, 
    Lag(LSF, 1, LSF) OVER(Partition By code_id ORDER BY id) AS prev_LSF,
    Lag(code_id, 1, code_id) OVER(Partition By code_id ORDER BY id) AS prev_code
FROM
    LongShortFlat
), b as
(
SELECT
    id, 
    LSF,
    code_id, 
    Sum(CASE 
            WHEN LSF <> prev_LSF AND code_id = prev_code
            THEN 1 
            ELSE 0 
        END) OVER(Partition By code_id ORDER BY id) AS grp 
FROM
    a
)
select * from b order by id

Вот обновленный sqlfiddle .

...