簡體   English   中英

遞歸 CTE 查詢以創建沒有重復的組

[英]Recursive CTE Query to make groups without duplicates

我需要構建一個 sql 查詢來為歐洲排球聯賽的球隊找到最便宜的球隊(薪水方面)。

對於每支球隊,小隊由 6 名球員組成。 整個團隊有 10 名球員(但可以有更多)。

職位是:

  1. 自由人
  2. 對面的
  3. 二傳手
  4. 中間
  5. 外線擊球手
  6. 防守專家

這是一個顯示 Players、Positions 和 Salaries 的查詢結果。 請注意,有些球員可以打多個位置。 工資以歐元為單位,並除以 1000 以使事情更簡單。

Player    Position  Salary
--------------------------
Player A, Libero, 100
Player A, Defensive Specialist,100
Player B, Opposite, 200
Player C, Middle, 150
Player D, Outside Hitter, 175
Player D, Opposite, 150
Player E, Setter, 100
Player F, Setter, 150
Player G, Middle, 125
Player G, Opposite, 100
Player H, Libero, 75
Player I, Outside Hitter, 150
Player J, Defensive Specialist, 200

由於一些球員可以擔任多個位置,我需要權衡將他們轉移到另一個 position 以減少工資的好處,即使他們在第一個 position 的成本更低。例如,如果一名球員可以擔任自由人和防守專家職位,並且作為自由人,他們比球隊中的其他自由人便宜,作為防守專家,他們甚至可能使球隊的成本低於自由人 position。

我的第一直覺是用所有球員生成所有可能的 6 人小隊(確保同一個球員不在同一個小隊中兩次)——然后按薪水總和排序。 這樣做會找到所有組合,並給我最便宜的陣容。 但是根據團隊的規模,這可能是一項非常密集的任務,所以我想知道是否有更有效的方法。

我可以找到這樣的第一個小隊(盡管這不會檢查不同位置的重復玩家名稱):

WITH Squads AS
(
    SELECT PlayerName, Salary, Position,
        ROW_NUMBER() OVER (PARTITION BY Position ORDER BY Position) AS RowNumber
    FROM Players
)
SELECT * FROM Squads WHERE RowNumber = 1 ORDER BY Position, Salary DESC, PlayerName

我相信我會想在 CTE 中添加 UNION ALL 或 CROSS APPLY 以遍歷所有球員和位置,確保每個小隊在 6 個位置中的每個位置都有一名球員。

最好的方法是什么?

該答案將 position 數據存儲在單獨的表中,然后遞歸迭代每個 position ID, join position 下列出的先前未選擇的玩家加入到結果中。 運行工資以及當前為給定球隊選擇的球員一起存儲:

with recursive cte(pid, position, player, players, salary) as (
   select 2, p.position, p1.player pl1, p1.player, p1.salary 
   from positions p join players p1 on p1.position = p.position where p.id = 1
   union all
   select c.pid + 1, p.position, p1.player, c.players||','||p1.player, c.salary + p1.salary 
   from cte c join positions p on p.id = c.pid join players p1 on p1.position = p.position where not c.players ~ p1.player
),
final_team as (
  select c.players, c.salary from cte c where c.pid = 7
)
select f.* from final_team f where f.salary = (select min(f1.salary) from final_team f1)

見小提琴

接受的答案效果很好。 我忘記提及我使用的 SQL 風格 - SQL Server 2019。答案使用 PostgreSQL。這是 SQL 服務器等效項(也將文本字段更改為 NVARCHAR):

with cte (pid, position, player, players, salary) as (
   select 2, p.position, p1.player pl1, p1.player, p1.salary 
   from positions p join players p1 on p1.position = p.position where p.id = 1
   union all
   select c.pid + 1, p.position, p1.player, CONVERT(nvarchar(200),c.players+','+p1.player), c.salary + p1.salary 
   from cte c join positions p on p.id = c.pid join players p1 on p1.position = p.position where not (p1.player LIKE '%'+c.players+'%')
),
final_team as (
  select c.players, c.salary from cte c where c.pid = 7
)
select f.* from final_team f where f.salary = (select min(f1.salary) from final_team f1)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM