Обновление T-SQL еще Вставьте хранимую процедуру с помощью функции секционирования Rank () (SQL 2008) - PullRequest
0 голосов
/ 28 июня 2011

У меня есть хранимая процедура, которая разбивает на части по дате, а затем ранжирует одно поле для каждой даты. Это было проверено и работает правильно. Текущая хранимая процедура УДАЛЯЕТ строки, если они существуют, а затем просто ВСТАВЛЯЕТ. Я хотел бы превратить это в хранимую процедуру, которая ОБНОВЛЯЕТ, если строка существует, ВСТАВЛЯЕТ новое значение. Я просмотрел многие сообщения, касающиеся Обновление еще Вставьте , но не смог получить правильный синтаксис обновления.

определение таблицы

   (@SeriesID   smallint,
    @SymbolID   smallint,
    @Date       smalldatetime,
    @Val        real)

Хранимая процедура, которую я хочу превратить в ОБНОВЛЕНИЕ, иначе ВСТАВКА:

CREATE PROCEDURE [dbo].[RankPerDate] 
        @StartDate  smallDateTime,
        @EndDate    smallDateTime,
        @SeriesToRankID smallint,
        @RankedSerieID  smallint
AS
    -- remove time series if it exists
    BEGIN
     DELETE FROM SeriesFloat
     WHERE SeriesID = @RankedSerieID AND (Date >= @StartDate) AND (Date <= @EndDate)    
    END

    BEGIN 
        INSERT INTO SeriesFloat
        SELECT SeriesID = @RankedSerieID, SymbolID, Date, RANK() OVER (PARTITION BY Date ORDER BY Val DESC) AS Val
        FROM SeriesFloat 
        WHERE (SeriesID = @SeriesToRankID)  AND (Date >= @StartDate) AND (Date <= @EndDate)
    END

Образцы входов хранимой процедуры:

 -- sample values for testing & parameters for stored procedure     
@StartDate = '1999-01-08 00:00:00';
@EndDate   = '1999-01-09 00:00:00';
@SeriesToRankID = 12; -- id of the series that is to be ranked
@RankedSerieID  = 35; -- id of the series that is to be updated/inserted

Пример таблицы данных до запроса:

SeriesID    SymbolID  Date                  Val
12          2011      1999-01-08 00:00:00   4215000
12          2012      1999-01-08 00:00:00   3215580
12          2013      1999-01-08 00:00:00   2029895
12          2011      1999-01-09 00:00:00   2029895
12          2012      1999-01-09 00:00:00   3395788
12          2013      1999-01-09 00:00:00   4029895
35          2012      1999-01-09 00:00:00   4 -- this row will be updated
35          2013      1999-01-09 00:00:00   8 -- this row will be updated

Результаты рейтинга:

SeriesID    SymbolID  Date                  Val
35          2011      1999-01-08 00:00:00   1 -- this row is inserted
35          2012      1999-01-08 00:00:00   2 -- this row is inserted
35          2013      1999-01-08 00:00:00   3 -- this row is inserted
35          2011      1999-01-09 00:00:00   3 -- this row is inserted
35          2012      1999-01-09 00:00:00   2 -- this row is updated
35          2013      1999-01-09 00:00:00   1 -- this row is updated

Пример таблицы данных после хранимой процедуры в прогоне:

SeriesID    SymbolID  Date                  Val
12          2011      1999-01-08 00:00:00   4215000
12          2012      1999-01-08 00:00:00   3215580
12          2013      1999-01-08 00:00:00   2029895
12          2011      1999-01-09 00:00:00   4029895
12          2012      1999-01-09 00:00:00   3395788
12          2013      1999-01-09 00:00:00   2029895
35          2011      1999-01-08 00:00:00   1 -- this row was inserted
35          2012      1999-01-08 00:00:00   2 -- this row was inserted
35          2013      1999-01-08 00:00:00   3 -- this row was inserted
35          2011      1999-01-09 00:00:00   3 -- this row was inserted
35          2012      1999-01-09 00:00:00   2 -- this row was updated
35          2013      1999-01-09 00:00:00   1 -- this row was updated

