0

I have a table with fallowing columns

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

And my application calling a SP for update this table.

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

But my application calling more than 1000 updates per second. so this SP not fast enough to update the table. I checked and found getdate() function is the bottleneck. but this system running with sql server 2000.

  1. Can I use any other method to update time other than getdate() Any
  2. other proposal for increase performance of this update.

FYI: this price_tbl having around 2000 records.


Eddied with test result.

Table Definition ..........................................................................................

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 without 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

Test Script ...........................................................................................

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

Result:

Test 01 : 82310
Test 02 : 12176

Result with reverse test.

Test 02 : 15413
Test 01 : 81636
4

5 回答 5

3

I checked and found getdate() function is the bottleneck

I can absolutely 100% guarantee that your finding is wrong. The performance problem is by no means getdate(). For the record, not only calling getdate() 1000 times is not visible in the performance, but getdate()` happens to be a runtime constant function!

If you want to troubleshoot SQL Server performance issues, follow a methodology like Waits and Queues or follow the Performance Troubleshooting Flowchart.

My bet is that the performance problem is here:

where market = @market and symbol = @symbol

You're either missing an appropriate index on (market, symbol) or the type of the parameters @market and/or @symbol are incorrect and, due to data type precedence rules, cause table scans instead of index seeks.

于 2012-09-12T06:44:46.577 回答
1

Since you haven't presented a benchmark, I thought I'd do one, and then everyone can poke holes in the methodology. Hence 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

The above sets us up with a table with ~30000 rows (so, more than you say you have). It also warms up the table, because every row has been recently accessed. I can't remember what invocations will clear this out for SQL Server 2000, but it ought to be fair enough to now run the tests in either order.

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

The only difference between these two cursor loops is that the second one does the GETDATE() based update to update_time, whereas the first performs no update to this column (as you indicated you'd done in your testing).

On my machine(*) , I get the result:

"FAST" took 20503
"SLOW" took 20436

Which indicates that the "slow" method, using GETDATE(), was 0.1 seconds faster.

(*)

Microsoft SQL Server 2000 - 8.00.2039 (Intel X86)

May 3 2005 23:18:38

Copyright (c) 1988-2003 Microsoft Corporation

Standard Edition on Windows NT 5.2 (Build 3790: Service Pack 2)

于 2012-09-12T13:06:31.023 回答
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

See if this works

于 2012-09-12T06:26:37.297 回答
0

I had a scenario like this and i gave a solution like this, I passed the date time as the parameter from the front end to SP. otherwise if you want to handle in the SP itself you can go for this, like

DECLARE @time DATETIME
Set @time = getdate()

and you can pass it to your argument list, but note that we wont get the exact milliseconds in some case we handle that too in front end. because all record will have same present time with only different second value.

In front end yo can pass the value as,

DateTime.Now.ToString("HH:mm:ss", System.Globalization.DateTimeFormatInfo.InvariantInfo)
DateTime.Now.ToString("HH:mm:ss.fff", System.Globalization.DateTimeFormatInfo.InvariantInfo)
于 2012-09-12T06:36:17.737 回答
0

This works for me Test 01 : 266

ALTER PROCEDURE [dbo].[update_test_one]            
     @Symbol varchar(10),      
     @Market varchar(5),       
     @BuyPrice money,      
     @SellPrice money,      
     @LastPrice money,      
     @High  money,      
     @Low  money    

    AS            
    SET NOCOUNT ON;
    UPDATE P    
    SET P.Buy_price = @BuyPrice,            
    P.Sell_price = @SellPrice,            
    P.GLDBid = @BuyPrice + C.BidRate,            
    P.GLDAsk = @SellPrice + isnull(C.AskRate,0),            
    P.Day_high = @High,            
    P.Day_Low = @Low,            
    P.GlobalGLDBid = @BuyPrice+ isnull(C.BidRateGlobal,0),            
    P.GlobalGLDAsk = @SellPrice+isnull(C.AskRateGlobal,0),          
    P.UpdateTime=CONVERT(VARCHAR(20), GETDATE(), 120),
    P.Last_Done = @LastPrice     
    FROM dbo.GLDPrice AS P    
    INNER JOIN DBO.GLDContract AS C
    ON P.Company_code=C.Company_code  
    WHERE  P.Symbol = @symbol AND P.Market = @Market 

    SET NOCOUNT OFF 
于 2012-09-19T01:27:32.337 回答