如何连接两个表来替换TSQL循环?

时间:2013-08-19 10:56:42

标签: sql-server tsql

我需要以特殊方式加入两个表。目前,连接是通过游标和循环完成的,我正在寻找一种更有效的方法来处理数据。

第一个表是待办事项列表:

create table todo
( todo_id int not null identity(1,1) primary key,
  cnt int not null,      -- how many units of work have to be completed?
  work char(1) not null  -- type of work, for example 'x', 'y', 'z'
);

例如,该表包含以下值

insert into todo (cnt, work) values (1, 'x'), (3, 'y'), (2, 'u'), (3, 'v'), (1, 'w');

这意味着工作'x'必须完成一次,工作'y'三次,依此类推。此todo表中的工作必须由Jobs表中的第一个“可用”Job_id的同一个worker完成。 todo表中的每个条目都会导致Jobs表中的“cnt”条目。

create table jobs
(job_id int not null identity(1,1) primary key,
 worker char(1) not null, -- name of worker, for example 'A', 'B', 'C'
 work char(1)
);

最初使用以下值填充Jobs表:

insert into jobs (worker) values 
('A'),('B'),('C'),('A'),('C'),('B'),('A'),('B'),('C'),('A'),('B'),('C');

按照todo表中每行的todo_id的顺序,我执行以下操作:

  1. 选择cnt和工作
  2. 按Job_id的顺序我从Jobs表中搜索第一个可用的工作人员
  3. 使用工作人员的工作
  4. 更新Jobs表cnt次数

    我目前使用以下T-SQL代码来完成此任务:

    DECLARE @Cnt int, @work char(1);
    DECLARE @Worker char(1);
    DECLARE myCursor CURSOR LOCAL FAST_FORWARD FOR
      SELECT cnt, work from todo
    OPEN myCursor
    FETCH NEXT FROM myCursor INTO @cnt, @work
    WHILE @@FETCH_STATUS = 0 BEGIN
      select top 1 @Worker=Worker from jobs where work is null;
      update top(@cnt) j
      set work=@work
      from jobs j
      where j.work is null and j.worker=@worker;
      FETCH NEXT FROM myCursor INTO @cnt, @work
    END
    CLOSE myCursor
    DEALLOCATE myCursor
    

    todo table:

    todo Table

    结果作业表:

    resulting jobs table

    我正在寻找一个有效的查询或更新语句来取代上面的循环,我现在想不出一种方法来正确地替换上面的循环行为。

1 个答案:

答案 0 :(得分:0)

我在while循环中重写了光标,它可能表现得更好:

-->Variable Declarations
declare @minIterator int ,@maxIterator int,@outerIterator int, @outerLoopCounter int=0, @innerIterator int, @innerLoopCounter int=1, @worker char(1);
-->Max todo id
select @maxIterator = max(todo_id) from todo;
-->Min todo_id
select @minIterator = min(todo_id) from todo;
-->Number of times outer loop needs to execute
select @outerIterator = @maxIterator-@minIterator;


 -->Outer Loop for todo items
  while @OuterLoopCounter<=@outerIterator
   begin
    -->Set number of time innerLoop iterates
    select @innerIterator = cnt from todo where todo_id=@minIterator;
    -->Set worker to do the job 
    select   @worker = worker from jobs where job_id=(select min(job_id) from jobs where work is null)
  -->Inner Loop for count of each todo item
    while @innerLoopCounter<=@innerIterator
      begin
        update j
            set j.work=t.work
        from 
            jobs j,
            todo t 
        where 
            t.todo_id=@minIterator
            and j.job_id=(select min(x.job_id) from jobs x where x.work is null and x.worker=@worker);
        set @innerLoopCounter=@innerLoopCounter+1;
      end
   set @innerLoopCounter=1;
   set @outerLoopCounter=@outerLoopCounter+1;
   set @minIterator = @minIterator+1;
  end


-->See Results
select * from jobs