如何使用一段时间优化 SQL 查询(在同一个表中获取和添加数据)?

Posted

技术标签:

【中文标题】如何使用一段时间优化 SQL 查询(在同一个表中获取和添加数据)?【英文标题】:How optimize a TSQL query using a while (fetching and adding data in the same table)? 【发布时间】:2013-06-28 13:34:03 【问题描述】:

我正在编写一个程序来获取特定机器的所有部件和子部件。但是,有可能一个子部分也有子部分等等。在顶层,零件有一个修订号,之后,我取子零件的最大修订号,因为我想要最新的(每个修订都有相同的子零件,它只是改变的图纸而我没有'不在乎)。到目前为止,我得到了这个:

CREATE TABLE [dbo].[PartMtl_3](
[PartNum] [nvarchar](50) ,
[RevisionNum] [nvarchar](16) NULL,
[MtlPartNum] [nvarchar](70)
) ON [PRIMARY]
--Here I can't put a primary key because 
--it should be PartNum and RevisionNum and MtlPartNum together
-- but i know some of the data have a null in the revision
-- and tsql don't support a pk null

Input parameter for the procedure @Machine VARCHAR(30)

DECLARE @Mytable TABLE
(
id INT IDENTITY(1,1) NOT NULL,
PartNum VARCHAR(70),
RevisionNum VARCHAR(16),
Processed TINYINT,
ParentId INT
)

DECLARE @ID INT;
DECLARE @PartNum VARCHAR(70)
DECLARE @RevisionNum VARCHAR(16)

INSERT INTO @Mytable(PartNum,RevisionNum,Processed,ParentId)
SELECT PartNum,RevisionNum,0,NULL WHERE Machine=@Machine

--Whith this insert I have my top parts for the machine

WHILE (SELECT COUNT(*) FROM @Mytable WHERE Processed=0)>0
BEGIN
     SELECT TOP 1 @ID=id,@PartNum=PartNum,@RevisionNum=RevisionNum Where Processed=0
     INSERT INTO @MyTable(PartNum,RevisionNum,Processed,ParentId)
     SELECT MtlPartNum,(SELECT MAX(RevisionNum) FROM PartMtl_3  
      WHERE PartNum=MtlPartNum) AS RevisionNum,0,@Id FROM PartMtl_3 
        WHERE PartNum=@PartNum AND RevisionNum=@RevisionNum
     UPDATE @Matable SET Processed=1 Where id=@Id
END
--other code here to basically insert the result of @Mytable in a real table

我知道这个程序可以工作,但它真的很慢,而且会吃掉服务器的所有资源。那么是否可以通过使用游标(我认为它非常接近所以我怀疑这种方法)或设置基本操作来优化它?

【问题讨论】:

【参考方案1】:

基本上,我要做的是创建一个表变量来保存您在每次传递中检索到的所有未处理部分。

这将允许您减少循环次数并同时更新多个条目。

另外,我会创建一个 CTE 来提前计算 MaxRevisionNum,这样就无需为每条记录重新运行查询(这基本上是在将查询放入字段时发生的情况。

然后我会使用 join 插入需要的内容,并在循环结束时更新 @MyTable 中的处理部分。

这就是代码的样子。

它可能需要一些调整,因为它无法针对您的数据集进行测试,但它应该非常接近您的需要。

declare @Unprocessed TABLE
(
id INT IDENTITY(1,1) NOT NULL,
PartNum VARCHAR(70),
RevisionNum VARCHAR(16)
)

WHILE (SELECT COUNT(*) FROM @Mytable WHERE Processed=0)>0
BEGIN
     delete from @Unprocessed
     insert into @Unprocessed
     select id, PartNum, RevisionNum 
           FROM @Mytable Where Processed=0

     ;WITH cte_partNum as
        ( select PartNum, MAX(RevisionNum) as RevisionNum 
           FROM PartMtl_3 group by PartNum )

     INSERT INTO @MyTable(PartNum,RevisionNum,Processed,ParentId)
     SELECT PartMtl_3.MtlPartNum, cte_partNum.RevisionNum ,0, Unprocessed.id 
     FROM PartMtl_3 
       inner join cte_partNum 
            on PartMtl_3.MtlPartNum = cte_partNum.PartNum
       inner join @Unprocessed  as Unprocessed 
            on Unprocessed.PartNum = PartMtl_3.PartNum 
                and Unprocessed.RevisionNum = PartMtl_3.RevisionNum

     UPDATE tbl
     set Processed = 1
     from @Matable tbl
        inner join @Unprocessed as Unprocessed
            on tbl.id = Unprocessed.id
END

根据 OP cmets 进行了一些修正。

【讨论】:

Hi Yan,首先感谢您的回答。我不知道这是否会改变你的答案,但我忘了做一点精确,在 partMtl_3 中,RevisionNum 仅适用于 PartNum,所以我不能通过 MtlParNum 做一个 MAX(revisionnum) 组,因为我会有他父母的修订,这会导致一些问题(不会找到那个的子部分)。 进行了一些更改以反映您提供给我的信息。

以上是关于如何使用一段时间优化 SQL 查询(在同一个表中获取和添加数据)?的主要内容,如果未能解决你的问题,请参考以下文章

我应该如何优化这个 SQL 查询?

如何优化这个大表SQL查询的响应时间?

SQL 查询优化:在事实表中两次使用相同指标的最佳方法是啥?

SQL查询优化Oracle

面试官:给你一段有问题的SQL,如何优化?

面试官:给你一段有问题的SQL,如何优化?