2014-01-25 4 views
3

У меня есть очень большой стол, содержащий историю цен.SQL Server 2012 очищает набор данных

CREATE TABLE [dbo].[SupplierPurchasePrice](
    [SupplierPurchasePriceId] [int] IDENTITY(1,1) PRIMARY KEY, 
    [ExternalSupplierPurchasePriceId] [varchar](20) NULL, 
    [ProductId] [int] NOT NULL, 
    [SupplierId] [int] NOT NULL, 
    [Price] [money] NOT NULL, 
    [PreviousPrice] [money] NULL, 
    [SupplierPurchasePriceDate] [date] NOT NULL, 
    [Created] [datetime] NULL, 
    [Modified] [datetime] NULL, 
) 

За товар (идентификатор) и поставщик (Id) У меня есть сотни цен. Теперь необходимо удалить основную часть данных, но при этом сохранить некоторые исторические данные. Для каждого продукта (Id) и поставщика (Id) я хочу сохранить, скажем, 14 записей. Но не первый или последний 14. Я хочу сохранить первую и последнюю запись. А затем сохраняйте 12 записей равномерно между первым и последним. Таким образом, я держу некоторую историю в такт.

Я не могу понять, как это сделать непосредственно с хранимой процедурой, а не через мой C# ORM (что слишком медленно).

+2

Вы можете добавить скрипт sql? –

+0

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

ответ

3

Вот прямой подсчет подход к решению проблемы:

select spp.* 
from (select spp.*, 
      sum(12.5/(cnt - 1)) over (partition by SupplierId, ProductId 
             order by SupplierPurchasePriceId 
             ) as cum 
     from (select spp.*, 
        row_number() over (partition by SupplierId, ProductId 
             order by SupplierPurchasePriceId 
            ) as seqnum, 
        count(*) over (partition by SupplierId, ProductId) as cnt, 
      from SupplierPurchasePrice spp 
      ) spp 
    ) spp 
where seqnum = 1 or seqnum = cnt or cnt <= 14 or 
     (floor(cumgap) <> floor(cumgap - 12.5/(cnt - 1))); 

Задача заключается в определении, где 12 записей между ними идти. Это вычисляет средний «пробел» в записях, как 12.5/(cnt - 1). Это константа, которая затем накапливается по записям. Он будет идти от 0 до 12,5 в самой большой записи. Идея состоит в том, чтобы захватить любую запись, где это передает целочисленное значение. Итак, если кумулятивная информация идет от 2.1 до 2.3, тогда запись не выбрана. Если он идет от 2,9 до 3,1, тогда выбирается запись.

Число 12,5 не является магическим. Любое число от 12 до 13 должно делать. За исключением вопроса с выбором самых старых и самых последних значений. Я выбрал 12.5, чтобы быть уверенным, что они не рассчитывают на 12.

Вы можете увидеть ту же логическую работу here в SQL Fiddle. Столбец флажка показывает, что будет выбрано, и totflag проверяет, что выбрано ровно 14.

1

Я хотел бы попробовать что-то вроде

select 
    to_keep.SupplierPurchasePriceId 
from 
    (select 
     foo.SupplierPurchasePriceId, 
     row_number() over (partition by ProductId, SupplierId, tile_num order by Created) as takeme 
    from 
     (select 
     SupplierPurchasePriceId, 
     ProductId, 
     SupplierId, 
     Created, 
     ntile(13) over(partition by ProductId, SupplierId order by Created) as tile_num 
     from 
     SupplierPurchasePrice 
    ) foo 
    ) to_keep 
where 
    to_keep.takeme = 1 

union 

select distinct 
    last_value(SupplierPurchasePriceId) over (partition by ProductId, SupplierId order by Created range between UNBOUNDED PRECEDING and UNBOUNDED FOLLOWING) as SupplierPurchasePriceId 
from 
    SupplierPurchasePrice 

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

1

(Оговорка - Я прочитал ваш вопрос неправильно Это сохраняет первый, последние и каждую четырнадцатая запись между ними, не обязательно в общей сложности 14 записей.).

После резервного копирования базы данных, и пытается сухой ход это первое, чтобы проверить, что это приведет к удалению требуемых данных (предполагается, что здесь является то, что хронологический порядок записей устанавливается SupplierPurchasePriceDate - изменение ORDER BY в зависимости от обстоятельств, если не так)

WITH CTE AS 
(
    SELECT 
     [SupplierId], 
     [ProductId], 
     [SupplierPurchasePriceId], 
     [SupplierPurchasePriceDate], 
     ROW_NUMBER() OVER (Partition BY [SupplierId], [ProductId] 
      ORDER BY [SupplierPurchasePriceDate]) -1 AS Rnk 
    FROM [dbo].[SupplierPurchasePrice] 
) 
SELECT cteRank.* 
    FROM 
     CTE cteRank 
     JOIN 
     (SELECT ProductId, SupplierId, MAX(Rnk) as MaxRnk 
      FROM CTE cteMax 
      GROUP BY ProductId, SupplierID) X 
     ON cteRank.SupplierId = X.SupplierId AND cteRank.ProductId = X.ProductId 
    WHERE cteRank.Rnk % 12 != 0 AND cteRank.Rnk != X.MaxRnk; 

Если это работает, как ожидалось, и поскольку кажется, что у вас уже есть суррогатный ключ, тогда шаг удаления просто удаляется ИНГИ совпавших суррогатных ключей:

DELETE FROM [dbo].[SupplierPurchasePrice] 
WHERE [SupplierPurchasePriceId] IN (...) 

Он работает путем ранжирования данных о ценах по дате, а затем удалять записи, которые не по модулю 12 (Изменения согласно вашему требованию). -1 - потому что ROW_NUMBER() основан на 1. Первая запись сохраняется, потому что в качестве первого соответствия Modulo. Шаг MaxRnk должен также сохранить самую последнюю цену для каждой пары Supplier, Product.

Обратите внимание, что это сохранит первый, каждый последующий 12-й и последний. Таким образом, вероятно, будет неравномерный разрыв между последней (Max) и второй последней записью (последний Modulo = 0). Но, конечно, достаточно близко?

SqlFiddle here

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