8

我正在尝试在地点列表中选择员工的唯一随机发布/招聘地点,所有员工都已在这些地点发布,我正在尝试为他们生成一个新的随机发布地点,其中“位置”条件为“员工新随机位置将不等于他们的家乡,并且随机选择的员工及其指定必须小于或等于 Places 表中的 Place wise 指定编号“

员工表是:

EmpNo   EmpName           CurrentPosting    Home        Designation   RandomPosting
1       Mac               Alabama           Missouri      Manager       
2       Peter             California        Montana       Manager       
3       Prasad            Delaware          Nebraska      PO       
4       Kumar             Indiana           Nevada        PO       
5       Roy               Iowa              New Jersey    Clerk       

等等...

并且 Places 表(具有员工数量的 PlaceNames - 指定明智)是: -

PlaceID  PlaceName      Manager     PO    Clerk
1        Alabama           2        0     1
2        Alaska            1        1     1
3        Arizona           1        0     2
4        Arkansas          2        1     1
5        California        1        1     1
6        Colorado          1        1     2
7        Connecticut       0        2     0

等等...

尝试使用 newid() 如下所示,并且能够选择具有 RandomPosting 地名的员工,

WITH cteCrossJoin AS (
SELECT e.*, p.PlaceName AS RandomPosting,
       ROW_NUMBER() OVER(PARTITION BY e.EmpNo ORDER BY NEWID()) AS RowNum
    FROM Employee e
        CROSS JOIN  Place p
    WHERE e.Home <> p.PlaceName
)
SELECT *
FROM cteCrossJoin
WHERE RowNum = 1;

另外,我需要根据指定编号(在 Places 表中)限制随机选择...即为每个员工随机分配一个 PlaceName(来自 Places),这不等于 CurrentPosting 和 Home(在 Employee 中)和 Place wise 指定将不超过给定的数字。

提前致谢。

4

2 回答 2

1

也许是这样的:

select C.* from 
(
    select *, ROW_NUMBER() OVER(PARTITION BY P.PlaceID, E.Designation ORDER BY NEWID()) AS RandPosition
        from Place as P cross join Employee E
    where P.PlaceName != E.Home AND P.PlaceName != E.CurrentPosting
) as C
where 
    (C.Designation = 'Manager' AND C.RandPosition <= C.Manager) OR
    (C.Designation = 'PO' AND C.RandPosition <= C.PO) OR
    (C.Designation = 'Clerk' AND C.RandPosition <= C.Clerk)

这应该尝试根据员工的指定随机匹配员工,丢弃相同的 currentPosting 和 home,并且不要分配超过每列指定的指定内容。但是,这可能会为多个地方返回同一员工,因为根据该标准,它们可以匹配多个。


编辑: 在看到您关于不需要高性能单个查询来解决此问题的评论后(我什至不确定这是否可能),并且因为它似乎更像是您的“一次性”过程将调用,我使用游标和一个临时表编写了以下代码来解决您的分配问题:

select *, null NewPlaceID into #Employee from Employee

declare @empNo int
DECLARE emp_cursor CURSOR FOR  
SELECT EmpNo from Employee order by newid()

OPEN emp_cursor   
FETCH NEXT FROM emp_cursor INTO @empNo

