SQL 2005:可以使用 Cursor 优化 upsert-like 存储过程吗?
SQL 2005: Optimize upsert-like Stored Procedure using Cursor, possible?
好的,这是我第二次尝试解决这个问题。
我想知道是否有可能优化为存储过程创建的游标,该游标用于迭代具有两个联合的大 select 语句。随后,存储过程开始将值插入到暂存 table 中,根据 "where not exist" select 语句检查每个值。
或者更好的是,是否可以使用 select 语句创建所有这些并可能加入。
插入过程需要很长时间才能完成,我认为select处理数据会快得多。
这里是 SQL 的例子:
declare @ID1 varchar(40) ,
@ID2 varchar(20) ,
@State varchar(20) ,
@isActive bit
Declare CuTable SCROLL INSENSITIVE cursor for
Select
Cast(ID1 as Varchar(20)) AS ID1,
Cast(ID2 as Varchar(20)) AS ID2,
'AT' AS [State],
CASE When (isAvtiveDate > { fn CURDATE() }) or isAvtiveDate is null Then 1 else 0 end AS isAvtive
From
server1.db.dbo.table1
Inner Join
server1.db.dbo.table2 on ID2 = ID1
Where ID3 = 1 AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
UNION
Select
Cast(ID1 as Varchar(20)) AS ID1,
Cast(ID2 as Varchar(20)) AS ID2,
'AP' AS [State],
CASE When (isActiveDate > { fn CURDATE() }) or isActiveDate is null Then 1 else 0 end AS isActive
From
server1.db.dbo.table1
Inner Join
server1.db.dbo.table2 on ID2 = ID1
Where
ID3 = 2 AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
UNION
Select
Cast(ID1 as Varchar(20)) AS ID1,
Cast(ID2 as Varchar(20)) AS ID2,
'AH' AS [State],
CASE When (isActiveDate > { fn CURDATE() }) or isActiveDate is null Then 1 else 0 end AS isActive
From server1.db.dbo.table1 inner join server1.db.dbo.table2 on ID2 = ID1
inner join server1.db.dbo.table13 on ID2 = ID4
Where ID3 = 5 and toDate is null and fromDate is not null AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
Open CuTable
Fetch Next From CuTable Into @ID1, @ID2, @[State], @isActive
While @@Fetch_Status = 0
Begin
Insert Into StagingTable (ID1, ID2, [State], isActive)
--Values
Select @ID1, @ID2, @[State], @isActive
where not exists(select * from StagingTable where ID1 = @ID1 and ID2 = @ID2)
Fetch Next From CuTable Into @ID1, @ID2, @[State], @isActive
End
close CuTable
deallocate CuTable
注意:我正在使用 SQL SERVER 2005
关于 Leonidas199x 评论线程的更新:
不要认为您根本不需要游标,因为数据不是动态变化的。您应该能够使用基于集合的方法来执行此操作。下面是一个使用 CTE 的示例,左连接仅插入暂存中不存在的那些 table:
;WITH CTE AS
(
SELECT CAST(ID1 as Varchar(20)) AS ID1,
CAST(ID2 as Varchar(20)) AS ID2,
'AT' AS [State],
CASE When (isAvtiveDate > { fn CURDATE() }) or isAvtiveDate is null Then 1 else 0 end AS isAvtive
FROM server1.db.dbo.table1
INNER JOIN server1.db.dbo.table2 on ID2 = ID1
WHERE ID3 = 1 AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
UNION
SELECT CAST(ID1 as Varchar(20)) AS ID1,
CAST(ID2 as Varchar(20)) AS ID2,
'AP' AS [State],
CASE WHEN (isActiveDate > { fn CURDATE() }) or isActiveDate is null Then 1 else 0 end AS isActive
FROM server1.db.dbo.table1
INNER JOIN server1.db.dbo.table2 on ID2 = ID1
WHERE ID3 = 2 AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
UNION
SELECT
Cast(ID1 as Varchar(20)) AS ID1,
Cast(ID2 as Varchar(20)) AS ID2,
'AH' AS [State],
CASE When (isActiveDate > { fn CURDATE() }) or isActiveDate is null Then 1 else 0 end AS isActive
FROM server1.db.dbo.table1
INNER JOIN server1.db.dbo.table2 ON ID2 = ID1
INNER JOIN server1.db.dbo.table13 ON ID2 = ID4
WHERE ID3 = 5 and toDate is null and fromDate is not null AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
)
INSERT INTO StagingTable
(
ID1,
ID2,
[State],
isActive
)
SELECT DISTINCT
CT.ID1,
CT.ID2,
CT.[State],
CT.isActive
FROM CTE AS CT
LEFT JOIN StagingTable AS ST ON ST.ID1 = CT.ID1 AND ST.ID2 = CT.ID2
WHERE ST.ID1 IS NULL
AND ST.ID2 IS NULL;
鉴于需要像游标一样检查每一行,我将使用以下内容,使用临时 table 检查标识的每组 ID1 和 ID2 在插入临时 table,然后从临时 table:
插入暂存 table
/*Create temp table*/
IF OBJECT_ID('tempdb..#tmpData') IS NOT NULL DROP TABLE #tmpData
GO
CREATE TABLE #tmpData
(
ID1 VARCHAR(20) ,
ID2 VARCHAR(20) ,
[State] VARCHAR(2) ,
IsActiveData BIT
)
/*Insert into the temp table, with each insert join back to the temp table to ensure ID1 and ID2 are not already inserted*/
INSERT INTO #tmpData
(
ID1 ,
ID2 ,
[State] ,
IsActiveData
)
SELECT CAST(ID1 as Varchar(20)) AS ID1,
CAST(ID2 as Varchar(20)) AS ID2,
'AT' AS [State],
CASE WHEN (isAvtiveDate > { fn CURDATE() }) or isAvtiveDate is null Then 1 else 0 end AS isAvtive
FROM server1.db.dbo.table1
INNER JOIN server1.db.dbo.table2 on ID2 = ID1
WHERE ID3 = 1 AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
INSERT INTO #tmpData
(
ID1 ,
ID2 ,
[State] ,
IsActiveData
)
SELECT CAST(T1.ID1 as VARCHAR(20)) AS ID1,
CAST(T2.ID2 as VARCHAR(20)) AS ID2,
'AP' AS [State],
CASE WHEN (isActiveDate > { fn CURDATE() }) or isActiveDate is null Then 1 else 0 end AS isActive
FROM server1.db.dbo.table1 AS T1
INNER JOIN server1.db.dbo.table2 AS T2 ON T2.ID2 = T1.ID1
LEFT JOIN #tmpData AS T ON T.ID1 = T1.ID1 AND T.ID2 = T2.ID2
WHERE ID3 = 2 AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
AND T.ID1 IS NULL
AND T.ID2 IS NULL
INSERT INTO #tmpData
(
ID1 ,
ID2 ,
[State] ,
IsActiveData
)
SELECT
Cast(T1.ID1 as Varchar(20)) AS ID1,
Cast(T2.ID2 as Varchar(20)) AS ID2,
'AH' AS [State],
CASE When (isActiveDate > { fn CURDATE() }) OR isActiveDate IS NULL Then 1 else 0 end AS isActive
FROM server1.db.dbo.table1 AS T1
INNER JOIN server1.db.dbo.table2 AS T2 ON T2.ID2 = T1.ID1
INNER JOIN server1.db.dbo.table13 AS T13 ON T2.ID2 = T13.ID4
LEFT JOIN #tmpData AS T ON T.ID1 = T1.ID1 AND T.ID2 = T2.ID2
WHERE ID3 = 5
AND toDate IS NULL
AND fromDate IS NOT NULL
AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
AND T.ID1 IS NULL
AND T.ID2 IS NULL
/*Insert into the staging table from the temp table ensuring only records that are not already in there are inserted.*/
INSERT INTO StagingTable
(
ID1,
ID2,
[State],
isActive
)
SELECT CT.ID1,
CT.ID2,
CT.[State],
CT.isActive
FROM #tmpData AS CT
LEFT JOIN StagingTable AS ST ON ST.ID1 = CT.ID1 AND ST.ID2 = CT.ID2
WHERE ST.ID1 IS NULL
AND ST.ID2 IS NULL;
好的,这是我第二次尝试解决这个问题。
我想知道是否有可能优化为存储过程创建的游标,该游标用于迭代具有两个联合的大 select 语句。随后,存储过程开始将值插入到暂存 table 中,根据 "where not exist" select 语句检查每个值。
或者更好的是,是否可以使用 select 语句创建所有这些并可能加入。
插入过程需要很长时间才能完成,我认为select处理数据会快得多。
这里是 SQL 的例子:
declare @ID1 varchar(40) ,
@ID2 varchar(20) ,
@State varchar(20) ,
@isActive bit
Declare CuTable SCROLL INSENSITIVE cursor for
Select
Cast(ID1 as Varchar(20)) AS ID1,
Cast(ID2 as Varchar(20)) AS ID2,
'AT' AS [State],
CASE When (isAvtiveDate > { fn CURDATE() }) or isAvtiveDate is null Then 1 else 0 end AS isAvtive
From
server1.db.dbo.table1
Inner Join
server1.db.dbo.table2 on ID2 = ID1
Where ID3 = 1 AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
UNION
Select
Cast(ID1 as Varchar(20)) AS ID1,
Cast(ID2 as Varchar(20)) AS ID2,
'AP' AS [State],
CASE When (isActiveDate > { fn CURDATE() }) or isActiveDate is null Then 1 else 0 end AS isActive
From
server1.db.dbo.table1
Inner Join
server1.db.dbo.table2 on ID2 = ID1
Where
ID3 = 2 AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
UNION
Select
Cast(ID1 as Varchar(20)) AS ID1,
Cast(ID2 as Varchar(20)) AS ID2,
'AH' AS [State],
CASE When (isActiveDate > { fn CURDATE() }) or isActiveDate is null Then 1 else 0 end AS isActive
From server1.db.dbo.table1 inner join server1.db.dbo.table2 on ID2 = ID1
inner join server1.db.dbo.table13 on ID2 = ID4
Where ID3 = 5 and toDate is null and fromDate is not null AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
Open CuTable
Fetch Next From CuTable Into @ID1, @ID2, @[State], @isActive
While @@Fetch_Status = 0
Begin
Insert Into StagingTable (ID1, ID2, [State], isActive)
--Values
Select @ID1, @ID2, @[State], @isActive
where not exists(select * from StagingTable where ID1 = @ID1 and ID2 = @ID2)
Fetch Next From CuTable Into @ID1, @ID2, @[State], @isActive
End
close CuTable
deallocate CuTable
注意:我正在使用 SQL SERVER 2005
关于 Leonidas199x 评论线程的更新:
不要认为您根本不需要游标,因为数据不是动态变化的。您应该能够使用基于集合的方法来执行此操作。下面是一个使用 CTE 的示例,左连接仅插入暂存中不存在的那些 table:
;WITH CTE AS
(
SELECT CAST(ID1 as Varchar(20)) AS ID1,
CAST(ID2 as Varchar(20)) AS ID2,
'AT' AS [State],
CASE When (isAvtiveDate > { fn CURDATE() }) or isAvtiveDate is null Then 1 else 0 end AS isAvtive
FROM server1.db.dbo.table1
INNER JOIN server1.db.dbo.table2 on ID2 = ID1
WHERE ID3 = 1 AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
UNION
SELECT CAST(ID1 as Varchar(20)) AS ID1,
CAST(ID2 as Varchar(20)) AS ID2,
'AP' AS [State],
CASE WHEN (isActiveDate > { fn CURDATE() }) or isActiveDate is null Then 1 else 0 end AS isActive
FROM server1.db.dbo.table1
INNER JOIN server1.db.dbo.table2 on ID2 = ID1
WHERE ID3 = 2 AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
UNION
SELECT
Cast(ID1 as Varchar(20)) AS ID1,
Cast(ID2 as Varchar(20)) AS ID2,
'AH' AS [State],
CASE When (isActiveDate > { fn CURDATE() }) or isActiveDate is null Then 1 else 0 end AS isActive
FROM server1.db.dbo.table1
INNER JOIN server1.db.dbo.table2 ON ID2 = ID1
INNER JOIN server1.db.dbo.table13 ON ID2 = ID4
WHERE ID3 = 5 and toDate is null and fromDate is not null AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
)
INSERT INTO StagingTable
(
ID1,
ID2,
[State],
isActive
)
SELECT DISTINCT
CT.ID1,
CT.ID2,
CT.[State],
CT.isActive
FROM CTE AS CT
LEFT JOIN StagingTable AS ST ON ST.ID1 = CT.ID1 AND ST.ID2 = CT.ID2
WHERE ST.ID1 IS NULL
AND ST.ID2 IS NULL;
鉴于需要像游标一样检查每一行,我将使用以下内容,使用临时 table 检查标识的每组 ID1 和 ID2 在插入临时 table,然后从临时 table:
插入暂存 table/*Create temp table*/
IF OBJECT_ID('tempdb..#tmpData') IS NOT NULL DROP TABLE #tmpData
GO
CREATE TABLE #tmpData
(
ID1 VARCHAR(20) ,
ID2 VARCHAR(20) ,
[State] VARCHAR(2) ,
IsActiveData BIT
)
/*Insert into the temp table, with each insert join back to the temp table to ensure ID1 and ID2 are not already inserted*/
INSERT INTO #tmpData
(
ID1 ,
ID2 ,
[State] ,
IsActiveData
)
SELECT CAST(ID1 as Varchar(20)) AS ID1,
CAST(ID2 as Varchar(20)) AS ID2,
'AT' AS [State],
CASE WHEN (isAvtiveDate > { fn CURDATE() }) or isAvtiveDate is null Then 1 else 0 end AS isAvtive
FROM server1.db.dbo.table1
INNER JOIN server1.db.dbo.table2 on ID2 = ID1
WHERE ID3 = 1 AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
INSERT INTO #tmpData
(
ID1 ,
ID2 ,
[State] ,
IsActiveData
)
SELECT CAST(T1.ID1 as VARCHAR(20)) AS ID1,
CAST(T2.ID2 as VARCHAR(20)) AS ID2,
'AP' AS [State],
CASE WHEN (isActiveDate > { fn CURDATE() }) or isActiveDate is null Then 1 else 0 end AS isActive
FROM server1.db.dbo.table1 AS T1
INNER JOIN server1.db.dbo.table2 AS T2 ON T2.ID2 = T1.ID1
LEFT JOIN #tmpData AS T ON T.ID1 = T1.ID1 AND T.ID2 = T2.ID2
WHERE ID3 = 2 AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
AND T.ID1 IS NULL
AND T.ID2 IS NULL
INSERT INTO #tmpData
(
ID1 ,
ID2 ,
[State] ,
IsActiveData
)
SELECT
Cast(T1.ID1 as Varchar(20)) AS ID1,
Cast(T2.ID2 as Varchar(20)) AS ID2,
'AH' AS [State],
CASE When (isActiveDate > { fn CURDATE() }) OR isActiveDate IS NULL Then 1 else 0 end AS isActive
FROM server1.db.dbo.table1 AS T1
INNER JOIN server1.db.dbo.table2 AS T2 ON T2.ID2 = T1.ID1
INNER JOIN server1.db.dbo.table13 AS T13 ON T2.ID2 = T13.ID4
LEFT JOIN #tmpData AS T ON T.ID1 = T1.ID1 AND T.ID2 = T2.ID2
WHERE ID3 = 5
AND toDate IS NULL
AND fromDate IS NOT NULL
AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
AND T.ID1 IS NULL
AND T.ID2 IS NULL
/*Insert into the staging table from the temp table ensuring only records that are not already in there are inserted.*/
INSERT INTO StagingTable
(
ID1,
ID2,
[State],
isActive
)
SELECT CT.ID1,
CT.ID2,
CT.[State],
CT.isActive
FROM #tmpData AS CT
LEFT JOIN StagingTable AS ST ON ST.ID1 = CT.ID1 AND ST.ID2 = CT.ID2
WHERE ST.ID1 IS NULL
AND ST.ID2 IS NULL;