在没有光标的SQL Server中循环

bhe*_*zel 5 sql t-sql sql-server sql-server-2012

下面是我一直在研究的一些SQL Server代码.我现在知道使用光标通常是一个坏主意,但我无法弄清楚我还能做些什么.光标的性能很糟糕.我真的只是使用一些简单的IF语句逻辑与循环,但不能将其转换为SQL.我正在使用SQL Server 2012.

IF [Last Employee] = [Employee] AND [Action] = '1-HR'
    SET [Employee Record] = @counter + 1
ELSE IF [Last Employee] != [Employee] OR [Last Employee] IS NULL
    SET [Employee Record] = 1
ELSE
    SET [Employee Record] = @counter
Run Code Online (Sandbox Code Playgroud)

基本上,如何在没有光标的情况下保持这个@counter.我觉得解决方案很简单,但我迷失了自己.谢谢你的期待.

declare curr cursor for
select WORKER, SEQUENCE, ACTION
FROM [DB].[Transactional History]
order by WORKER ,SEQUENCE asc

declare @EmployeeID as nvarchar(max);
declare @SequenceNum as nvarchar(max);
declare @LastEEID as nvarchar(max);
declare @action as nvarchar(max);
declare @currentEmpRecord int
declare @counter int;

open curr
fetch next from curr into @EmployeeID, @SequenceNum, @action;
while @@FETCH_STATUS=0