WHILE @@FETCH_STATUS = 0   
BEGIN
    update #Employee 
    set NewPlaceID = 
        (
        select top 1 p.PlaceID from Place p 
        where 
            p.PlaceName != #Employee.Home AND 
            p.PlaceName != #Employee.CurrentPosting AND
            (
                CASE #Employee.Designation 
                WHEN 'Manager' THEN p.Manager
                WHEN 'PO' THEN p.PO
                WHEN 'Clerk' THEN p.Clerk
                END
            ) > (select count(*) from #Employee e2 where e2.NewPlaceID = p.PlaceID AND e2.Designation = #Employee.Designation)
        order by newid()
        ) 
    where #Employee.EmpNo = @empNo
    FETCH NEXT FROM emp_cursor INTO @empNo   
END

CLOSE emp_cursor
DEALLOCATE emp_cursor

select e.*, p.PlaceName as RandomPosting from Employee e
inner join #Employee e2 on (e.EmpNo = e2.EmpNo)
inner join Place p on (e2.NewPlaceID = p.PlaceID)

drop table #Employee

基本思想是,它以随机顺序对员工进行迭代,并为每个员工分配一个符合不同家庭和当前职位标准的随机地点,并控制为每个指定分配给每个地点的数量以确保不会为每个角色“过度分配”位置。

这个片段实际上并没有改变你的数据。最后的SELECT语句只返回建议的分配。Employee但是,您可以很容易地更改它以相应地对您的表进行实际更改。

于 2012-09-22T15:30:19.247 回答
1

我假设约束是:

  • 员工不能去他/她目前所在的同一地点。
  • 所有站点必须在每个类别中至少有一名员工,其中需要一名员工。

最重要的想法是意识到您不是在寻找“随机”分配。您正在寻找位置的排列,但前提是每个人都移动到其他地方。

我将为管理者描述一个答案。您可能需要对每种类型的员工进行三个查询。

关键思想是一个 ManagerPositions 表。这有一个地点、一个序号和一个地点内的序号。下面是一个例子:

Araria     1    1
Araria     2    2
Arwal      1    3
Arungabad  1    4

该查询通过使用 row_number() 函数连接到 INFORMATION_SCHEMA.columns 以分配序列来创建此表。这是在 SQL Server 中获取序列的一种快速而肮脏的方法——但只要您需要的最大数量(即任何一个位置的最大管理器数量)小于数据库。还有其他方法可以处理更一般的情况。

下一个关键思想是旋转位置,而不是随机选择它们。这使用了模算术的思想——添加一个偏移量并在总位置数上取余数。最终查询如下所示:

with ManagerPositions as (
     select p.*,
            row_number() over (order by placerand, posseqnum) as seqnum,
            nums.posseqnum
     from (select p.*, newid() as placerand
           from places p
          ) p join
          (select row_number() over (order by (select NULL)) as posseqnum
           from INFORMATION_SCHEMA.COLUMNS c
          ) nums
          on p.Manager <= nums.posseqnum
    ),
   managers as (
    select e.*, mp.seqnum
    from (select e.*,
                 row_number() over (partition by currentposting order by newid()
                                   ) as posseqnum
          from Employees e              
          where e.Designation = 'Manager'
         ) e join
         ManagerPositions mp
         on e.CurrentPosting = mp.PlaceName and
            e.posseqnum = mp.posseqnum
  )
select m.*, mp.PlaceId, mp.PlaceName
from managers m cross join
     (select max(seqnum) as maxseqnum, max(posseqnum) as maxposseqnum
      from managerPositions mp
     ) const join
     managerPositions mp
     on (m.seqnum+maxposseqnum+1) % maxseqnum + 1 = mp.seqnum

好的,我意识到这很复杂。每个经理职位都有一个表格(不像您的声明中那样计数,每个职位都有一行很重要)。有两种方法可以确定位置。第一个是按位置和按位置内的计数(posseqnum)。第二个是行上的增量 id。

找到每个经理在表中的当前位置。这应该是唯一的,因为我考虑到了每个地方的经理人数。然后,为该位置添加一个偏移量,并分配该位置。通过使偏移量大于 maxseqnum,可以保证管理器移动到另一个位置(除非在一个位置拥有超过一半管理器的异常边界情况下)。

如果所有当前的经理职位都被填补,那么这保证了所有人都将搬到下一个位置。因为 ManagerPositions 使用随机 id 来分配 seqnum,所以“下一个”位置是随机的,而不是按 id 或字母顺序排列的下一个位置。

该解决方案确实让许多员工一起前往同一个新地点。您可以通过在表达式中尝试“1”以外的值来解决这个问题(m.seqnum+maxposseqnum+1)

我意识到有一种方法可以修改它,以防止当前位置和下一个位置之间的关联。这将执行以下操作:

  1. 将 seqnum 随机分配给 ManagerPosition
  2. 比较表中的不同偏移量,根据表中由该偏移量分隔的两个位置相同的次数对每个位置进行评级。
  3. 选择具有最小额定值的偏移量(最好是 0)。
  4. Use that offset in the final matching clause.

I don't have enough time right now to write the SQL for this.

于 2012-09-22T16:06:51.520 回答