8

場所のリスト内で従業員の一意のランダムな投稿/採用場所を選択しようとしています.すべての従業員はすでにこれらの場所に投稿されています.ランダムな場所は自宅の場所と同じではなく、無作為に選択された指定の従業員は、場所テーブルの場所ごとの指定番号以下でなければなりません」

従業員テーブルは次のとおりです。

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 から) をランダムに割り当てます。与えられた数を超えないこと。

前もって感謝します。

4

2 に答える 2

1

制約は次のとおりであると想定しています。

  • 従業員は、現在いる場所と同じ場所に行くことはできません。
  • すべてのサイトには、従業員が期待される各カテゴリに少なくとも 1 人の従業員が必要です。

最も重要なアイデアは、「ランダムな」割り当てを探しているのではないことを認識することです。全員が別の場所に移動するという条件に従って、ポジションの順列を探しています。

管理者向けの回答を説明します。おそらく、従業員のタイプごとに 3 つのクエリが必要になるでしょう。

重要なアイデアは ManagerPositions テーブルです。これには、場所、連番、および場所内の連番があります。次に例を示します。

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

クエリは、INFORMATION_SCHEMA.columns に row_number() 関数を結合してシーケンスを割り当てることにより、このテーブルを作成します。これは、SQL Server でシーケンスを取得する手っ取り早い方法ですが、必要な最大数 (つまり、任意の 1 つの場所にあるマネージャーの最大数) が、データベース。より一般的なケースを処理する方法は他にもあります。

次の重要なアイデアは、場所をランダムに選択するのではなく、回転させることです。これは、モジュロ算術からのアイデアを使用します。つまり、オフセットを追加し、位置の総数の残りを取ります。最終的なクエリは次のようになります。

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

わかりました、これは複雑です。各マネージャーのポジションのテーブルがあります (ステートメントのようなカウントではありません。各ポジションの行を持つことが重要です)。位置を識別する方法は 2 つあります。1 つ目は、場所ごと、および場所内のカウント (posseqnum) ごとです。2 つ目は、行の増分 ID によるものです。

各マネージャーのテーブルで現在の位置を見つけます。各場所のマネージャーの数を考慮しているため、これは一意である必要があります。次に、位置にオフセットを追加し、その場所を割り当てます。オフセットを maxseqnum よりも大きくすることにより、マネージャーは別の場所に移動することが保証されます (1 つの場所に半分以上のマネージャーがいる異常な境界の場合を除きます)。

現在のマネージャーのポジションがすべて埋まっている場合、これにより全員が次の場所に移動することが保証されます。ManagerPositions は seqnum の割り当てにランダム ID を使用するため、「次の」場所はランダムであり、ID またはアルファベット順ではありません。

このソリューションでは、多くの従業員が同じ新しい場所に一緒に移動します。式で「1」以外の値を試すことで、これをある程度修正できます(m.seqnum+maxposseqnum+1)

現在の場所と次の場所の相関を防ぐために、これを変更する方法があることに気付きました。これにより、次のことが行われます。

  1. seqnum を ManagerPosition にランダムに割り当てます
  2. テーブル内の異なるオフセットを比較し、そのオフセットで区切られたテーブル内の 2 つの位置が同じである回数でそれぞれを評価します。
  3. 最小定格 (できれば 0) のオフセットを選択します。
  4. 最後の一致句でそのオフセットを使用します。

このための SQL を記述する十分な時間がありません。

于 2012-09-22T16:06:51.520 に答える
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 とホームを破棄する指定に基づいて従業員をランダムに一致させようとし、指定の各列で指定されている以上のものを割り当てないようにする必要があります。ただし、その基準に基づいて複数の場所に一致する可能性があるため、複数の場所で同じ従業員が返される可能性があります。


編集: この問題を解決するために高性能の単一クエリが必要ないというコメントを見た後(これが可能かどうかさえわかりません)、それは「1回限りの」プロセスのように思われるためです。が呼び出されます。割り当ての問題を解決するために、カーソルと 1 つの一時テーブルを使用して次のコードを作成しました。

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 に答える