-2

我有一个按日期分区然后为每个日期排列一个字段的存储过程。这已经过测试并且可以正常工作。当前存储过程删除行(如果存在),然后执行 INSERT。我想把它变成一个存储过程,如果该行存在则更新,否则插入一个新值。我查看了许多有关Update else Insert的帖子,但未能获得正确的更新语法。

表定义是

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

我希望变成 UPDATE else INSERT 的存储过程是:

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

谁能提供一个如何做到这一点的例子?

4

1 回答 1

1

在拔牙之后,这就是我想出的。您ORDER BY ValRANK() 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 排名?似乎您将始终能够在查询时生成它(如果您经常使用该列,则使用视图)。

于 2011-06-28T03:10:43.250 回答