begin 
    if @LastEEID=@EmployeeID and @action='1-HR'
    begin
        set @sql = concat('update [DB].[Transactional History]
        set EMPRECORD=',+ @currentEmpRecord, '+1 
        where WORKER=', @EmployeeID, ' and SEQUENCE=', @SequenceNum)
        EXECUTE sp_executesql @sql 
        set @counter=@counter+1;
        set @LastEEID=@EmployeeID;
        set @currentEmpRecord=@currentEmpRecord+1;
    end
    else if @LastEEID is null or @LastEEID<>@EmployeeID
        begin
            set @sql = concat('update [DB].[Transactional History]
            set EMPRECORD=1
            where WORKER=', @EmployeeID, ' and SEQUENCE=', @SequenceNum)
            EXECUTE sp_executesql @sql
            set @counter=@counter+1;
            set @LastEEID=@EmployeeID;
            set @currentEmpRecord=1
        end
    else
        begin
            set @sql = concat('update [DB].[Transactional History]
            set EMPRECORD=', @currentEmpRecord, ' 
            where WORKER=', @EmployeeID, ' and SEQUENCE=', @SequenceNum)
            EXECUTE sp_executesql @sql
            set @counter=@counter+1;
        end
    fetch next from curr into @EmployeeID, @SequenceNum, @action;
    end

close curr;
deallocate curr;
Run Code Online (Sandbox Code Playgroud)

下面是构建示例表的代码.我想在每次记录为'1-HR'时增加EMPRECORD,但是为每个新的WORKER重置它.在执行此代码之前,EMPRECORD对所有记录都为null.此表显示目标输出.

CREATE TABLE [DB].[Transactional History-test](
    [WORKER] [nvarchar](255) NULL,
    [SOURCE] [nvarchar](50) NULL,
    [TAB] [nvarchar](25) NULL,
    [EFFECTIVE_DATE] [date] NULL,
    [ACTION] [nvarchar](5) NULL,
    [SEQUENCE] [numeric](26, 0) NULL,
    [EMPRECORD] [numeric](26, 0) NULL,
    [MANAGER] [nvarchar](255) NULL,
    [PAYRATE] [nvarchar](20) NULL,
    [SALARY_PLAN] [nvarchar](1) NULL,
    [HOURLY_PLAN] [nvarchar](1) NULL,
    [LAST_MANAGER] [nvarchar](255) NULL
) ON [PRIMARY]

GO
INSERT [DB].[Transactional History-test] ([WORKER], [SOURCE], [TAB], [EFFECTIVE_DATE], [ACTION], [SEQUENCE], [EMPRECORD], [MANAGER], [PAYRATE], [SALARY_PLAN], [HOURLY_PLAN], [LAST_MANAGER]) VALUES (N'1', NULL, N'EMP-Position Mgt', CAST(N'2004-01-01' AS Date), N'1-HR', CAST(1 AS Numeric(26, 0)), CAST(1 AS Numeric(26, 0)), N'3', N'Hourly', NULL, NULL, NULL)
GO
INSERT [DB].[Transactional History-test] ([WORKER], [SOURCE], [TAB], [EFFECTIVE_DATE], [ACTION], [SEQUENCE], [EMPRECORD], [MANAGER], [PAYRATE], [SALARY_PLAN], [HOURLY_PLAN], [LAST_MANAGER]) VALUES (N'1', NULL, N'Change Job', CAST(N'2004-05-01' AS Date), N'5-JC', CAST(2 AS Numeric(26, 0)), CAST(1 AS Numeric(26, 0)), N'4', NULL, NULL, NULL, N'3')
GO
INSERT [DB].[Transactional History-test] ([WORKER], [SOURCE], [TAB], [EFFECTIVE_DATE], [ACTION], [SEQUENCE], [EMPRECORD], [MANAGER], [PAYRATE], [SALARY_PLAN], [HOURLY_PLAN], [LAST_MANAGER]) VALUES (N'1', NULL, N'EMP-Terminations', CAST(N'2005-01-01' AS Date), N'6-TR', CAST(3 AS Numeric(26, 0)), CAST(1 AS Numeric(26, 0)), N'4', NULL, NULL, NULL, N'4')
GO
INSERT [DB].[Transactional History-test] ([WORKER], [SOURCE], [TAB], [EFFECTIVE_DATE], [ACTION], [SEQUENCE], [EMPRECORD], [MANAGER], [PAYRATE], [SALARY_PLAN], [HOURLY_PLAN], [LAST_MANAGER]) VALUES (N'1', NULL, N'Change Job', CAST(N'2010-05-01' AS Date), N'5-JC', CAST(4 AS Numeric(26, 0)), CAST(1 AS Numeric(26, 0)), N'3', NULL, NULL, NULL, N'4')
GO
INSERT [DB].[Transactional History-test] ([WORKER], [SOURCE], [TAB], [EFFECTIVE_DATE], [ACTION], [SEQUENCE], [EMPRECORD], [MANAGER], [PAYRATE], [SALARY_PLAN], [HOURLY_PLAN], [LAST_MANAGER]) VALUES (N'1', NULL, N'EMP-Position Mgt', CAST(N'2011-05-01' AS Date), N'1-HR', CAST(5 AS Numeric(26, 0)), CAST(2 AS Numeric(26, 0)), N'3', N'Hourly', NULL, NULL, NULL)
GO
INSERT [DB].[Transactional History-test] ([WORKER], [SOURCE], [TAB], [EFFECTIVE_DATE], [ACTION], [SEQUENCE], [EMPRECORD], [MANAGER], [PAYRATE], [SALARY_PLAN], [HOURLY_PLAN], [LAST_MANAGER]) VALUES (N'1', NULL, N'CWR-Position Mgt', CAST(N'2012-01-01' AS Date), N'1-HR', CAST(6 AS Numeric(26, 0)), CAST(3 AS Numeric(26, 0)), NULL, NULL, NULL, NULL, NULL)
GO
INSERT [DB].[Transactional History-test] ([WORKER], [SOURCE], [TAB], [EFFECTIVE_DATE], [ACTION], [SEQUENCE], [EMPRECORD], [MANAGER], [PAYRATE], [SALARY_PLAN], [HOURLY_PLAN], [LAST_MANAGER]) VALUES (N'1', NULL, N'Organizations', CAST(N'2015-01-01' AS Date), N'3-ORG', CAST(7 AS Numeric(26, 0)), CAST(3 AS Numeric(26, 0)), NULL, NULL, NULL, NULL, NULL)
GO
INSERT [DB].[Transactional History-test] ([WORKER], [SOURCE], [TAB], [EFFECTIVE_DATE], [ACTION], [SEQUENCE], [EMPRECORD], [MANAGER], [PAYRATE], [SALARY_PLAN], [HOURLY_PLAN], [LAST_MANAGER]) VALUES (N'1', NULL, N'Organizations', CAST(N'2015-01-01' AS Date), N'3-ORG', CAST(8 AS Numeric(26, 0)), CAST(3 AS Numeric(26, 0)), NULL, NULL, NULL, NULL, NULL)
GO
INSERT [DB].[Transactional History-test] ([WORKER], [SOURCE], [TAB], [EFFECTIVE_DATE], [ACTION], [SEQUENCE], [EMPRECORD], [MANAGER], [PAYRATE], [SALARY_PLAN], [HOURLY_PLAN], [LAST_MANAGER]) VALUES (N'2', NULL, N'EMP-Terminations', CAST(N'2001-01-01' AS Date), N'6-TR', CAST(9 AS Numeric(26, 0)), CAST(1 AS Numeric(26, 0)), NULL, NULL, NULL, NULL, NULL)
GO
INSERT [DB].[Transactional History-test] ([WORKER], [SOURCE], [TAB], [EFFECTIVE_DATE], [ACTION], [SEQUENCE], [EMPRECORD], [MANAGER], [PAYRATE], [SALARY_PLAN], [HOURLY_PLAN], [LAST_MANAGER]) VALUES (N'2', NULL, N'EMP-Terminations', CAST(N'2001-05-01' AS Date), N'6-TR', CAST(10 AS Numeric(26, 0)), CAST(1 AS Numeric(26, 0)), NULL, NULL, NULL, NULL, NULL)
GO
INSERT [DB].[Transactional History-test] ([WORKER], [SOURCE], [TAB], [EFFECTIVE_DATE], [ACTION], [SEQUENCE], [EMPRECORD], [MANAGER], [PAYRATE], [SALARY_PLAN], [HOURLY_PLAN], [LAST_MANAGER]) VALUES (N'2', NULL, N'Change Job', CAST(N'2004-01-01' AS Date), N'5-JC', CAST(11 AS Numeric(26, 0)), CAST(1 AS Numeric(26, 0)), N'3', NULL, NULL, NULL, NULL)
GO
INSERT [DB].[Transactional History-test] ([WORKER], [SOURCE], [TAB], [EFFECTIVE_DATE], [ACTION], [SEQUENCE], [EMPRECORD], [MANAGER], [PAYRATE], [SALARY_PLAN], [HOURLY_PLAN], [LAST_MANAGER]) VALUES (N'2', NULL, N'Change Job', CAST(N'2004-01-01' AS Date), N'5-JC', CAST(12 AS Numeric(26, 0)), CAST(1 AS Numeric(26, 0)), N'3', NULL, NULL, NULL, N'3')
GO
INSERT [DB].[Transactional History-test] ([WORKER], [SOURCE], [TAB], [EFFECTIVE_DATE], [ACTION], [SEQUENCE], [EMPRECORD], [MANAGER], [PAYRATE], [SALARY_PLAN], [HOURLY_PLAN], [LAST_MANAGER]) VALUES (N'2', NULL, N'EMP-Position Mgt', CAST(N'2014-01-01' AS Date), N'1-HR', CAST(13 AS Numeric(26, 0)), CAST(2 AS Numeric(26, 0)), N'4', N'Salary', NULL, NULL, NULL)
GO
INSERT [DB].[Transactional History-test] ([WORKER], [SOURCE], [TAB], [EFFECTIVE_DATE], [ACTION], [SEQUENCE], [EMPRECORD], [MANAGER], [PAYRATE], [SALARY_PLAN], [HOURLY_PLAN], [LAST_MANAGER]) VALUES (N'3', NULL, N'EMP-Terminations', CAST(N'2012-01-01' AS Date), N'6-TR', CAST(14 AS Numeric(26, 0)), CAST(1 AS Numeric(26, 0)), NULL, NULL, NULL, NULL, NULL)
GO
INSERT [DB].[Transactional History-test] ([WORKER], [SOURCE], [TAB], [EFFECTIVE_DATE], [ACTION], [SEQUENCE], [EMPRECORD], [MANAGER], [PAYRATE], [SALARY_PLAN], [HOURLY_PLAN], [LAST_MANAGER]) VALUES (N'4', NULL, N'EMP-Position Mgt', CAST(N'2012-01-01' AS Date), N'1-HR', CAST(15 AS Numeric(26, 0)), CAST(1 AS Numeric(26, 0)), NULL, NULL, NULL, NULL, NULL)
GO

select * from DB.[Transactional History-test]
Run Code Online (Sandbox Code Playgroud)

Mar*_*ith 5

这应该以更有效的方式重现光标的逻辑

WITH T
     AS (SELECT *,
                IIF(FIRST_VALUE([ACTION]) OVER (PARTITION BY WORKER 
                                                    ORDER BY [SEQUENCE]
                                    ROWS UNBOUNDED PRECEDING) = '1-HR', 0, 1) + 
                COUNT(CASE
                        WHEN [ACTION] = '1-HR'
                            THEN 1
                        END) OVER (PARTITION BY WORKER 
                                       ORDER BY [SEQUENCE]
                                   ROWS UNBOUNDED PRECEDING) AS _EMPRECORD
         FROM   DB.[Transactional History-test])
UPDATE T
SET    EMPRECORD = _EMPRECORD; 
Run Code Online (Sandbox Code Playgroud)


Ste*_*han 5

我认为你需要的是带有case语句的Windows函数.这是简单,应该执行显著,特别是如果你有良好的指标比你更好的光标.

WITH CTE
AS
(
    SELECT  *,
            CASE    WHEN [action] = '1-HR' OR [Sequence] = MIN([sequence]) OVER (PARTITION BY worker) 
                        THEN 1 --cnter increases by 1 whether the action is 1-HR OR the sequence is the first for that worker
                    ELSE 0 END cnter
    FROM [Transactional History-test]
)

SELECT  empRecord, --can add any columns you want here
        SUM(cnter) OVER (PARTITION BY worker ORDER BY [SEQUENCE]) AS new_EMPRECORD --just a cumalative sum of cnter per worker
FROM CTE
Run Code Online (Sandbox Code Playgroud)

结果(我的匹配你的):

empRecord                               new_EMPRECORD
--------------------------------------- -------------
1                                       1
1                                       1
1                                       1
1                                       1
2                                       2
3                                       3
3                                       3
3                                       3
1                                       1
1                                       1
1                                       1
1                                       1
2                                       2
1                                       1
1                                       1
Run Code Online (Sandbox Code Playgroud)