0

次の列を持つテーブルがあります

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

しかし、私のアプリケーションは 1 秒あたり 1000 を超える更新を呼び出しています。そのため、この SP はテーブルを更新するのに十分な速さではありません。確認したところ、 getdate() 関数がボトルネックであることがわかりました。しかし、このシステムはSQL Server 2000で実行されています.

  1. getdate() 以外の方法で時刻を更新できますか
  2. このアップデートのパフォーマンスを向上させるための他の提案。

参考までに: この 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

getdate() を使用した SP ............................................. ...................................................

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

getdata() なしの SP .......................................................... ................................................................

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

5 に答える 5

3

確認したところ、 getdate() 関数がボトルネックであることがわかりました

あなたの発見が間違っていることを100%保証できます。パフォーマンスの問題は決してありませんgetdate()。記録のために、1000回の呼び出しgetdate()がパフォーマンスに表示されないだけでなく、 getdate()` はたまたまランタイム定数関数です!

SQL Server のパフォーマンスの問題をトラブルシューティングする場合は、待機とキューなどの方法に従うか、パフォーマンス トラブルシューティング フローチャートに従ってください。

私の賭けは、パフォーマンスの問題がここにあるということです:

where market = @market and symbol = @symbol

(market, symbol)パラメータの適切なインデックスまたはタイプが欠落しているか、正しくないか、またはデータ型の優先規則により@marketインデックスシークの代わりにテーブル スキャンが発生します。@symbol

于 2012-09-12T06:44:46.577 に答える
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

これら 2 つのカーソル ループの唯一の違いは、2 番目のカーソル ループは へのGETDATE()ベース更新をupdate_time行うのに対し、最初のカーソル ループはこの列の更新を実行しないことです (テストで行ったことを示したように)。

私のマシン (*) では、次の結果が得られます。

"FAST" took 20503
"SLOW" took 20436

これは、 を使用した「遅い」方法GETDATE()が 0.1 秒速かったことを示しています。

(*)

Microsoft SQL Server 2000 - 8.00.2039 (インテル X86)

2005 年 5 月 3 日 23:18:38

Copyright (c) 1988-2003 Microsoft Corporation

Windows NT 5.2 の Standard Edition (ビルド 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

これが機能するかどうかを確認してください

于 2012-09-12T06:26:37.297 に答える
0

これは私のために働く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 に答える
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)
于 2012-09-12T06:36:17.737 に答える