如何摆脱游标?
How to get rid of cursors?
我写了一个 SQL 使用游标的查询,我意识到它在服务器上占用了太多资源而且速度也很慢。
所以,这是 table:
我需要计算 Status = Assigned 和 Status = In Progress 之间的时间差,其中 Assigned_group 或 prev_assigned_group 类似于“%Hotline%”。
我也有一个序列,所以我 select 区分事件 ID 并按 Incident_Sequence 升序排列行,因为我必须从最小的序列号开始。
到目前为止一切顺利。
我从第一行开始,然后跳到下一行,直到我发现状态为 "In Progress"。找到状态后,我计算第一个分配的行与状态在其中的行之间的差异发现有进展。
所以最大的问题是:我可以在没有游标的情况下执行此操作吗?如果是,那怎么办?
SET NOCOUNT ON
DECLARE @day DATE
SET @day = '20160606'
CREATE TABLE #result(
[Assigned_Group] NVARCHAR(100),
[ProgressTime] INTEGER,
[Tickets] INTEGER,
[Avarage] FLOAT
)
INSERT INTO #result(Assigned_Group,ProgressTime,Tickets,Avarage)
SELECT DISTINCT Assigned_Group,0,0,0.0
FROM [grs_dwh].[smt].[Aht]
WHERE (Assigned_Group like 'CI-Hotline%' OR Prev_assigned_Group like 'CI-Hotline%')
and CONVERT(DATE,Last_Modified_Date, 104) = @day
-- raw
SELECT [Incident_Sequence]
,[Incident_Id]
,[Assigned_Group]
,[Prev_assigned_Group]
,[Status]
,[Last_Modified_Date]
,[Service]
INTO #rawData
FROM [grs_dwh].[smt].[Aht]
WHERE (Assigned_Group like 'CI-Hotline%' OR Prev_assigned_Group like 'CI-Hotline%')
and CONVERT(DATE,Last_Modified_Date, 104) = @day
ORDER BY Incident_Sequence asc
--CREATE TABLE #orderList(
--)
SELECT DISTINCT[Incident_id] INTO #incidentList FROM #rawData
DECLARE cur0 CURSOR FOR SELECT incident_Id FROM #incidentList
DECLARE @currentIncident NVARCHAR(15)
OPEN cur0
FETCH next from cur0 INTO @currentIncident
WHILE @@FETCH_STATUS = 0
BEGIN
-- PRINT @currentIncident
SELECT * INTO #tmpTable FROM #rawData WHERE Incident_Id = @currentIncident ORDER BY Incident_Sequence
DECLARE cur1 CURSOR FOR SELECT * FROM #tmpTable ORDER BY Incident_Sequence ASC
DECLARE @incident_Sequence INTEGER
DECLARE @incident_Id NVARCHAR(50)
DECLARE @assigned_Group NVARCHAR(100)
DECLARE @previous_Assiggned NVARCHAR(100)
DECLARE @status NVARCHAR(50)
DECLARE @last_Modified_Date DATETIME
DECLARE @service NVARCHAR(50)
DECLARE @progressFound BIT
DECLARE @startProgressDate DATETIME
DECLARE @ticketProgressTime INTEGER
DECLARE @resultGroup NVARCHAR(100)
SET @progressFound = 0
OPEN cur1
FETCH next from cur1
INTO @incident_Sequence, @incident_Id, @assigned_Group, @previous_Assiggned, @status, @last_Modified_Date, @service
WHILE @@FETCH_STATUS = 0
BEGIN
IF @progressFound = 0 AND @status <> 'In Progress'
BEGIN
FETCH next from cur1 INTO @incident_Sequence, @incident_Id, @assigned_Group, @previous_Assiggned, @status, @last_Modified_Date, @service
CONTINUE
END
IF @progressFound = 0
BEGIN
SET @startProgressDate = @last_Modified_Date
SET @resultGroup = @assigned_Group
SET @progressFound = 1
FETCH next from cur1 INTO @incident_Sequence, @incident_Id, @assigned_Group, @previous_Assiggned, @status, @last_Modified_Date, @service
CONTINUE
END
ELSE
BEGIN
SET @ticketProgressTime = DATEDIFF(SECOND, @startProgressDate, @last_Modified_Date)
UPDATE #result SET ProgressTime = ProgressTime + @ticketProgressTime, Tickets = Tickets+1 WHERE Assigned_Group = @resultGroup
SET @ticketProgressTime = 0
SET @progressFound = 0
END
FETCH next from cur1
INTO @incident_Sequence, @incident_Id, @assigned_Group, @previous_Assiggned, @status, @last_Modified_Date, @service
END
CLOSE cur1
DEALLOCATE cur1
--IF @incident_Id = 'INC000010047798'
--SELECT * FROM #tmpTable ORDER BY Incident_Sequence ASC
DROP TABLE #tmpTable
FETCH next from cur0 INTO @currentIncident
END
CLOSE cur0
DEALLOCATE cur0
SET NUMERIC_ROUNDABORT OFF
UPDATE #result SET Avarage = CAST(ProgressTime AS float) / CASE WHEN Tickets = 0 THEN 1 ELSE CAST(Tickets AS float) END
SELECT * FROM #result
ORDER BY 1 asc
DROP TABLE #result
DROP TABLE #rawData
DROP TABLE #incidentList
您可以通过WHILE 重写Cursor(但并非在所有情况下都推荐)。为了加快光标速度,您可以将光标定义为 FAST_FORWARD 而不是摆脱光标。
DECLARE cur1 CURSOR FAST_FORWARD
阅读更多有关通过 WHILE HERE
重写 Cursor 的信息
您可以使用 ROW_NUMBER
函数来跟踪序列并进行自连接以计算这些值:
;WITH Data AS
( -- Sample data (http://i.stack.imgur.com/TfzL7.png)
SELECT Id, Incident_Id Incident, Incident_Sequence Sequence,
Prev_Assigned_Group Previous, Assigned_Group GroupName,
Status, CAST(Last_Modified_Date AS DATETIME) Modified,
ROW_NUMBER() OVER (PARTITION BY Incident_Id ORDER BY Id) RowNumber -- Start over the count for every Incident_Id
FROM ( VALUES
(164293, 05, 'INC000010047798', 'Eastern Europe1' , 'CI-Hotline North America', 'Assigned' , '2016-06-04 12:28:46'),
(171241, 07, 'INC000010047798', 'CI-Hotline' , 'Eastern Europe1' , 'Assigned' , '2016-06-06 06:42:16'),
(171919, 09, 'INC000010047798', 'CI-Hotline' , 'Eastern Europe1' , 'In Progress', '2016-06-06 06:46:19'),
(172138, 10, 'INC000010047798', 'CI-Hotline Romania', 'CI-Hotline' , 'Assigned' , '2016-06-06 06:46:35'),
(172483, 12, 'INC000010047798', 'CI-Hotline Romania', 'CI-Hotline' , 'In Progress', '2016-06-06 07:11:53'),
(173003, 15, 'INC000010047798', 'Austria Adria3' , 'CI-Hotline Romania' , 'Assigned' , '2016-06-06 07:15:36'),
(208011, 17, 'INC000010047798', 'Austria Adria3' , 'CI-Hotline Romania' , 'Resolved' , '2016-06-10 12:14:05')
) AS X(Id, Incident_Sequence, Incident_Id, Assigned_Group, Prev_Assigned_Group, Status, Last_Modified_Date)
WHERE Assigned_Group LIKE '%HOTLINE%' OR
Prev_Assigned_Group LIKE '%HOTLINE%'
)
SELECT Assigned.Incident,
Assigned.Status + ' » ' + InProgress.Status,
DATEDIFF(second, InProgress.Modified, Assigned.Modified) / 60.0 / 60.0 Hours
--,Assigned.*, InProgress.*
FROM Data Assigned
JOIN Data InProgress
ON Assigned.Incident = InProgress.Incident AND
Assigned.RowNumber = InProgress.RowNumber + 1
下次请以文本格式发送您的示例数据;-)
编辑: 要计算不同于 'In Progress'
的任何状态与下一个 'In Progress'
状态(或最后一个可用状态)之间的时间差,使用此代码:
;WITH Data AS
( -- Sample data (http://i.stack.imgur.com/TfzL7.png)
SELECT Id, Incident_Id Incident, Incident_Sequence Sequence,
Prev_Assigned_Group Previous, Assigned_Group GroupName,
Status, CAST(Last_Modified_Date AS DATETIME) Modified,
ROW_NUMBER() OVER
( PARTITION BY Incident_Id
ORDER BY Incident_Sequence
) RowNumber -- Start over the count for every Incident_Id
FROM [aht_data_one_day]
WHERE -- Incident_Id IN ('INC000010164572') AND
(Assigned_Group LIKE '%HOTLINE%' OR Prev_Assigned_Group LIKE '%HOTLINE%')
)
SELECT Assigned.Id, Assigned.Incident,
CAST(Assigned.Sequence AS VARCHAR(5)) + ' » ' +
CAST(InProgress.Sequence AS VARCHAR(5)) Transition,
DATEDIFF(second, Assigned.Modified, InProgress.Modified) TotalSeconds
--, Assigned.*, InProgress.*
FROM Data Assigned
JOIN Data InProgress
ON Assigned.Status NOT IN ('In Progress') AND
InProgress.Id = ISNULL(
( -- Try to locate the next 'In Progress' status
SELECT MIN(Id)
FROM Data
WHERE Status IN ('In Progress') AND
Data.Incident = Assigned.Incident AND
Data.RowNumber > Assigned.RowNumber -- That's the trick
),
( -- If not found, get the latest status
SELECT MAX(Id)
FROM Data
WHERE Data.Incident = Assigned.Incident
))
ORDER BY Assigned.Incident, Assigned.Id
编辑: 在您的数据更新中,我保留了以前的代码并稍作改动。下面是一个大事件的逻辑验证和查询返回的数据:
Id Incident Transition TotalSeconds
----------- --------------- ------------- ------------
172090 INC000010164572 10 » 13 1877
172939 INC000010164572 15 » 25 6578
173241 INC000010164572 17 » 25 4045
173597 INC000010164572 20 » 25 3616
173949 INC000010164572 23 » 25 1125
174298 INC000010164572 27 » 34 981
174468 INC000010164572 30 » 34 287
174647 INC000010164572 33 » 34 100
174773 INC000010164572 36 » 36 0
编辑: 最后一次尝试
SELECT InProgress.Id, InProgress.Incident,
CAST(InProgress.Sequence AS VARCHAR(5)) + ' » ' +
CAST(NextStatus.Sequence AS VARCHAR(5)) Transition,
DATEDIFF(second, InProgress.Modified, NextStatus.Modified) TotalSeconds
-- , InProgress.*, NextStatus.*
FROM Data InProgress
JOIN Data NextStatus
ON InProgress.Status IN ('In Progress') AND
InProgress.Incident = NextStatus.Incident AND
NextStatus.Id = ISNULL(
( -- Try to locate the next status different than 'In Progress'
SELECT MIN(Id)
FROM Data
WHERE Status NOT IN ('In Progress') AND
Data.Incident = InProgress.Incident AND
Data.RowNumber > InProgress.RowNumber -- That's the trick
),
( -- If not found, get the latest status
SELECT MAX(Id)
FROM Data
WHERE Data.Incident = InProgress.Incident
))
ORDER BY InProgress.Incident, InProgress.Id
输出:
Id Incident Transition TotalSeconds
----------- --------------- ------------- ------------
172564 INC000010164572 13 » 15 236
174123 INC000010164572 25 » 27 688
174689 INC000010164572 34 » 36 77
祝你好运。
我写了一个 SQL 使用游标的查询,我意识到它在服务器上占用了太多资源而且速度也很慢。
所以,这是 table:
我需要计算 Status = Assigned 和 Status = In Progress 之间的时间差,其中 Assigned_group 或 prev_assigned_group 类似于“%Hotline%”。
我也有一个序列,所以我 select 区分事件 ID 并按 Incident_Sequence 升序排列行,因为我必须从最小的序列号开始。
到目前为止一切顺利。
我从第一行开始,然后跳到下一行,直到我发现状态为 "In Progress"。找到状态后,我计算第一个分配的行与状态在其中的行之间的差异发现有进展。
所以最大的问题是:我可以在没有游标的情况下执行此操作吗?如果是,那怎么办?
SET NOCOUNT ON
DECLARE @day DATE
SET @day = '20160606'
CREATE TABLE #result(
[Assigned_Group] NVARCHAR(100),
[ProgressTime] INTEGER,
[Tickets] INTEGER,
[Avarage] FLOAT
)
INSERT INTO #result(Assigned_Group,ProgressTime,Tickets,Avarage)
SELECT DISTINCT Assigned_Group,0,0,0.0
FROM [grs_dwh].[smt].[Aht]
WHERE (Assigned_Group like 'CI-Hotline%' OR Prev_assigned_Group like 'CI-Hotline%')
and CONVERT(DATE,Last_Modified_Date, 104) = @day
-- raw
SELECT [Incident_Sequence]
,[Incident_Id]
,[Assigned_Group]
,[Prev_assigned_Group]
,[Status]
,[Last_Modified_Date]
,[Service]
INTO #rawData
FROM [grs_dwh].[smt].[Aht]
WHERE (Assigned_Group like 'CI-Hotline%' OR Prev_assigned_Group like 'CI-Hotline%')
and CONVERT(DATE,Last_Modified_Date, 104) = @day
ORDER BY Incident_Sequence asc
--CREATE TABLE #orderList(
--)
SELECT DISTINCT[Incident_id] INTO #incidentList FROM #rawData
DECLARE cur0 CURSOR FOR SELECT incident_Id FROM #incidentList
DECLARE @currentIncident NVARCHAR(15)
OPEN cur0
FETCH next from cur0 INTO @currentIncident
WHILE @@FETCH_STATUS = 0
BEGIN
-- PRINT @currentIncident
SELECT * INTO #tmpTable FROM #rawData WHERE Incident_Id = @currentIncident ORDER BY Incident_Sequence
DECLARE cur1 CURSOR FOR SELECT * FROM #tmpTable ORDER BY Incident_Sequence ASC
DECLARE @incident_Sequence INTEGER
DECLARE @incident_Id NVARCHAR(50)
DECLARE @assigned_Group NVARCHAR(100)
DECLARE @previous_Assiggned NVARCHAR(100)
DECLARE @status NVARCHAR(50)
DECLARE @last_Modified_Date DATETIME
DECLARE @service NVARCHAR(50)
DECLARE @progressFound BIT
DECLARE @startProgressDate DATETIME
DECLARE @ticketProgressTime INTEGER
DECLARE @resultGroup NVARCHAR(100)
SET @progressFound = 0
OPEN cur1
FETCH next from cur1
INTO @incident_Sequence, @incident_Id, @assigned_Group, @previous_Assiggned, @status, @last_Modified_Date, @service
WHILE @@FETCH_STATUS = 0
BEGIN
IF @progressFound = 0 AND @status <> 'In Progress'
BEGIN
FETCH next from cur1 INTO @incident_Sequence, @incident_Id, @assigned_Group, @previous_Assiggned, @status, @last_Modified_Date, @service
CONTINUE
END
IF @progressFound = 0
BEGIN
SET @startProgressDate = @last_Modified_Date
SET @resultGroup = @assigned_Group
SET @progressFound = 1
FETCH next from cur1 INTO @incident_Sequence, @incident_Id, @assigned_Group, @previous_Assiggned, @status, @last_Modified_Date, @service
CONTINUE
END
ELSE
BEGIN
SET @ticketProgressTime = DATEDIFF(SECOND, @startProgressDate, @last_Modified_Date)
UPDATE #result SET ProgressTime = ProgressTime + @ticketProgressTime, Tickets = Tickets+1 WHERE Assigned_Group = @resultGroup
SET @ticketProgressTime = 0
SET @progressFound = 0
END
FETCH next from cur1
INTO @incident_Sequence, @incident_Id, @assigned_Group, @previous_Assiggned, @status, @last_Modified_Date, @service
END
CLOSE cur1
DEALLOCATE cur1
--IF @incident_Id = 'INC000010047798'
--SELECT * FROM #tmpTable ORDER BY Incident_Sequence ASC
DROP TABLE #tmpTable
FETCH next from cur0 INTO @currentIncident
END
CLOSE cur0
DEALLOCATE cur0
SET NUMERIC_ROUNDABORT OFF
UPDATE #result SET Avarage = CAST(ProgressTime AS float) / CASE WHEN Tickets = 0 THEN 1 ELSE CAST(Tickets AS float) END
SELECT * FROM #result
ORDER BY 1 asc
DROP TABLE #result
DROP TABLE #rawData
DROP TABLE #incidentList
您可以通过WHILE 重写Cursor(但并非在所有情况下都推荐)。为了加快光标速度,您可以将光标定义为 FAST_FORWARD 而不是摆脱光标。
DECLARE cur1 CURSOR FAST_FORWARD
阅读更多有关通过 WHILE HERE
重写 Cursor 的信息您可以使用 ROW_NUMBER
函数来跟踪序列并进行自连接以计算这些值:
;WITH Data AS
( -- Sample data (http://i.stack.imgur.com/TfzL7.png)
SELECT Id, Incident_Id Incident, Incident_Sequence Sequence,
Prev_Assigned_Group Previous, Assigned_Group GroupName,
Status, CAST(Last_Modified_Date AS DATETIME) Modified,
ROW_NUMBER() OVER (PARTITION BY Incident_Id ORDER BY Id) RowNumber -- Start over the count for every Incident_Id
FROM ( VALUES
(164293, 05, 'INC000010047798', 'Eastern Europe1' , 'CI-Hotline North America', 'Assigned' , '2016-06-04 12:28:46'),
(171241, 07, 'INC000010047798', 'CI-Hotline' , 'Eastern Europe1' , 'Assigned' , '2016-06-06 06:42:16'),
(171919, 09, 'INC000010047798', 'CI-Hotline' , 'Eastern Europe1' , 'In Progress', '2016-06-06 06:46:19'),
(172138, 10, 'INC000010047798', 'CI-Hotline Romania', 'CI-Hotline' , 'Assigned' , '2016-06-06 06:46:35'),
(172483, 12, 'INC000010047798', 'CI-Hotline Romania', 'CI-Hotline' , 'In Progress', '2016-06-06 07:11:53'),
(173003, 15, 'INC000010047798', 'Austria Adria3' , 'CI-Hotline Romania' , 'Assigned' , '2016-06-06 07:15:36'),
(208011, 17, 'INC000010047798', 'Austria Adria3' , 'CI-Hotline Romania' , 'Resolved' , '2016-06-10 12:14:05')
) AS X(Id, Incident_Sequence, Incident_Id, Assigned_Group, Prev_Assigned_Group, Status, Last_Modified_Date)
WHERE Assigned_Group LIKE '%HOTLINE%' OR
Prev_Assigned_Group LIKE '%HOTLINE%'
)
SELECT Assigned.Incident,
Assigned.Status + ' » ' + InProgress.Status,
DATEDIFF(second, InProgress.Modified, Assigned.Modified) / 60.0 / 60.0 Hours
--,Assigned.*, InProgress.*
FROM Data Assigned
JOIN Data InProgress
ON Assigned.Incident = InProgress.Incident AND
Assigned.RowNumber = InProgress.RowNumber + 1
下次请以文本格式发送您的示例数据;-)
编辑: 要计算不同于 'In Progress'
的任何状态与下一个 'In Progress'
状态(或最后一个可用状态)之间的时间差,使用此代码:
;WITH Data AS
( -- Sample data (http://i.stack.imgur.com/TfzL7.png)
SELECT Id, Incident_Id Incident, Incident_Sequence Sequence,
Prev_Assigned_Group Previous, Assigned_Group GroupName,
Status, CAST(Last_Modified_Date AS DATETIME) Modified,
ROW_NUMBER() OVER
( PARTITION BY Incident_Id
ORDER BY Incident_Sequence
) RowNumber -- Start over the count for every Incident_Id
FROM [aht_data_one_day]
WHERE -- Incident_Id IN ('INC000010164572') AND
(Assigned_Group LIKE '%HOTLINE%' OR Prev_Assigned_Group LIKE '%HOTLINE%')
)
SELECT Assigned.Id, Assigned.Incident,
CAST(Assigned.Sequence AS VARCHAR(5)) + ' » ' +
CAST(InProgress.Sequence AS VARCHAR(5)) Transition,
DATEDIFF(second, Assigned.Modified, InProgress.Modified) TotalSeconds
--, Assigned.*, InProgress.*
FROM Data Assigned
JOIN Data InProgress
ON Assigned.Status NOT IN ('In Progress') AND
InProgress.Id = ISNULL(
( -- Try to locate the next 'In Progress' status
SELECT MIN(Id)
FROM Data
WHERE Status IN ('In Progress') AND
Data.Incident = Assigned.Incident AND
Data.RowNumber > Assigned.RowNumber -- That's the trick
),
( -- If not found, get the latest status
SELECT MAX(Id)
FROM Data
WHERE Data.Incident = Assigned.Incident
))
ORDER BY Assigned.Incident, Assigned.Id
编辑: 在您的数据更新中,我保留了以前的代码并稍作改动。下面是一个大事件的逻辑验证和查询返回的数据:
Id Incident Transition TotalSeconds
----------- --------------- ------------- ------------
172090 INC000010164572 10 » 13 1877
172939 INC000010164572 15 » 25 6578
173241 INC000010164572 17 » 25 4045
173597 INC000010164572 20 » 25 3616
173949 INC000010164572 23 » 25 1125
174298 INC000010164572 27 » 34 981
174468 INC000010164572 30 » 34 287
174647 INC000010164572 33 » 34 100
174773 INC000010164572 36 » 36 0
编辑: 最后一次尝试
SELECT InProgress.Id, InProgress.Incident,
CAST(InProgress.Sequence AS VARCHAR(5)) + ' » ' +
CAST(NextStatus.Sequence AS VARCHAR(5)) Transition,
DATEDIFF(second, InProgress.Modified, NextStatus.Modified) TotalSeconds
-- , InProgress.*, NextStatus.*
FROM Data InProgress
JOIN Data NextStatus
ON InProgress.Status IN ('In Progress') AND
InProgress.Incident = NextStatus.Incident AND
NextStatus.Id = ISNULL(
( -- Try to locate the next status different than 'In Progress'
SELECT MIN(Id)
FROM Data
WHERE Status NOT IN ('In Progress') AND
Data.Incident = InProgress.Incident AND
Data.RowNumber > InProgress.RowNumber -- That's the trick
),
( -- If not found, get the latest status
SELECT MAX(Id)
FROM Data
WHERE Data.Incident = InProgress.Incident
))
ORDER BY InProgress.Incident, InProgress.Id
输出:
Id Incident Transition TotalSeconds
----------- --------------- ------------- ------------
172564 INC000010164572 13 » 15 236
174123 INC000010164572 25 » 27 688
174689 INC000010164572 34 » 36 77
祝你好运。