优化存储过程

Posted

技术标签:

【中文标题】优化存储过程【英文标题】:Optimize a stored procedure 【发布时间】:2019-06-19 19:04:00 【问题描述】:

我有一个存储过程,它被多次调用以获取显示页面所需的所有元素。

样本数据:

CREATE TABLE #temp 
(plan_id INT, parentid INT, label VARCHAR(20),  color VARCHAR(10),  comp_id INT,    start_date DATETIME) 

INSERT INTO #temp 
VALUES
(607, NULL,'abc',   'CDC',  432,    '2018-05-22 00:00:00'),
(607,NULL,'abc',    'CDC',  432,    '2018-05-22 00:00:00'),
(607,NULL,'abc',    'CDC',  433,    '2018-05-22 00:00:00'),  
(NULL,432,'def',    'CDC',  434,    '2018-05-22 00:00:00'),
(NULL,432,'def',    'CDC',  434,    '2018-05-22 00:00:00'),
(NULL,433,'def',    'CDC',  435,    NULL),
(NULL,433,'def',    'CDC',  435,    NULL), 
(NULL,434,'obj',    'CDC',  436,    '2018-05-22 00:00:00'),
(NULL,434,'obj',    'CDC',  436,    '2018-05-22 00:00:00'),
(NULL,435,'obj',    'CDC',  437,    NULL), 
(NULL,436,'ion',    'CDC',  438,    '2018-05-22 00:00:00'),
(NULL,436,'ion',    'CDC',  438,    '2018-05-22 00:00:00'), 
(NULL,437,'ion',    'CDC',  439,    NULL)

存储过程的第一次调用提供一个plan_id(@plan_id)并获取父节点。然后,这些节点在页面代码中循环,随后调用相同的存储过程,但循环中元素的 comp_id,然后通过另一个调用循环以查找其子级。

这对于包含许多元素的大型计划非常低效。 (例如,如果您使用@plan_id = 607 执行此操作,则将使用 comp_id 提取父节点;它们将在下次调用中提供,@plan_id 为空)。

DECLARE
@plan_id INT =null,
@parentid INT =null

IF @plan_id is not null 
BEGIN   
SELECT label, color, comp_id, start_date FROM #temp WHERE plan_id = @plan_id 
END 
ELSE 
BEGIN 
SELECT label, color, comp_id, start_date FROM #temp WHERE parentid = @parentid  
END 

即将使用@plan_id = 607 和@parentid = null 调用存储过程; @plan_id = null 和 @432; @plan_id = null 和 @parentid = 433; @plan_id = null 和 @parentid = 434; @plan_id = null 和 @parentid = 435; @plan_id = null 和 @parentid = 436;和@plan_id = null 和@parentid = 437

例如:@plan_id = 607时,输出为:

我想要做的是,在一个有效的存储过程中构建它,该存储过程将返回显示页面所需的所有内容。

根据我们的示例,我希望得到如下结果:

SELECT label, color, comp_id, start_date FROM #temp 

第一次执行时plan_id = 607,存储过程给出:

label   color   comp_id start_date
abc     CDC     432     2018-05-22 00:00:00.000
abc     CDC     432     2018-05-22 00:00:00.000
abc     CDC     433     2018-05-22 00:00:00.000

那么需要432和433才能得到parentIds;然后它将调用存储过程以使用父 ID 执行,保持计划 ID 为空。例如,如果 parentid = 432,它将返回以下输出:

label   color   comp_id start_date
def     CDC     434     2018-05-22 00:00:00.000
def     CDC     434     2018-05-22 00:00:00.000

该表只是显示其工作原理和预期输出的示例。

否则plan_id和parentid是存储过程的参数,结果是连接多张表并根据需要的条件过滤后的结果。

谁能帮我优化一下,从而使单次调用高效的存储过程?

【问题讨论】:

为什么不直接WHERE plan_id = @plan_id OR parentid = @parentid 我已经把它作为一个例子。一旦提供了 plan_id,它就会从 comp_id 中获取 parentid。它保留它们,生成 parentid,然后使用它来执行存储过程。 @lije,我没有验证脚本..在我看来..您是否尝试过接受参数并在连接查询或选择查询中工作的表值函数.. @goofyui,好的,我会检查一下。但是如果有机会,请尝试使用脚本查看。谢谢! 您希望根据您提供的样本数据生成什么输出?请记住 - 我们在这方面的工作不如您,所以对您来说显而易见的事情对我们来说并不明显。 【参考方案1】:

它看起来像一个简单的递归公用表表达式 (CTE):

declare @Samples as Table ( PlanId Int, ParentId Int, Label VarChar(20), Color VarChar(10), CompId Int, StartDate DateTime );
insert into @Samples ( PlanId, ParentId, Label, Color, CompId, StartDate ) values
(607, NULL,'abc',   'CDC',  432,    '2019-05-22 00:00:00'),
(607,NULL,'abc',    'CDC',  432,    '2018-05-22 00:00:00'),
(607,NULL,'abc',    'CDC',  433,    '2018-05-22 00:00:00'),  
(NULL,432,'def',    'CDC',  434,    '2018-05-22 00:00:00'),
(NULL,432,'def',    'CDC',  434,    '2018-05-22 00:00:00'),
(NULL,433,'def',    'CDC',  435,    NULL),
(NULL,433,'def',    'CDC',  435,    NULL), 
(NULL,434,'obj',    'CDC',  436,    '2018-05-22 00:00:00'),
(NULL,434,'obj',    'CDC',  436,    '2018-05-22 00:00:00'),
(NULL,435,'obj',    'CDC',  437,    NULL), 
(NULL,436,'ion',    'CDC',  438,    '2018-05-22 00:00:00'),
(NULL,436,'ion',    'CDC',  438,    '2018-05-22 00:00:00'), 
(NULL,437,'ion',    'CDC',  439,    NULL);

select * from @Samples;

with Things as (
  -- Get all of the plans ...
  select PlanId, ParentId, Label, Color, CompId, StartDate
    from @Samples
    where ParentId is NULL
  union all
  -- ... add the children one level at a time.
  select S.PlanId, S.ParentId, S.Label, S.Color, S.CompId, S.StartDate
    from Things as T inner join @Samples as S on T.CompId = S.ParentId
  )
  select PlanId, ParentId, Label, Color, CompId, StartDate
    from Things;

【讨论】:

但问题是,输出没有 planId 和 parentid 列。我把它们放在一起是为了提供一个很好的例子。不过还是谢谢。 @lije 您可以从最后的select 中省略PlanIdParentId 列。 我已编辑问题以提供几个示例表/输出。你能看看吗?我没有按原样的@sample 表;我的意思是给出整个数据集的想法。我根据传递的参数检索了它。因此,我什至无法在 CTE 中按 parentid 进行选择和过滤。

以上是关于优化存储过程的主要内容,如果未能解决你的问题,请参考以下文章

MYSQL数据太大时怎么优化 视图?存储过程? - 技术问答

利用存储过程优化复杂的数据库操作

存储过程部分优化

优化SQL存储过程

mysql优化专题之存储过程和存储函数

优化存储过程