2016-03-21 2 views
4

Мне нужно запросить таблицу для последней версии записи для всех доступных дат (конец дневного времени). Пример ниже иллюстрирует то, чего я пытаюсь достичь.Эффективный запрос для последней версии записи с использованием SQL

Мой вопрос заключается в том, является ли дизайн таблицы (первичный ключ и т. Д.) И запросом LEFT OUTER JOIN для достижения этой цели самым эффективным образом.

CREATE TABLE [PriceHistory] 
(
    [RowID] [int] IDENTITY(1,1) NOT NULL, 
    [ItemIdentifier] [varchar](10) NOT NULL, 
    [EffectiveDate] [date] NOT NULL, 
    [Price] [decimal](12, 2) NOT NULL, 

    CONSTRAINT [PK_PriceHistory] 
     PRIMARY KEY CLUSTERED ([ItemIdentifier] ASC, [RowID] DESC, [EffectiveDate] ASC) 
) 

INSERT INTO [PriceHistory] VALUES ('ABC','2016-03-15',5.50) 
INSERT INTO [PriceHistory] VALUES ('ABC','2016-03-16',5.75) 
INSERT INTO [PriceHistory] VALUES ('ABC','2016-03-16',6.25) 
INSERT INTO [PriceHistory] VALUES ('ABC','2016-03-17',6.05) 
INSERT INTO [PriceHistory] VALUES ('ABC','2016-03-18',6.85) 
GO 

SELECT 
    L.EffectiveDate, L.Price 
FROM 
    [PriceHistory] L 
LEFT OUTER JOIN 
    [PriceHistory] R ON L.ItemIdentifier = R.ItemIdentifier 
        AND L.EffectiveDate = R.EffectiveDate 
        AND L.RowID < R.RowID 
WHERE 
    L.ItemIdentifier = 'ABC' and R.EffectiveDate is NULL 
ORDER BY 
    L.EffectiveDate 

Последующие действия: Таблица может содержать тысячи ItemIdentifiers, каждый из которых содержит данные о ценах на ценные бумаги. Историческая версия данных должна быть сохранена для целей аудита. Скажем, я запрашиваю таблицу и использую данные в отчете. Я храню @MRID = Max(RowID) на момент создания отчета. Теперь, если цена для «ABC» в «2016-03-16» будет исправлена ​​в более поздний срок, я могу изменить запрос с помощью @MRID и реплицировать отчет, который я запускал ранее.

+0

Почему вы делаете объединение? Не выбрано ли сверху 1 достаточно? –

+1

Благодарим вас за размещение данных ddl и образцов. Это облегчает помощь SO MUCH. Я бы хотел, чтобы все следовали за вами так! –

ответ

2

Слегка модифицированная версия @ SeanLange Ответим даст вам последнюю строку на дату, а не для каждого продукта:

with sortedResults as 
(
    select * 
     , ROW_NUMBER() over(PARTITION by ItemIdentifier, EffectiveDate 
          ORDER by ID desc) as RowNum 
    from PriceHistory 
) 

select ItemIdentifier, EffectiveDate, Price 
from sortedResults 
where RowNum = 1 
order by 2 
+1

Это, очевидно, то, что искали OP. Единственное предложение - не упорядочить по порядку. Яснее и безопаснее заказывать по названию столбца. –

+0

@SeanLange Согласен.Используя ORDER BY с порядковой позицией, код будет недостаточно читабельным, и, кроме того, если вы измените порядок столбцов в SELECT, запрос вернет неправильные результаты. –

2

Я предполагаю, что у вас более 1 ItemIdentifier в вашей таблице. Ваш дизайн немного проблематичен тем, что вы сохраняете версии данных в своей таблице. Однако вы можете сделать что-то подобное довольно просто, чтобы получить самую последнюю информацию для каждого ItemIdentifier.

with sortedResults as 
(
    select * 
     , ROW_NUMBER() over(PARTITION by ItemIdentifier order by EffectiveDate desc) as RowNum 
    from PriceHistory 
) 
select * 
from sortedResults 
where RowNum = 1 
+0

Было бы, если бы они хотели получить только значение для данного ItemIdentifier. –

+0

Этот запрос дает мне самую последнюю цену «ABC». Мне нужна последняя цена за каждую дату, которая существует в таблице. Запрос, который я предоставил, дает правильные результаты: временной ряд с одной ценой за дату. – c31983

+0

Это не ограничивает результаты только отображением ABC. Он покажет вам последнюю цену для каждого ItemIdentifier во всей таблице. Если вам нужен только один элемент, вы должны использовать один из TOP-предложений. –

1

Короткий ответ, нет.

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

Это был бы самый эффективный запрос того, что вы просите.

SELECT 
    L.EffectiveDate, 
    L.Price 
FROM 
    (
     SELECT 
      L.EffectiveDate, 
      L.Price, 
      ROW_NUMBER() OVER (
       PARTITION BY 
        L.ItemIdentifier, 
        L.EffectiveDate 
       ORDER BY RowID DESC) RowNum 
     FROM [PriceHistory] L 
     WHERE L.ItemIdentifier = 'ABC' 
    ) L 
WHERE 
    L.RowNum = 1; 
+0

Первый предложенный запрос возвращает только одну запись. Мне нужен временной ряд, который содержит самое последнее значение для каждой даты. Запрос, который я включил в мой пост, дает правильные результаты. Я не знаком с оператором CROSS APPLY, указанным во втором предложении, и у меня возникли проблемы с его выполнением. Откуда [пункт]? – c31983

+0

Хорошо, я исправил это, в основном, чтобы соответствовать тому, как другие ответы уже структурированы. Проблема возникает, если вы попытаетесь присоединиться к этому набору результатов в другую таблицу или использовать в более крупном запросе. Вот где CROSS APPLY может вступить в игру. Я удалил это предложение, потому что это не похоже на то, что вы ищете. –

Смежные вопросы