getdate()性能问题sql server 2000

时间:2012-09-12 06:23:22

标签: sql-server sqlperformance

我有一张表格,其中有一些列

symbol |market| bid_price | ask_price | update_time
------- ------ ----------- ----------- ------------
ABC        US     123.00     675.00         20012-09-10 4:24:32.986
CDE        SG     456.00     545.00         20012-09-10 4:26:32.986

我的应用程序调用SP来更新此表。

update price_tbl 
set bid_price=@bid_price, ask_price=@ask_price, update_time = getdate() 
where market = @market and symbol = @symbol

但我的应用程序每秒调用超过1000次更新。所以这个SP不够快,不能更新表格。我查了一下,发现getdate()函数是瓶颈。但是这个系统用sql server 2000运行。

  1. 除了getdate()Any
  2. 之外,我可以使用任何其他方法来更新时间
  3. 提高此次更新效果的其他提案。
  4. 仅供参考:这个price_tbl有大约2000条记录。


    测试结果。

    表定义 .................................................. ........................................

    CREATE TABLE [dbo].[GLDPrice](
        [Company_code] [varchar](10) NOT NULL,
        [Symbol] [varchar](10) NOT NULL,
        [SymbolA] [varchar](10) NULL,
        [SymbolB] [varchar](10) NULL,
        [Market] [char](2) NOT NULL,
        [ExchangeCode] [varchar](4) NULL,
        [Remark] [char](6) NULL,
        [Last_done] [numeric](19, 8) NULL,
        [Change] [numeric](19, 8) NOT NULL,
        [Open_Price] [numeric](19, 8) NULL,
        [Closing_Price] [numeric](19, 8) NULL,
        [Buy_Price] [numeric](19, 8) NULL,
        [Sell_Price] [numeric](19, 8) NULL,
        [Day_High] [numeric](19, 8) NULL,
        [Day_Low] [numeric](19, 8) NULL,
        [Time_done] [char](5) NULL,
        [Cumm_vol] [int] NOT NULL,
        [Buy_quantity] [int] NULL,
        [Sell_quantity] [int] NULL,
        [Per_Change] [numeric](19, 8) NULL,
        [GLDBid] [numeric](19, 8) NULL,
        [GLDAsk] [numeric](19, 8) NULL,
        [GlobalGLDBid] [numeric](19, 8) NULL,
        [GlobalGLDAsk] [numeric](19, 8) NULL,
        [GLDBuyLastDone] [numeric](19, 8) NULL,
        [GLDSellLastDone] [numeric](19, 8) NULL,
        [GLDBuyLDUptTime] [datetime] NULL,
        [GLDSellLDUptTime] [datetime] NULL,
        [UpdateTime] [datetime] NOT NULL,
     CONSTRAINT [PK_GLDPrice] PRIMARY KEY CLUSTERED 
    (
        [Company_code] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    
    GO
    SET ANSI_PADDING OFF
    GO
    ALTER TABLE [dbo].[GLDPrice] ADD  CONSTRAINT [DF_GLDPrice_SymbolA]  DEFAULT (' ') FOR [SymbolA]
    GO
    ALTER TABLE [dbo].[GLDPrice] ADD  CONSTRAINT [DF_GLDPrice_Last_done]  DEFAULT (0) FOR [Last_done]
    GO
    ALTER TABLE [dbo].[GLDPrice] ADD  DEFAULT (getdate()) FOR [UpdateTime]
    GO
    

    SP with getdate() .................................................. ........................................

    ALTER PROCEDURE [dbo].[update_test_one]      
     @Symbol varchar(10),      
     @Market varchar(5),       
     @BuyPrice money,      
     @SellPrice money,      
     @LastPrice money,      
     @High  money,      
     @Low  money      
    
    AS      
    DECLARE @GLDBidPrice money      
    DECLARE @GLDAskPrice money      
    DECLARE @GlobalGLDBid  money      
    DECLARE @GlobalGLDAsk  money      
    DECLARE @GLDBidAdjust money      
    DECLARE @GLDAskAdjust money      
    DECLARE @GlobalBidAdjust money      
    DECLARE @GlobalAskAdjust money    
    
    SELECT @GLDBidPrice = @BuyPrice + 5      
    SELECT @GLDAskPrice = @SellPrice + 5      
    SELECT @GlobalGLDBid = @BuyPrice + 5      
    SELECT @GlobalGLDAsk = @SellPrice + 5
    
    UPDATE dbo.GLDprice      
    SET Buy_price = @BuyPrice,      
    Sell_price = @SellPrice,      
    GLDBid = @GLDBidPrice,      
    GLDAsk = @GLDAskPrice,      
    Day_high = @High,      
    Day_Low = @Low,      
    GlobalGLDBid = @GlobalGLDBid,      
    GlobalGLDAsk = @GlobalGLDAsk,    
    UpdateTime=GetDate(),
    Last_Done = @LastPrice    
    WHERE  Symbol = @symbol AND Market = @Market
    

    SP没有getdata() .................................................. ..........................................

    ALTER PROCEDURE [dbo].[update_test_two]      
     @Symbol varchar(10),      
     @Market varchar(5),       
     @BuyPrice money,      
     @SellPrice money,      
     @LastPrice money,      
     @High  money,      
     @Low  money      
    
    AS      
    DECLARE @GLDBidPrice money      
    DECLARE @GLDAskPrice money      
    DECLARE @GlobalGLDBid  money      
    DECLARE @GlobalGLDAsk  money      
    DECLARE @GLDBidAdjust money      
    DECLARE @GLDAskAdjust money      
    DECLARE @GlobalBidAdjust money      
    DECLARE @GlobalAskAdjust money    
    
    SELECT @GLDBidPrice = @BuyPrice + 5      
    SELECT @GLDAskPrice = @SellPrice + 5      
    SELECT @GlobalGLDBid = @BuyPrice + 5      
    SELECT @GlobalGLDAsk = @SellPrice + 5
    
    UPDATE dbo.GLDprice      
    SET Buy_price = @BuyPrice,      
    Sell_price = @SellPrice,      
    GLDBid = @GLDBidPrice,      
    GLDAsk = @GLDAskPrice,      
    Day_high = @High,      
    Day_Low = @Low,      
    GlobalGLDBid = @GlobalGLDBid,      
    GlobalGLDAsk = @GlobalGLDAsk,    
    Last_Done = @LastPrice    
    WHERE  Symbol = @symbol AND Market = @Market
    

    测试脚本 .................................................. .........................................

    DECLARE @return_value int
    DECLARE @count int
    DECLARE @start datetime
    
    
    SET NOCOUNT ON
    SET @count = 0;
    set @start = CURRENT_TIMESTAMP
    
    WHILE (@count < 10000)
    BEGIN
    SET @count = @count + 1
    EXEC    [dbo].[update_test_one]
            @Symbol = N'I9T',
            @Market = N'SG',
            @BuyPrice = 0.8,
            @SellPrice = 0.8,
            @LastPrice = 0.8,
            @High = 0.8,
            @Low = 0.8
    
    EXEC    [dbo].[update_test_one]
            @Symbol = N'0001.HK',
            @Market = N'HK',
            @BuyPrice = 112,
            @SellPrice = 112,
            @LastPrice = 112,
            @High = 112,
            @Low = 112
    END
    
    print 'Test 01 : ' + CONVERT(varchar(20),DATEDIFF(millisecond,@start,CURRENT_TIMESTAMP))
    
    SET @count = 0;
    set @start = CURRENT_TIMESTAMP
    WHILE (@count < 10000)
    BEGIN
    SET @count = @count + 1
    EXEC    [dbo].[update_test_two]
            @Symbol = N'I9T',
            @Market = N'SG',
            @BuyPrice = 0.8,
            @SellPrice = 0.8,
            @LastPrice = 0.8,
            @High = 0.8,
            @Low = 0.8
    
    EXEC    [dbo].[update_test_two]
            @Symbol = N'0001.HK',
            @Market = N'HK',
            @BuyPrice = 112,
            @SellPrice = 112,
            @LastPrice = 112,
            @High = 112,
            @Low = 112
    END
    
    print 'Test 02 : ' + CONVERT(varchar(20),DATEDIFF(millisecond,@start,CURRENT_TIMESTAMP))
    GO
    

    结果:

    Test 01 : 82310
    Test 02 : 12176
    

    反向测试的结果。

    Test 02 : 15413
    Test 01 : 81636
    

4 个答案:

答案 0 :(得分:3)

  

我查了一下,发现getdate()函数是瓶颈

我绝对可以100%保证你的发现是错误的。性能问题绝不是getdate()。对于记录,不仅在性能中看不到调用getdate() 1000次,而且getdate()`恰好是runtime constant function

如果您想解决SQL Server性能问题,请按照Waits and Queues之类的方法或按照Performance Troubleshooting Flowchart进行操作。

我敢打赌,性能问题在于:

where market = @market and symbol = @symbol

您要么错过了(market, symbol)上的相应索引,要么参数类型@market和/或@symbol不正确,并且由于data type precedence rules原因导致表格不正确扫描而不是索引搜索。

答案 1 :(得分:1)

由于你还没有提出基准测试,我认为我会做一个,然后每个人都可以在方法论上挖洞。因此CW: - ):

SET NOCOUNT ON
go
create table prices (symbol char(3) not null,market char(2) not null,
    bid_price decimal(18,4) not null,ask_price decimal(18,4) not null,
    update_time datetime not null,
    constraint PK_prices PRIMARY KEY (symbol,market)
)
GO
create table #Digits (d int not null)
insert into #Digits (d)
select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all
select 5 union all select 6 union all select 7 union all select 8 union all select 9
create table #Numbers (n int not null)
insert into #Numbers (n)
select d1.d * 10000 + d2.d * 1000 + d3.d * 100 + d4.d * 10 + d5.d
from #Digits d1,#Digits d2,#Digits d3,#Digits d4,#Digits d5

insert into prices (symbol,market,bid_price,ask_price,update_time)
select
    CHAR(65+n/(26*26)) + CHAR(65+((n/26)%26)) + CHAR(65+(n%26)),m.market,n,n+5,GETDATE()
from
    #Numbers nu,
    (select 'US' as market union all select 'SG') m
where nu.n < (26*26*26)
go
drop table #Digits
drop table #Numbers

以上为我们设置了一个约30000行的表(所以,比你说的更多)。它还会使表格变暖,因为最近访问过每一行。我不记得哪些调用会清除SQL Server 2000的内容,但它应该足够公平,现在可以按任何顺序运行测试。

declare @market char(2)
declare @symbol char(3)
declare @price1 decimal(18,4)
declare @price2 decimal(18,4)
declare @start datetime
declare updates cursor local static for select symbol,market,bid_price,ask_price from prices
open updates

set @start = CURRENT_TIMESTAMP

fetch next from updates into @symbol,@market,@price1,@price2
while @@FETCH_STATUS = 0
begin
    update prices 
    set bid_price=@price2, ask_price=@price1
    where market = @market and symbol = @symbol

    fetch next from updates into @symbol,@market,@price1,@price2
end
close updates
deallocate updates

print '"FAST" took ' + CONVERT(varchar(20),DATEDIFF(millisecond,@start,CURRENT_TIMESTAMP))
go
declare @market char(2)
declare @symbol char(3)
declare @price1 decimal(18,4)
declare @price2 decimal(18,4)
declare @start datetime
declare updates cursor local static for select symbol,market,bid_price,ask_price from prices
open updates

set @start = CURRENT_TIMESTAMP

fetch next from updates into @symbol,@market,@price1,@price2
while @@FETCH_STATUS = 0
begin
    update prices 
    set bid_price=@price2, ask_price=@price1,update_time = GETDATE()
    where market = @market and symbol = @symbol

    fetch next from updates into @symbol,@market,@price1,@price2
end
close updates
deallocate updates

print '"SLOW" took ' + CONVERT(varchar(20),DATEDIFF(millisecond,@start,CURRENT_TIMESTAMP))
go
drop table prices

这两个游标循环之间的唯一区别是第二个游标循环基于GETDATE()的更新执行update_time,而第一个不执行此列的更新(正如您所指示的那样)你的测试)。

在我的机器(*)上,我得到了结果:

"FAST" took 20503
"SLOW" took 20436

这表明使用GETDATE()的“慢”方法速度提高了0.1秒。

(*)

  

Microsoft SQL Server 2000 - 8.00.2039(Intel X86)

     

2005年5月3日23:18:38

     

版权所有(c)1988-2003 Microsoft Corporation

     

Windows NT 5.2(Build 3790:Service Pack 2)上的标准版

答案 2 :(得分:0)

DECLARE @CurrentDate DateTime;
SELECT @CurrentDate = getdate();

update price_tbl 
set bid_price=@bid_price, ask_price=@ask_price, update_time = @CurrentDate
where market = @market and symbol = @symbol

看看是否有效

答案 3 :(得分:0)

我有这样的场景,我给出了这样的解决方案, 我将日期时间作为参数从前端传递给SP。否则,如果你想在SP本身处理,你可以这样做,比如

DECLARE @time DATETIME
Set @time = getdate()

并且你可以将它传递给你的参数列表,但请注意,在某些情况下我们也不会得到精确的毫秒数。因为所有记录都具有相同的当前时间,只有不同的第二个值。

在前端你可以传递值为,

DateTime.Now.ToString("HH:mm:ss", System.Globalization.DateTimeFormatInfo.InvariantInfo)
DateTime.Now.ToString("HH:mm:ss.fff", System.Globalization.DateTimeFormatInfo.InvariantInfo)