Может ли кто-нибудь привести пример того, как это делается?

1 Ответ

1 голос
/ 28 июня 2011

После того, как все зубы потянулись, вот что я придумал.Ваш ORDER BY Val в предложении RANK() OVER() не имеет смысла (поскольку Val - это всего лишь ранжирование, и его перераспределяют).Основываясь на вашем примере вывода, я предположил, что этот рейтинг должен определяться SymbolID.

USE tempdb;
GO

IF OBJECT_ID('dbo.SeriesFloat') IS NOT NULL
    DROP TABLE dbo.SeriesFloat;
GO

-- suggest using DATE since you don't care about time
-- also does the Val column really need to be REAL?
-- could probably be an INT.

CREATE TABLE dbo.SeriesFloat
(
    SeriesID   SMALLINT,
    SymbolID   SMALLINT,
    [Date]     SMALLDATETIME,
    Val        REAL
);

INSERT dbo.SeriesFloat SELECT 12, 2011, '1999-01-08', 4215000;
INSERT dbo.SeriesFloat SELECT 12, 2012, '1999-01-08', 3215580;
INSERT dbo.SeriesFloat SELECT 12, 2013, '1999-01-08', 2029895;
INSERT dbo.SeriesFloat SELECT 12, 2011, '1999-01-09', 4029895;
INSERT dbo.SeriesFloat SELECT 12, 2012, '1999-01-09', 3395788;
INSERT dbo.SeriesFloat SELECT 12, 2013, '1999-01-09', 2029895;
INSERT dbo.SeriesFloat SELECT 35, 2012, '1999-01-09', 4;
INSERT dbo.SeriesFloat SELECT 35, 2013, '1999-01-09', 8;

-- change these two params to test larger ranges (up to 2,048 days):

DECLARE @Start DATE = '1999-01-08',
        @End   DATE = '1999-01-09',
        @SeriesToRankID SMALLINT = 12,
        @RankedSerieID  SMALLINT = 35;

-- let's figure out the set of days - good for a range up to 2,048 days
-- if you need more than that, build a table of numbers

DECLARE @DaysInRange TABLE
(
    d DATE
);

INSERT @DaysInRange
    SELECT DISTINCT DATEADD(DAY, number, @Start)
        FROM [master].dbo.spt_values
        WHERE number BETWEEN 0 AND DATEDIFF(DAY, @Start, @End);

-- let's insert the rows that don't yet exist

INSERT dbo.SeriesFloat(SeriesID, SymbolID, [Date])
SELECT DISTINCT SeriesID = @RankedSerieID, s.SymbolID, d.d
    FROM dbo.SeriesFloat AS s 
    CROSS JOIN @DaysInRange AS d
    WHERE s.SeriesID = @SeriesToRankID
    AND NOT EXISTS
    (
        SELECT 1 FROM dbo.SeriesFloat 
            WHERE SeriesID = @RankedSerieID
            AND [Date] = d.d
            AND SymbolID = s.SymbolID
    );

-- then update all of them with ranking

WITH s AS 
(
    SELECT 
        SeriesID, SymbolID, [Date],
        Val = ROW_NUMBER() OVER (PARTITION BY [Date] ORDER BY SymbolID)
    FROM 
        dbo.SeriesFloat
    WHERE 
        SeriesID = @RankedSerieID
)
UPDATE sf SET Val = s.Val
    FROM dbo.SeriesFloat AS sf
    INNER JOIN s 
        ON sf.SymbolID = s.SymbolID
        AND sf.[Date] = s.[Date]
    WHERE sf.SeriesID = @RankedSerieID;

SELECT SeriesID, SymbolID, [Date], Val
    FROM dbo.SeriesFloat 
    ORDER BY SeriesID, [Date], Val;
GO

Мне не было интересно попробовать MERGE для решения этой проблемы, но вы можете проверить документы здесь:

http://msdn.microsoft.com/en-us/library/bb510625(SQL.100).aspx

Кроме того, зачем вам нужно сохранять рейтинг Val?Похоже, вы всегда сможете сгенерировать это во время запроса (используя представление, если вы часто используете этот столбец).

...