2012-01-03 83 views
-2

我想用c#填充SQL數據庫中的數據庫表。我應該在數據庫中找到不同的數據包,然後爲每個數據包首先填寫一個包含該數據包所有行的數據表,然後查找該行的深度。我使用下面的函數,以加快通過使用並行任務的過程:使用C#進行並行編程

private void DoParrallelComputing(DataTable dtpacket, string Identifier) 
{ 
    con = new SqlConnection(strConnect); 

    Dictionary<int, DataTable> dtsName = new Dictionary<int, DataTable>(); 
    Dictionary<int, SqlDataAdapter> ReaderName = new Dictionary<int, SqlDataAdapter>(); 
    Dictionary<int, string> strName = new Dictionary<int, string>(); 
    Dictionary<int, SqlCommand> cmdName = new Dictionary<int, SqlCommand>(); 

    Dictionary<int, int> Node = new Dictionary<int, int>(); 
    Dictionary<int, string> NodeID = new Dictionary<int, string>(); 
    Dictionary<int, string> ParentID = new Dictionary<int, string>(); 
    Dictionary<int, int> Depth = new Dictionary<int, int>(); 

    Parallel.For(0, dtpacket.Rows.Count - 1, i => 
    { 
    progressBarTree.Value = i; 

    Application.DoEvents(); 
    string packetID = dtpacket.Rows[i][0].ToString(); 

    strName[i] = ""; 
    strName[i] = "SELECT Node,NodeID,ParentId,Depth from tblTree where [Identifier]='" + Identifier + "' and [PacketId]='" + packetID + "'"; 
    strName[i] = strName[i] + " order by [Node]"; 

    if (con.State != ConnectionState.Open) 
    { 
     con.Open(); 
    } 

    ReaderName[i] = new SqlDataAdapter(strName[i], con); 
    dtsName[i] = new DataTable(); 

    ReaderName[i].Fill(dtsName[i]); 

    int rowsCount = 0; 
    rowsCount = dtsName[i].Rows.Count - 1; 

    for (int j = rowsCount; j >= 0; j--) 
    { 
     Application.DoEvents(); 
     Node[i] = Form1.val_int(dtsName[i].Rows[j][0].ToString()); 
     NodeID[i] = dtsName[i].Rows[j][1].ToString(); 
     ParentID[i] = dtsName[i].Rows[j][2].ToString(); 
     Depth[i] = 0; 

     for (int ii = j - 1; ii >= 0; ii--) 
     { 
     string dtParent = ""; 
     string nodeParent = ""; 

     dtParent = dtsName[i].Rows[ii][1].ToString(); 
     nodeParent = ParentID[i]; 

     if (dtParent == nodeParent) 
     { 
      ParentID[i] = dtsName[i].Rows[ii][2].ToString(); 
      Depth[i]++; 
     } 
     } 

     //Update Tree table 
     strName[i] = "update tblTree set depth=" + Depth[i] + " where node=" + Node[i].ToString(); 

     if (con.State != ConnectionState.Open) 
     { 
     con.Open(); 
     } 

     cmdName[i] = new SqlCommand(strName[i], con); 
     cmdName[i].ExecuteNonQuery(); 
     cmdName[i].Dispose(); 
    } 
    }); 
} 

但是當我運行這個功能,我可以看到,CPU使用率70%與4個核CPU,也看到應用程序使用大約300個線程,但在10分鐘後,我看不到數據庫中的任何變化!如果我不使用「並行任務」,則需要大約20天才能完成,這就是爲什麼我要使用「並行任務」的原因。

+1

第一觀察:你應該做盡可能多的這個數據庫服務器上儘可能。如果您使用SQL Server,則可以使用CTE來計算樹中節點的深度。見http://www.mssqltips.com/sqlservertip/1520/recursive-queries-using-common-table-expressions-cte-in-sql-server/另外 - 它看起來像所有這些都可以在SQL服務器上完成,這是爲這種類型的事情進行了優化。避免在客戶端上加入數據。 – 2012-01-03 17:42:26

+4

通過單個SqlConnection並行傳遞300個命令不能很好地結束.. – plenderj 2012-01-03 17:58:49

+0

謝謝,我的結構是這樣的:有一些數據包,併爲每個數據包我有層次結構,我的意思是每行都出現在它的父數據庫後,所以我怎麼能在數據庫中做到這一點? – Mohammad 2012-01-03 18:05:17

回答

4
  1. 一個不喜歡通過在[Identifier]='" + Identifier + "'它串聯價值觀構建SQL命令。這是一個SQL注入攻擊矢量,你不應該這樣寫代碼。使用參數:[Identifier] = @identifier並將該參數值添加到該命令。在繼續之前,請幫助自己並閱讀How Data Access Code Affects Database Performance

  2. 添加線程ad nauseam不會使代碼運行得更快,它會使它運行得更慢。如果您想要獲得更好的吞吐量,則必須使用異步數據庫命令。確保你用AsyncronouwProcessing = true修飾連接字符串,然後使用BeginExecuteReader。調整未完成請求的數量。

  3. 使用DataAdapater和DataTable是降低性能的可靠方法。使用未加工的SqlDataReader。我再次向你推薦Bob Beauchamin的文章。

  4. 不要在客戶端進行處理,SQL Server是,其他更好。

  5. 不要做痛苦的慢行處理,使用集合導向的邏輯。遞歸CTE可用於執行分層處理,請參閱Using Common Table Expressions

還有更多的問題與您的代碼(如事件循環,清楚地表明,此代碼不上的UI線程屬於存在,頻繁ToString()電話清楚地表明缺乏的理解明確呼籲Dispose,而不是依靠using(...)塊,盲連接等重新開放等等等

您的第一個和最重要的焦點應該是通過表達它作爲單個基於CTE的更新沒有數據模式和需求的完整定義,人們只能猜測你想要做什麼,但它會是興這樣的:

with cte as (
    select Node, NodeID, ParentId, Depth, 0 as ComputedDepth 
    from tblTree 
    where [Identifier]= @Identifier 
    union all 
    select c.Node, c.NodeID, c.ParentId, c.Depth, p.ComputedDepth+1 
    from tblTree c 
    join cte p on c.ParentId = p.NodeId) 
update cte 
    set Depth = ComputedDepth; 

下面是一個實際的例子:

create table tblTree (
[NodeID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY, 
[Identifier] [varchar](10) NULL, 
[ParentID] [int] NULL, 
[Depth] [tinyint] NULL); 
GO 

insert into tblTree (Identifier, ParentID) 
    values ('Foo', NULL) 
     , (NULL, 1) 
     , (NULL, 1) 
     , (NULL, 2) 
     , (NULL, 4) 
     , ('Bar', NULL); 
go 

declare @identifier VARCHAR(10) = 'Foo'; 
with cte as (
    select NodeID, 0 as Depth 
    from tblTree 
    where Identifier = @identifier 
    union all 
    select c.NodeID, p.Depth+1 
    from tblTree c 
    join cte p on c.ParentID = p.NodeID) 
update t 
    set t.Depth = c.Depth 
from tblTree t 
join cte c on t.NodeID = c.NodeID; 
go 

select * from tblTree; 
+0

謝謝,我的tblTree表結構如下:[Node] [int] IDENTITY(1,1)NOT NULL, \t [Identifier] [varchar](10)NOT NULL, \t [PacketID] [varchar](10 )NOT NULL, \t [NodeID] [int] NOT NULL, \t [ParentID] [int] NULL, \t [深度] [tinyint] NULL ===>如何在SQL中計算此表的深度? – Mohammad 2012-01-03 18:41:22

+0

另一種評論是:深度計算應分別對每個[PacketID]進行。因此,我添加了另一個約束,如:將c.ParentId = p.NodeId和c.PacketID = p.PacketID連接到表達式。 – Mohammad 2012-01-03 18:46:36

+0

我在SQL上執行你的建議並得到這個錯誤:派生表'cte'不可更新,因爲派生表的一列是派生的或常量。 – Mohammad 2012-01-03 18:49:16