我使用ANTS探查器来识别我的C#应用程序中的剩余瓶颈:SQL Server存储过程。我正在使用SQL Server 2008.这里的任何人都可以帮助我提高性能,或者指出我可以做些什么来使其更好或更高效?
首先,这是程序:
PROCEDURE [dbo].[readerSimilarity]
-- Add the parameters for the stored procedure here
@id int,
@type int
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
IF (@type=1) --by Article
SELECT id1, id2, similarity_byArticle FROM similarity WHERE (id1 = @id OR id2 = @id)
AND similarity_byArticle != 0
ELSE IF (@type=2) --by Parent
SELECT id1, id2, similarity_byParent FROM similarity WHERE (id1 = @id OR id2 = @id)
AND similarity_byParent != 0
ELSE IF (@type=3) --by Child
SELECT id1, id2, similarity_byChild FROM similarity WHERE (id1 = @id OR id2 = @id)
AND similarity_byChild != 0
ELSE IF (@type=4) --combined
SELECT id1, id2, similarity_combined FROM similarity WHERE (id1 = @id OR id2 = @id)
AND similarity_combined != 0
END
表格“similarity
”由两个id
s(id1
和id2
)以及一些存储double
值的列组成。约束是id1 < id2
。
Column Data ----- ---- ID1 PK, Indexed ID2 PK, Indexed The table contains 28.5 million entries.
存储过程的工作是获取id
或id1
中具有参数id2
的所有行。此外,type-parameter指定的列不能为零。
对于不同的ids
,多次调用存储过程。虽然每次呼叫只接受~1.6 ms
,但总共召唤了17,000次。
处理器的运行率仅为25%,这似乎是因为应用程序正在等待程序调用返回。
你认为有什么方法可以加快速度吗?
C# Code Snippet
private HashSet<NodeClustering> AddNeighbourNodes(int id)
{
HashSet<NodeClustering> resultSet = new HashSet<NodeClustering>();
HashSet<nodeConnection> simSet = _graphDataLoader.LoadEdgesOfNode(id);
foreach (nodeConnection s in simSet)
{
int connectedId = s.id1;
if (connectedId == id)
connectedId = s.id2;
// if the corresponding node doesn't exist yet, add it to the graph
if (!_setNodes.ContainsKey(connectedId))
{
NodeClustering nodeToAdd = CreateNode(connectedId);
GraphAddOuter(nodeToAdd);
ChangeWeightIntoCluster(nodeToAdd.id, s.weight);
_bFlowOuter += s.weight;
resultSet.Add(nodeToAdd);
}
}
// the nodes in the result set have been added
to the outernodes -> add to the outernodes count
_setNodes[id].countEdges2Outside += resultSet.Count;
return resultSet;
}
每次向集群添加新id
时都会调用此方法。它获取id
的所有连接节点(当数据库中有id1=id
或id2=id
的条目时,它们已连接)
_graphDataLoader.LoadEdgesOfNode(id);
然后它检查所有连接的ids
以及它们是否尚未加载:
if (!_setNodes.ContainsKey(connectedId))
它加载它们:
CreateNode(connectedId);
方法:
_graphDataLoader.LoadEdgesOfNode(id);
再次调用,这次使用connectedId
。
我需要这个来获取新节点与已经在集合中的那些节点的所有连接。
我可能会收集我需要添加的所有节点的ids
,并仅使用一个id列表调用我的存储过程一次。
我可以通过像
这样的东西一次加载连接的ids连接 SELECT id1, id2, similarity_byArticle FROM similarity WHERE
(id1 = @id OR id2 = @id OR
id1 IN (SELECT id1 FROM similarity WHERE id2 = @id) OR
id2 IN (SELECT id1 FROM similarity WHERE id2 = @id) OR
id1 IN (SELECT id2 FROM similarity WHERE id1 = @id) OR
id2 IN (SELECT id2 FROM similarity WHERE id1 = @id))
AND similarity_byArticle != 0
然后我会得到比我需要的更多的条目,因为我也会为已经加载的节点获取它们(从我的测试中,它将占调用的大约75%)。
List<int>
作为SP参数吗?答案 0 :(得分:6)
如果它运行得那么快,那么问题可能在于重复调用该程序的次数。有没有办法可以修改存储过程和代码,以便在一次调用中返回应用程序所需的所有结果?
优化在不到2毫秒内运行的查询可能不是一项富有成效的工作。我怀疑你可以通过查询调整来削减超过几分之一毫秒。
答案 1 :(得分:3)
我尝试将应用程序更改为仅对每个ID调用一次,但如果不可能,请尝试这样做(确保在interact.id1上有一个索引,在similar.id2上有另一个索引):
PROCEDURE [dbo].[readerSimilarity]
-- Add the parameters for the stored procedure here
@id int,
@type int
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
IF @type=1 --by Article
BEGIN
SELECT
id1, id2,similarity_byArticle
FROM similarity
WHERE id1 = @id AND similarity_byArticle!=0
UNION
SELECT
id1, id2,similarity_byArticle
FROM similarity
WHERE id2 = @id AND similarity_byArticle!=0
END
ELSE IF @type=2 --by Parent
BEGIN
SELECT
id1, id2,similarity_byParent
FROM similarity
WHERE id1 = @id AND similarity_byParent!=0
UNION
SELECT
id1, id2,similarity_byParent
FROM similarity
WHERE id2 = @id AND similarity_byParent!=0
END
ELSE IF @type=3 --by Child
BEGIN
SELECT
id1, id2,similarity_byChild
FROM similarity
WHERE id1 = @id AND similarity_byChild!=0
UNION
SELECT
id1, id2,similarity_byChild
FROM similarity
WHERE id2 = @id AND similarity_byChild!=0
END
ELSE IF @type=4 --combined
BEGIN
SELECT
id1, id2,similarity_combined
FROM similarity
WHERE id1 = @id AND similarity_combined!=0
UNION
SELECT
id1, id2,similarity_combined
FROM similarity
WHERE id2 = @id AND similarity_combined!=0
END
END
GO
编辑基于OP的最新评论:
整个图表存储在 MSSQL-Database和我加载它 先后将程序纳入 一些字典结构
您需要重新设计加载过程。您应该只调用一次数据库来加载所有这些数据。由于ID已存在于Database表中,因此您可以在此查询中使用连接从另一个表中获取正确的ID。使用包含要绘制图表的ID的表架构编辑您的问题,以及它们与已发布代码的关系。一旦您获得单个查询以返回所有数据,每次17,000次调用单个行的速度会快得多。
答案 2 :(得分:1)
使用分隔列表(使用逗号或斜杠或其他任何东西,使用管道符[[]],立即将所有ID传递到存储过程中。 将下面列出的用户定义函数(UDF)添加到数据库中。它会将分隔列表转换为可以加入相似表的表。然后在你的实际存储过程中,你可以写...
Create Procedure GetSimilarityIDs
@IdValues Text -- @IdValues is pipe-delimited [|] list of Id Values
As
Set NoCount On
Declare @IDs Table
(rowNum Integer Primary Key Identity Not Null,
Id Integer Not Null)
Insert Into @IDs(Id)
Select Cast(sVal As Integer)
From dbo.ParseString(@IdValues, '|') -- specify delimiter
-- ---------------------------------------------------------
Select id1, id2, similarity_byArticle
From similarity s Join @IDs i On i.Id = s.Id
Where similarity_byArticle <> 0
Return 0
- ********************************************* **************
下面的代码是创建泛型函数UDF,它可以将任何文本字符串解析为字符串值表...:
Create FUNCTION [dbo].[ParseTextString] (@S Text, @delim VarChar(5))
Returns @tOut Table
(ValNum Integer Identity Primary Key,
sVal VarChar(8000))
As
Begin
Declare @dLLen TinyInt -- Length of delimiter
Declare @sWin VarChar(8000) -- Will Contain Window into text string
Declare @wLen Integer -- Length of Window
Declare @wLast TinyInt -- Boolean to indicate processing Last Window
Declare @wPos Integer -- Start Position of Window within Text String
Declare @sVal VarChar(8000) -- String Data to insert into output Table
Declare @BtchSiz Integer -- Maximum Size of Window
Set @BtchSiz = 7900 -- (Reset to smaller values to test routine)
Declare @dPos Integer -- Position within Window of next Delimiter
Declare @Strt Integer -- Start Position of each data value within Window
-- -------------------------------------------------------------------------
If @delim is Null Set @delim = '|'
If DataLength(@S) = 0 Or
Substring(@S, 1, @BtchSiz) = @delim Return
-- ---------------------------
Select @dLLen = Len(@delim),
@Strt = 1, @wPos = 1,
@sWin = Substring(@S, 1, @BtchSiz)
Select @wLen = Len(@sWin),
@wLast = Case When Len(@sWin) = @BtchSiz
Then 0 Else 1 End,
@dPos = CharIndex(@delim, @sWin, @Strt)
-- ------------------------------------
While @Strt <= @wLen
Begin
If @dPos = 0 -- No More delimiters in window
Begin
If @wLast = 1 Set @dPos = @wLen + 1
Else
Begin
Set @wPos = @wPos + @Strt - 1
Set @sWin = Substring(@S, @wPos, @BtchSiz)
-- ----------------------------------------
Select @wLen = Len(@sWin), @Strt = 1,
@wLast = Case When Len(@sWin) = @BtchSiz
Then 0 Else 1 End,
@dPos = CharIndex(@delim, @sWin, 1)
If @dPos = 0 Set @dPos = @wLen + 1
End
End
-- -------------------------------
Set @sVal = LTrim(Substring(@sWin, @Strt, @dPos - @Strt))
Insert @tOut (sVal) Values (@sVal)
-- -------------------------------
-- Move @Strt to char after last delimiter
Set @Strt = @dPos + @dLLen
Set @dPos = CharIndex(@delim, @sWin, @Strt)
End
Return
End
答案 3 :(得分:0)
首先创建一个视图
CREATE VIEW ViewArticles
AS
SELECT id1, id2, similarity_byArticle
FROM similarity
WHERE (id1 = @id or id2 = @id)
and similarity_byArticle != 0
在您的代码中将所有需要的ID填充到表中。
创建一个以所有id表作为参数的函数。
CREATE FUNCTION
SelectArticles
(
@Ids TABLE
)
RETURNS TABLE
AS
RETURN
(
SELECT id1, id2, similarity_byArticle FROM ViewArticles
INNER JOIN @Ids I ON I.Id = id1
UNION
SELECT id1, id2, similarity_byArticle FROM ViewArticles
INNER JOIN @Ids I ON I.Id = id2
)