Self Join на переменных данных - PullRequest
0 голосов
/ 03 декабря 2009

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

Fiscal Year | Fiscal Quarter | Fiscal Week | Data
2009        | 2              | 22          | 9.5
2009        | 2              | 24          | 8.8
2009        | 2              | 26          | 8.8
2009        | 3              | 28          | 8.8
2009        | 3              | 31          | 9.1
2009        | 3              | 33          | 8.8

Я хотел бы написать запрос, который выдает следующее:

Fiscal Year | Fiscal Quarter | Fiscal Week | Data | Trend
2009        | 2              | 22          | 9.5  | NULL
2009        | 2              | 24          | 8.8  | -0.7
2009        | 2              | 26          | 8.8  | 0
2009        | 3              | 28          | 8.8  | 0
2009        | 3              | 31          | 9.1  | 0.3
2009        | 3              | 33          | 8.8  | -0.3 

Я знаю, что это может быть легко достигнуто путем простого объединения таблицы с предыдущей финансовой неделей, однако это не всегда будет простой t1.[Fiscal Week] = t2.[Fiscal Week] - 2, потому что иногда разница составляет 3 недели.

Я могу легко получить максимальную запись с помощью чего-то вроде этого:

SELECT
    MAX(t1.[Fiscal Week]) "LastWeek"
FROM t1
WHERE t1.[Fiscal Year] = 2009
    AND t1.[Fiscal Week] < 31

Но я в недоумении, когда дело доходит до абстрагирования, чтобы заставить объединение работать.

Как я могу самостоятельно присоединиться к "самой большой финансовой неделе, которая меньше текущей записи"?

Ответы [ 4 ]

3 голосов
/ 03 декабря 2009

Самый простой способ сделать это с помощью Sql 2005+ - это использовать функции ранжирования и управления окнами - просто разделить / упорядочить данные и назначить соответствующую последовательность для каждой записи (в этом случае вы делите с помощью fiscalYear и упорядочение по fiscalWeek через это окно) - будет выглядеть примерно так:

with data as
(
    select  row_number() over (partition by a.fiscalYear order by a.fiscalWeek) as rn,
            a.fiscalYear, a.fiscalQuarter, a.fiscalWeek, a.Data
    from    #TableName a
)
select  a.fiscalYear, a.fiscalQuarter, a.fiscalWeek, a.Data, 
        a.Data - b.Data as Trend
from    data a
left join data b
on      a.fiscalYear = b.fiscalYear
and     a.rn = b.rn + 1
order by a.fiscalYear, a.rn

Этот конкретный запрос не разрешит расширение через границы fiscalYear - если вы хотите расширить границы, чтобы пересечь границы этого года, вы просто хотели бы отбросить предложение "partition by" и условие соединения "fiscalYear" и вместо этого упорядочить набор по комбинации фискального года и фискальной недели, что-то вроде этого:

with data as
(
    select  row_number() over (order by a.fiscalYear + a.fiscalWeek) as rn,
            a.fiscalYear, a.fiscalQuarter, a.fiscalWeek, a.Data
    from    #TableName a
)
select  a.fiscalYear, a.fiscalQuarter, a.fiscalWeek, a.Data, 
        a.Data - b.Data as Trend
from    data a
left join data b
on      a.rn = b.rn + 1
order by a.rn
1 голос
/ 03 декабря 2009
DECLARE @Fiscal TABLE
  ( 
   FiscalYear int
  ,FiscalQuarter int
  ,FiscalWeek int
  ,[Data] decimal(4,1) 
  )

INSERT INTO @Fiscal
          ( FiscalYear, FiscalQuarter, FiscalWeek, [Data] )
SELECT 2009, 2, 22, 9.5 UNION
SELECT 2009, 2, 24, 8.8 UNION
SELECT 2009, 2, 26, 8.8 UNION
SELECT 2009, 3, 28, 8.8 UNION
SELECT 2009, 3, 31, 9.1 UNION
SELECT 2009, 3, 33, 8.8 UNION
SELECT 2010, 1, 1, 9.0 UNION
SELECT 2010, 1, 2, 9.2

;
WITH  abcd
        AS ( SELECT FiscalYear
                   ,FiscalQuarter
                   ,FiscalWeek
                   ,[Data]
                   ,row_number() OVER ( ORDER BY FiscalYear, FiscalWeek ) AS rn
             FROM   @Fiscal
           )
  SELECT  a.FiscalYear
         ,a.FiscalQuarter
         ,a.FiscalWeek
         ,a.[Data]
         ,a.[Data] - b.[Data] AS [Trend]
  FROM    abcd AS a
          LEFT JOIN abcd AS b ON b.rn = ( a.rn - 1 )
  ORDER BY a.FiscalYear
         ,a.FiscalWeek
1 голос
/ 03 декабря 2009

это работает? Встроенный запрос немного устарел, и, вероятно, есть лучший способ сделать это ...

select
  [fiscal year]
, [fiscal quarter]
, [fiscal week]
, [data] 
, (
    select top 1 (i.data - t1.data) from t1 as i
    where i.[fiscal year] >= t1.[fiscal year]
    and i.[fiscal quarter] >= t1.[fiscal quarter]
    and i.[fiscal week] >= t1.[fiscal week]
    and (
          i.[fiscal year] <> t1.[fiscal year]
       or i.[fiscal quarter] <> t1.[fiscal quarter]
       or i.[fiscal week] <> t1.[fiscal week]
    )
    order by [fiscal year] asc, [fiscal quarter] asc, [fiscal week] asc
) as trend
from t1
1 голос
/ 03 декабря 2009

Если вы используете SQL Server 2005, вы можете использовать предложение CROSS APPLY.

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

CREATE FUNCTION dbo.fn_GetFiscalWeekBeforeThis(@Year AS int, 
  @CurrentWeek as int)
  RETURNS TABLE
AS
RETURN
  SELECT TOP 1 *
  FROM t1
  WHERE [Fiscal Year] = @Year 
  AND [Fiscal Week] < @CurrentWeek
  ORDER BY [Fiscal Week] DESC
GO

А потом,

SELECT A.*,
A.Data - B.Data
FROM
t1 A
CROSS APPLY 
   dbo.fn_GetFiscalWeekBeforeThis(A.[Fiscal Year],  
   A.[Fiscal Week]) AS B

РЕДАКТИРОВАТЬ: Обратите внимание, что я адаптировал SQL, глядя на статью и без IDE.
Кроме того, я не знаю - как это будет работать для первого ряда.

Итак, будьте добры:)

РЕДАКТИРОВАТЬ2: Дайте мне знать, если это не работает вообще.
Это поможет мне узнать - не отвечать на вопросы, не проверяя результаты.

РЕДАКТИРОВАТЬ3: я удалил необходимость функции Quarter из функции.

...