SQL Server 中VARCHAR(MAX)變量賦值引起的性能問題。


案例環境:

          操作系統版本 : Windows Server 2008 R2 Standard  SP1

          數據庫版本   :  Microsoft SQL Server 2012 (SP1) - 11.0.3000.0 (X64)

案例介紹:

 

由於不能將生產環境的代碼和數據貼上來,所以我構造了下面一個小案例,當然沒法和生產環境的案例一致。只能是接近而已。但是足以反映問題本質就足夠了。

DROP TABLE ProductPrice; 
 
GO 
 
CREATE TABLE ProductPrice 
 
( 
 
ProductName VARCHAR(14), 
 
Sequence INT , 
 
ProductPrice FLOAT 
 
) 
 
GO 
 

 

構造8000條測試數據,然后將數據插入臨時表#tmp(其實完全可以不用臨時表,只因為生產環境也是臨時表,故模擬接近案例環境)

DECLARE @index INT =1;
DECLARE @subindex INT;
 
 WHILE @index <= 800
 BEGIN
 SET @subindex = 1;
 WHILE @subindex <=10
  BEGIN 
   INSERT INTO ProductPrice
    SELECT 'product' + convert(varchar,@index), @subindex, rand()*1000;
 
   SET @subindex = @subindex +1;
  END;
 
  SET @index = @index +1;
END
 
 
 
SELECT * INTO #tmp FROM ProductPrice;
GO

 

本來開發人人員也許是要使用動態SQL語句獲取下面這樣一段SQL語句(隨意構造小例子,形似神不似)

 

DECLARE @sqlText NVARCHAR(MAX) =''; 
  
SELECT @sqlText=@sqlText+ quotename(productname)+
    '=CAST(MAX(CASE WHEN [productname]='+QUOTENAME(productname,'''')
      +' THEN [productPrice] END) AS VARCHAR)' 
FROM #tmp 
GROUP BY ProductName 

SELECT datalength(@sqlText);

 

 

clipboard

 

但是由於疏忽或是對動態SQL不了解,寫成了這樣一個SQL語句,結果執行時間一下子飈增到7分多鍾。

 

DECLARE @sqlText NVARCHAR(MAX) =''; 
  
SELECT @sqlText=@sqlText+ quotename(productname)+
    '=CAST(MAX(CASE WHEN [productname]='+QUOTENAME(productname,'''')
      +' THEN [productPrice] END) AS VARCHAR)' 
FROM #tmp ; 
SELECT datalength(@sqlText);

 

clipboard[1]

看來SQL對於處理非常長的字符串對象有一定的性能問題,於是為了驗證我的想法,我又構造了下面一個例子。創建臨時表#tmp,數據來源於 sys.all_columns

 

DROP TABLE #tmp;
GO
SELECT * INTO #tmp FROM sys.all_columns;
GO
 
 
7364 行受影響)

然后我們來看一下下面SQL語句

DECLARE @output NVARCHAR(MAX)
SELECT @output=ISNULL(@output,'') + QUOTENAME(name) + REPLICATE('it is only a test ', 200)
FROM #tmp

clipboard[2]

那么我們來看看這條SQL的執行計划,如下所示,很普通的執行計划,看不出有啥特別之處。但是執行性能那叫一個糟糕透頂!

SET SHOWPLAN_ALL ON;
 
GO
 
DECLARE @output NVARCHAR(MAX)
 
SELECT @output=ISNULL(@output,'') + QUOTENAME(name) + REPLICATE('it is only a test ', 200)
 
FROM #tmp 

clipboard[3]

StmtText的內容,如下所示:

DECLARE @output NVARCHAR(MAX)

SELECT @output=ISNULL(@output,'') + QUOTENAME(name) + REPLICATE('it is only a test ', 200)

FROM #tmp

  |--Compute Scalar(DEFINE:([Expr1004]=isnull([@output],CONVERT_IMPLICIT(nvarchar(max),'',0))+quotename([tempdb].[dbo].[#tmp].[name])+N'it is only a test it is only a test it is only a test it is only a test it is only a test it is only a test it is only a test it is only a test it is only a test it is only a test it is only a test it is only a test it is only a test it is onl'))

       |--Table Scan(OBJECT:([tempdb].[dbo].[#tmp]))

 

雖然能理解處理大對象需要很多資源,會產生一定的性能問題,但是執行時間這么長,還是讓我覺得有點不可思議,但是又不清楚具體原因!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM