簡體   English   中英

在我的情況下如何提高SQL查詢性能

[英]how to improve SQL query performance in my case

我有一個表,架構非常簡單,一個ID列作為唯一的主鍵(uniqueidentifier類型)和其他一些nvarchar列。 我當前的目標是,對於5000個輸入,我需要計算表中已經包含哪些輸入,哪些不包含。 輸入是字符串,我有一個C#函數,它將字符串轉換為uniqueidentifier(GUID)。 我的邏輯是,如果存在現有ID,則將字符串視為已包含在表中。

我的問題是,如果我需要找出數據庫中已經包含的5000個輸入字符串中的哪些,哪些不是,最有效的方法是什么?

順便說一句:我當前的實現是使用C#代碼將字符串轉換為GUID,然后調用/實現一個存儲過程,該過程查詢數據庫中是否存在ID並返回到C#代碼。

我的工作環境:VSTS 2008 + SQL Server 2008 + C#3.5。

我的第一個直覺是將5000個輸入泵入一個單列臨時表X,可能對其進行索引,然后使用:

SELECT X.thecol
FROM X
JOIN ExistingTable USING (thecol)

以獲取存在的內容,並且(如果需要這兩個集合)

SELECT X.thecol
FROM X
LEFT JOIN ExistingTable USING (thecol)
WHERE ExistingTable.thecol IS NULL

得到那些缺席的。 至少值得進行基准測試。

編輯:根據要求,以下是有關SQL Server中的臨時表的一些不錯的文檔和教程。 Bill Graziano有一個簡單的介紹,介紹了臨時表,表變量和全局臨時表。 Randy DyessSQL Master討論了支持和反對他們的性能問題(但是請記住,如果遇到性能問題,您確實希望對替代方法進行基准測試, 不僅僅是考慮理論上的問題!)。

MSDN上有關於tempdb (保存臨時表)和優化其性能的文章。

步驟1.確保您要解決的問題。 在許多情況下,一次插入五千個插入並不多。

您確定最簡單的方法還不夠嗎? 到目前為止,您衡量了哪些性能問題?

您需要對表中存在或不存在的那些條目做什么?

根據您的需求,也許SQL Server 2008中的新MERGE語句可以滿足您的要求-更新現有內容,插入新內容,將所有內容整齊地包裝到單個SQL語句中。 看看這個!

您的聲明如下所示:

MERGE INTO 
    (your target table) AS t
USING 
    (your source table, e.g. a temporary table) AS s
ON t.ID = s.ID
WHEN NOT MATCHED THEN  -- new rows does not exist in base table
  ....(do whatever you need to do)
WHEN MATCHED THEN      -- row exists in base table
  ... (do whatever else you need to do)
;

為了使之真正快速,我將使用BULK INSERT將“新”記錄從TXT或CSV文件加載到SQL Server中的臨時表中:

BULK INSERT YourTemporaryTable
FROM 'c:\temp\yourimportfile.csv'
WITH 
(
    FIELDTERMINATOR =',',
    ROWTERMINATOR =' |\n'
)

大容量插入與MERGE相結合應該為您提供在這個星球上可以獲得的最佳性能:-)

PS:這是TechNet上關於MERGE性能的說明,以及為什么它比個別陳述要快的原因:

在SQL Server 2008中,可以使用MERGE語句在單個語句中執行多種數據操作語言(DML)操作。 例如,您可能需要通過根據另一個表中的差異在一個表中插入,更新或刪除行來同步兩個表。 通常,這是通過執行包含單個INSERT,UPDATE和DELETE語句的存儲過程或批處理來完成的。 但是,這意味着源表和目標表中的數據都會被評估和處理多次; 每個陳述至少要一次。 通過使用MERGE語句,可以將單個DML語句替換為單個語句。 因為操作是在單個語句中執行的,所以可以提高查詢性能,從而最大程度地減少了源表和目標表中數據的處理次數。 但是,性能的提高取決於正確的索引,連接和其他考慮因素。 本主題提供最佳實踐建議,以幫助您使用MERGE語句時獲得最佳性能。

嘗試確保最終只運行一個查詢-即,如果您的解決方案包含對數據庫運行5000個查詢,那么這可能是該操作最大的資源消耗者。

