Reputation: 24766
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.
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
Upvotes: 0
Views: 1748
Reputation: 24766
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
Upvotes: 0
Reputation: 239684
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)
Upvotes: 1
Reputation: 294287
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.
Upvotes: 3
Reputation: 97
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)
Upvotes: 0
Reputation: 2943
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
Upvotes: 0