【问题标题】:Counting values from multiple columns in SQL计算 SQL 中多列的值
【发布时间】:2015-05-30 21:55:06
【问题描述】:

对于体育比赛,人们要么与另一队 2 名球员成对比赛,我想输出一个特定球员面对的每个对手的列表,以及比赛中的赢、平和输数特定玩家和那个对手之间的关系。

例如,如果结果是:

  1. 玩家 1 和玩家 2    2-1    玩家 3 和玩家 4
  2. 球员 3 和球员 1 1-0 球员 2 和球员 4
  3. 玩家 2 和玩家 4    4-2    玩家 1 和玩家 3
  4. 球员 1 和球员 4 0-0 球员 3 和球员 2

我希望玩家 1 的输出为:

  • 玩家 2,W1 D1 L1
  • 玩家 3,W1 D1 L0
  • 玩家 4,W2 D0 L1

事实证明,这很难实现。 我的数据被组织成两张表,一张是所有玩家的表(Players),一张是分数表(Scores)。

Players 有字段 PlayerID 和 PlayerName。

Scores 具有 Player1、Player2、Player3、Player4 和 Score 字段,其中每个 Player 字段都可以包含任何 PlayerID(因此任何给定的 ID 可能会根据行出现在不同的列中),并且会存储 Score,以便如果小于 21,玩家 1 和 2 输,如果大于 21,他们赢。

这个查询有什么问题?显然它返回 0。

SELECT opponent as opp, COUNT(result='win') as c1, COUNT(result='draw') as c2, COUNT(result='loss') as c3
      FROM (SELECT
            CASE
                WHEN '$player' IN(Players1.PlayerName, Players2.PlayerName) THEN Players3.PlayerName
                WHEN '$player' IN(Players3.PlayerName, Players4.PlayerName) THEN Players1.PlayerName
                ELSE 'NA' 
                END as opponent,
            CASE
                WHEN '$player' IN(Players1.PlayerName, Players2.PlayerName) 
                THEN
                    CASE
                        WHEN Scores.Score > 21 THEN 'win'
                        ELSE 'NA'
                        END as result,
                    CASE
                        WHEN Scores.Score < 21 THEN 'loss'
                        ELSE 'NA'
                        END as result,
                WHEN '$player' IN(Players3.PlayerName, Players4.PlayerName) 
                THEN
                    CASE
                        WHEN Scores.Score < 21 THEN 'win'
                        ELSE 'NA'
                        END as result,
                    CASE
                        WHEN Scores.Score > 21 THEN 'loss'
                        ELSE 'NA'
                        END as result,
                WHEN Scores.Score = 21 THEN 'draw'
                ELSE 'NA' 
                END as result,
        FROM Scores
        INNER JOIN Players Players1 ON Scores.Player1=Players1.PlayerID
        INNER JOIN Players Players2 ON Scores.Player2=Players2.PlayerID
        INNER JOIN Players Players3 ON Scores.Player3=Players3.PlayerID
        INNER JOIN Players Players4 ON Scores.Player4=Players4.PlayerID
        WHERE Players1.PlayerName = '$player' OR Players2.PlayerName = '$player' OR Players3.PlayerName = '$player' OR Players4.PlayerName = '$player'
        UNION ALL
        SELECT
            CASE
                WHEN '$player' IN(Players1.PlayerName, Players2.PlayerName) THEN Players4.PlayerName
                WHEN '$player' IN(Players3.PlayerName, Players4.PlayerName) THEN Players2.PlayerName
                ELSE 'NA' 
                END as opponent,
            CASE
                WHEN '$player' IN(Players1.PlayerName, Players2.PlayerName) AND Scores.Score > 21 THEN 'win'
                WHEN '$player' IN(Players1.PlayerName, Players2.PlayerName) AND Scores.Score < 21 THEN 'loss'
                WHEN '$player' IN(Players3.PlayerName, Players4.PlayerName) AND Scores.Score < 21 THEN 'win'
                WHEN '$player' IN(Players3.PlayerName, Players4.PlayerName) AND Scores.Score > 21 THEN 'loss'
                WHEN Scores.Score = 21 THEN 'draw'
                ELSE 'NA' 
                END as result,
        FROM Scores
        INNER JOIN Players Players1 ON Scores.Player1=Players1.PlayerID
        INNER JOIN Players Players2 ON Scores.Player2=Players2.PlayerID
        INNER JOIN Players Players3 ON Scores.Player3=Players3.PlayerID
        INNER JOIN Players Players4 ON Scores.Player4=Players4.PlayerID 
        WHERE Players1.PlayerName = '$player' OR Players2.PlayerName = '$player' OR Players3.PlayerName = '$player' OR Players4.PlayerName = '$player'
    ) AS SUBQUERY
    GROUP BY opp

【问题讨论】:

  • 是否有可能重构您的数据库架构?
  • 不幸的是,数据库不是我的,还有许多其他历史查询依赖于它保留其当前模式。我可能可以在其他地方创建一个具有不同布局的副本,但这似乎是不必要的浪费,特别是考虑到数据库的大小。

标签: mysql sql


【解决方案1】:

它并不漂亮,但由于 MySQL 缺少 Pivot 函数,因此您可以使用内部查询来分析结果,该查询分解了谁玩了谁以及结果是什么。然后外部查询可以聚合这些结果。像下面这样的东西对我有用,虽然,再次,不漂亮

select players.name as playerName, 
  opponents.name as opponentName, 
  SUM(CASE WHEN result = 'W'THEN 1 ELSE 0 END) AS win, 
  SUM(CASE WHEN result = 'L'THEN 1 ELSE 0 END) AS lose, 
  SUM(CASE WHEN result = 'D' THEN 1 ELSE 0 END) AS draw 
from  (
    select player1 as player, player3 as opponent, score, CASE WHEN score > 21 THEN 'W' WHEN score < 21 THEN 'L' ELSE 'D' END as result
    from scores
    union all
    select player1 as player, player4 as opponent, score, CASE WHEN score > 21 THEN 'W' WHEN score < 21 THEN 'L' ELSE 'D' END as result
    from scores
    union all
    select player2 as player, player3 as opponent, score, CASE WHEN score > 21 THEN 'W' WHEN score < 21 THEN 'L' ELSE 'D' END as result
    from scores
    union all
    select player2 as player, player4 as opponent, score, CASE WHEN score > 21 THEN 'W' WHEN score < 21 THEN 'L' ELSE 'D' END as result
    from scores
    -- now reverse result because opponents are being slotted into the player position
    union all
    select player3 as player, player1 as opponent, score, CASE WHEN score > 21 THEN 'L' WHEN score < 21 THEN 'W' ELSE 'D' END as result
    from scores
    union all
    select player4 as player, player1 as opponent, score, CASE WHEN score > 21 THEN 'L' WHEN score < 21 THEN 'W' ELSE 'D' END as result
    from scores
    union all
    select player3 as player, player2 as opponent, score, CASE WHEN score > 21 THEN 'L' WHEN score < 21 THEN 'W' ELSE 'D' END as result
    from scores
    union all
    select player4 as player, player2 as opponent, score, CASE WHEN score > 21 THEN 'L' WHEN score < 21 THEN 'W' ELSE 'D' END as result
    from scores
) resultsByPlayer
inner join players on players.playerId = resultsByPlayer.player
inner join players as opponents on opponents.playerId = resultsByPlayer.opponent
group by players.playerId, opponents.playerId

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-29
    • 2013-09-13
    • 1970-01-01
    • 2022-01-09
    相关资源
    最近更新 更多