我有一张表格,其中有一些列
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运行。
仅供参考:这个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
答案 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)