Запрос на отсутствующие элементы - PullRequest
3 голосов
/ 15 февраля 2011

У меня есть таблица со следующей структурой:

timestamp | name | value
0         | john | 5
1         | NULL | 3
8         | NULL | 12
12        | john | 3
33        | NULL | 4
54        | pete | 1
180       | NULL | 4
400       | john | 3
401       | NULL | 4
592       | anna | 2

Теперь, что я ищу, это запрос, который даст мне сумму значений для каждого имени, и обрабатывает нули между ними (упорядоченный по метке времени) в качестве первого ненулевого имени в списке, как если бы таблица была следующей:

timestamp | name | value
0         | john | 5
1         | john | 3
8         | john | 12
12        | john | 3
33        | pete | 4
54        | pete | 1
180       | john | 4
400       | john | 3
401       | anna | 4
592       | anna | 2

, и я бы запросил SUM(value), name from this table group by name.Я думал и пытался, но я не могу найти правильное решение.Я посмотрел на рекурсивные общие табличные выражения и думаю, что ответ может лежать там, но я не смог их правильно понять.

Эти таблицы являются лишь примерами, и я не знаю отметку временизначения заранее.

Может ли кто-нибудь помочь мне?Помощь будет очень ценится.

Ответы [ 3 ]

1 голос
/ 15 февраля 2011

Вам не нужен CTE, просто простой подзапрос.

select t.timestamp, ISNULL(t.name, (
    select top(1) i.name
    from inputs i
    where i.timestamp < t.timestamp
    and i.name is not null
    order by i.timestamp desc
    )), t.value
from inputs t

И суммирование отсюда

select name, SUM(value) as totalValue
from
(
    select t.timestamp, ISNULL(t.name, (
        select top(1) i.name
        from inputs i
        where i.timestamp < t.timestamp
        and i.name is not null
        order by i.timestamp desc
        )) as name, t.value
    from inputs t
) N
group by name
1 голос
/ 15 февраля 2011
With Inputs As
    (
    Select 0 As [timestamp], 'john' As Name, 5 As value
    Union All Select 1, NULL, 3
    Union All Select 8, NULL, 12
    Union All Select 12, 'john', 3
    Union All Select 33, NULL, 4
    Union All Select 54, 'pete', 1
    Union All Select 180, NULL, 4
    Union All Select 400, 'john', 3
    Union All Select 401, NULL, 4
    Union All Select 592, 'anna', 2
    )
    , NamedInputs As
    (
    Select I.timestamp
        , Coalesce (I.Name
            ,   (
                Select I3.Name
                From Inputs As I3
                Where I3.timestamp = (
                                    Select Max(I2.timestamp)
                                    From Inputs As I2
                                    Where I2.timestamp < I.timestamp
                                        And I2.Name Is not Null
                                    )
                )) As name
        , I.value
    From Inputs As I
    )
Select NI.name, Sum(NI.Value) As Total
From NamedInputs As NI
Group By NI.name

Кстати, что было бы на несколько порядков быстрее, чем любой запрос, чтобы сначала исправить данные. Т.е. обновите столбец имени, чтобы он имел правильное значение, сделайте его необнуляемым, а затем запустите простую Группировку, чтобы получить ваши итоги.

Дополнительное решение

Select Coalesce(I.Name, I2.Name), Sum(I.value) As Total
From Inputs As I
    Left Join   (
                Select I1.timestamp, MAX(I2.Timestamp) As LastNameTimestamp
                From Inputs As I1
                    Left Join Inputs As I2
                        On I2.timestamp < I1.timestamp
                            And I2.Name Is Not Null   
                Group By I1.timestamp
                ) As Z
        On Z.timestamp = I.timestamp        
    Left Join Inputs As I2
        On I2.timestamp = Z.LastNameTimestamp
Group By Coalesce(I.Name, I2.Name)
0 голосов
/ 16 февраля 2011

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

;WITH
numbered_table AS (
  SELECT
    timestamp, name, value,
    rownum = ROW_NUMBER() OVER (ORDER BY timestamp)
  FROM your_table
),
filled_table AS (
  SELECT
    timestamp,
    name,
    value
  FROM numbered_table
  WHERE rownum = 1

  UNION ALL

  SELECT
    nt.timestamp,
    name = ISNULL(nt.name, ft.name),
    nt.value
  FROM numbered_table nt
    INNER JOIN filled_table ft ON nt.rownum = ft.rownum + 1
)
SELECT *
FROM filled_table
/* or go ahead aggregating instead */
...