2011-06-28 56 views
-2

我有一个存储过程按日期进行分区,然后为每个日期排序一个字段。这已经过测试并正常工作。当前存储过程删除行(如果存在),然后执行INSERT。我想把它变成一个存储过程,如果行存在则更新INSERT一个新的值。我看了很多有关更新的帖子,其他插入,但一直没能得到正确的更新语法。T-SQL更新else使用Rank()分区函数插入存储过程(SQL 2008)

表定义是

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

存储过程我想变成UPDATE别的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 

任何人都可以提供如何做到这一点的例子吗?

+0

为什么@SeriesID = 12和@OutputID = 35?后来你说SELECT SELECTID = @OutputID ... WHERE SeriesID = @SeriesID。错字,故意混淆,介于两者之间?另外,您可以指定和/或标记您是否正在使用Microsoft SQL Server(tag = sql-server)以及您正在使用的版本(例如sql-server-2005,sql-server-2008)不具有所有版本的功能的优势。 –

+1

使用sql-server-2008(添加标签) –

+0

SeriesID是要排列的系列的标识。它是存储过程的输入之一。 –

回答

1

拉完所有的牙齿后,这就是我想出来的。 RANK() OVER()条款中的ORDER BY Val没有意义(因为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

顺便说一句,你为什么需要存储的瓦尔排名?看起来像你总是能够在查询时生成这个(如果你使用这个列很多,使用视图)。

+0

谢谢你。在这种情况下val可以通过INT,但是这个表大多持有需要实际类型的值。日期同上。 –

+0

有时间来运行这个。不是解决方案,因为排名基于Val not SymbolID。请不要在此花更多时间。我的删除和插入(存储过程在我原来的问题中)即使它不是理想的。 –

+0

根据您提供的样本数据,您的描述没有意义。 SeriesID = 35的两行分别是2012/4和2013/8的SymbolID/Val。然而,在输出中,你已经改变了它,以便分配给这些行的Val是2和1.基于4和8的2和1是如何的? –