如果您可以將5000個ID插入臨時表中,則可以編寫一個查詢以查找數據庫中不存在的ID。

如果要簡化,因為5000條記錄不是很多,那么從C#中只需使用循環為要添加到表中的每個字符串生成一個插入語句。 將插入物包裝在TRY CATCH塊中。 將它們全部發送到服務器,如下所示:

BEGIN TRY
INSERT INTO table (theCol, field2, field3)
SELECT theGuid, value2, value3
END TRY BEGIN CATCH END CATCH

BEGIN TRY
INSERT INTO table (theCol, field2, field3)
SELECT theGuid, value2, value3
END TRY BEGIN CATCH END CATCH

BEGIN TRY
INSERT INTO table (theCol, field2, field3)
SELECT theGuid, value2, value3
END TRY BEGIN CATCH END CATCH

如果在字符串GUID上定義了唯一索引或主鍵,則重復插入將失敗。 提前檢查以查看記錄是否不存在,只是重復了SQL將要做的工作。

如果性能確實很重要,請考慮將5000 GUIDS下載到本地工作站並進行所有本地分析。 讀取5000 GUIDS應該花費不到1秒的時間。 這比批量導入到臨時表(這是從臨時表中獲得性能的唯一方法)並使用對臨時表的聯接進行更新要簡單得多。

絕對不要一一對應。

我的首選解決方案是使用一個可以采用以下參數的參數創建存儲過程,並且XML采用以下格式:

<ROOT>
  <MyObject ID="60EAD98F-8A6C-4C22-AF75-000000000000">
  <MyObject ID="60EAD98F-8A6C-4C22-AF75-000000000001">
  ....
</ROOT>

然后,在使用類型為NCHAR(MAX)的參數的過程中,將其轉換為XML,然后將其用作具有單列的表(將其稱為@FilterTable)。 存儲過程如下:

CREATE PROCEDURE dbo.sp_MultipleParams(@FilterXML NVARCHAR(MAX))
AS BEGIN
    SET NOCOUNT ON

    DECLARE @x XML
    SELECT @x = CONVERT(XML, @FilterXML)

    -- temporary table (must have it, because cannot join on XML statement)
    DECLARE @FilterTable TABLE (
         "ID" UNIQUEIDENTIFIER
    )

    -- insert into temporary table
    -- @important: XML iS CaSe-SenSiTiv
    INSERT      @FilterTable
    SELECT      x.value('@ID', 'UNIQUEIDENTIFIER')
    FROM        @x.nodes('/ROOT/MyObject') AS R(x)

    SELECT      o.ID,
                SIGN(SUM(CASE WHEN t.ID IS NULL THEN 0 ELSE 1 END)) AS FoundInDB
    FROM        @FilterTable o
    LEFT JOIN   dbo.MyTable t
            ON  o.ID = t.ID
    GROUP BY    o.ID

END
GO

您將其運行為:

EXEC sp_MultipleParams '<ROOT><MyObject ID="60EAD98F-8A6C-4C22-AF75-000000000000"/><MyObject ID="60EAD98F-8A6C-4C22-AF75-000000000002"/></ROOT>'

您的結果如下所示:

ID                                   FoundInDB
------------------------------------ -----------
60EAD98F-8A6C-4C22-AF75-000000000000 1
60EAD98F-8A6C-4C22-AF75-000000000002 0

由於使用的是Sql Server 2008,因此可以使用表值參數。 這是一種提供表作為存儲過程參數的方法。

使用ADO.NET,您可以輕松地預先填充DataTable並將其作為SqlParameter傳遞。 您需要執行的步驟:

創建一個自定義Sql類型

CREATE TYPE MyType AS TABLE
(
UniqueId INT NOT NULL,
Column NVARCHAR(255) NOT NULL
) 

創建一個接受類型的存儲過程

CREATE PROCEDURE spInsertMyType
@Data MyType READONLY
AS 
xxxx

使用C#調用

SqlCommand insertCommand = new SqlCommand(
   "spInsertMyType", connection);
 insertCommand.CommandType = CommandType.StoredProcedure;
 SqlParameter tvpParam = 
    insertCommand.Parameters.AddWithValue(
    "@Data", dataReader);
 tvpParam.SqlDbType = SqlDbType.Structured;

鏈接: SQL 2008中的表值參數

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM