每个比赛距离的男性和女性 SQLite 最佳三倍

SQLite best three times for men and women for each race distance

我从这个简单的查询开始,它为我提供了在 125 公里比赛中取得最佳成绩(第一、第二和第三)的三个人。

SELECT *
FROM Coureurs
WHERE Genre=’M’ AND Epreuve='125km' AND TempsPassage IS NOT NULL
ORDER BY TempsPassage
LIMIT 3;

但是,我还需要在该距离上表现最好的三位女性(类型=F)。 以及其他每个距离的最佳三名男子和三名女子(TempsPassage=80km/65km/40km,..)。

这远远超出了我的水平...我真的想避免构建单独的“硬编码”查询。 提前致谢, 皮埃尔

进行以下assumptions/changes

  • Epreuve 列是数字(存储 125、80 .... 而不是 125km、80km ....)

    • 125km 和 80km 如果排序的话,80km 大于 125km
    • 文本值会花费一些额外的存储空间
    • 如果需要,可以很容易地提取附加 km 的值,例如SELECT genre, Epreuve||'km', TempsPassage, Name FROM Coureurs
  • TempsPassage 列存储时间(示例使用秒但参见 Time Values

假设以下数据:-

还假设您想要一个相对简单易懂的单个查询,那么以下内容可能适合:-

WITH
     m125 AS (SELECT * FROM Coureurs WHERE genre = 'M' AND Epreuve = 125 ORDER BY TempsPassage ASC LIMIT 3),
     f125 AS (SELECT * FROM Coureurs WHERE genre = 'F' AND Epreuve = 125 ORDER BY TempsPassage ASC LIMIT 3),
     m80 AS (SELECT * FROM Coureurs WHERE genre = 'M' AND Epreuve = 80 ORDER BY TempsPassage ASC LIMIT 3),
     f80 AS (SELECT * FROM Coureurs WHERE genre = 'F' AND Epreuve = 80 ORDER BY TempsPassage ASC LIMIT 3),
     m65 AS (SELECT * FROM Coureurs WHERE genre = 'M' AND Epreuve = 65 ORDER BY TempsPassage ASC LIMIT 3),
     f65 AS (SELECT * FROM Coureurs WHERE genre = 'F' AND Epreuve = 65 ORDER BY TempsPassage ASC LIMIT 3),
     m40 AS (SELECT * FROM Coureurs WHERE genre = 'M' AND Epreuve = 40 ORDER BY TempsPassage ASC LIMIT 3),
     f40 AS (SELECT * FROM Coureurs WHERE genre = 'F' AND Epreuve = 40 ORDER BY TempsPassage ASC LIMIT 3)
SELECT * FROM m125 
    UNION SELECT * FROM f125
    UNION SELECT * FROM m80
    UNION SELECT * FROM f80
    UNION SELECT * FROM m65
    UNION SELECT * FROM f65
    UNION SELECT * FROM m40
    UNION SELECT * FROM f40
    ORDER BY Epreuve DESC,Genre DESC, TempsPassage ASC
;

这利用了所谓的 CTE(通用 Table 表达式),它们基本上是临时表。

每个 Genre 和 Epreuve 的排列使用一个 CTE (2 * 4 = 8),它是用类似的查询构建的。

创建所有 CTE 后,使用 UNION 组合 8 个单独的临时表。

使用结果上方的数据是(显然您可能希望对结果进行不同的排序):-

下面是SQL用来测试上面的内容并创建结果:-

DROP TABLE IF EXISTS Coureurs;
CREATE TABLE IF NOT EXISTS Coureurs (Genre TEXT, Epreuve int, TempsPassage int, name TEXT);
INSERT INTO Coureurs VALUES
    ('M',125,600,'Fred'),('M',125,610,'Bert'),('M',125,630,'Harry'),('M',125,620,'Albert'),('M',125,575,'David')
    
    ,('F',125,615,'Mary'),('F',125,625,'Anne'),('F',125,601,'Betty'),('F',125,625,'Sue'),('F',125,670,'Shelia')
    
    ,('F',80,450,'Louise'),('F',80,460,'Celia'),('F',80,425,'Debra'),('F',80,475,'Diana')
    
    ,('F',65,350,'Zara'),('F',65,360,'Yvonne'),('F',65,325,'Wilma'),('F',65,375,'Ursurla')
    
    ,('F',40,250,'Tracy'),('F',40,260,'Rhona'),('F',40,225,'Samantha'),('F',40,275,'Karen')
    
    ,('M',80,450,'Lou'),('M',80,460,'Colin'),('M',80,425,'Danny'),('M',80,475,'Eddy')
    
    ,('M',65,350,'Zed'),('M',65,360,'Mark'),('M',65,325,'William'),('M',65,375,'Tom')
    
    ,('M',40,250,'Jim'),('M',40,260,'Larry'),('M',40,225,'Peter'),('M',40,275,'Ronald')
;

SELECT * FROM Coureurs ORDER BY Random();
/* Easiest to understand - combining individual queries as CTE
    CTE = Common Table Expression (equates to temporary table) 
*/
WITH
     m125 AS (SELECT * FROM Coureurs WHERE genre = 'M' AND Epreuve = 125 ORDER BY TempsPassage ASC LIMIT 3),
     f125 AS (SELECT * FROM Coureurs WHERE genre = 'F' AND Epreuve = 125 ORDER BY TempsPassage ASC LIMIT 3),
     m80 AS (SELECT * FROM Coureurs WHERE genre = 'M' AND Epreuve = 80 ORDER BY TempsPassage ASC LIMIT 3),
     f80 AS (SELECT * FROM Coureurs WHERE genre = 'F' AND Epreuve = 80 ORDER BY TempsPassage ASC LIMIT 3),
     m65 AS (SELECT * FROM Coureurs WHERE genre = 'M' AND Epreuve = 65 ORDER BY TempsPassage ASC LIMIT 3),
     f65 AS (SELECT * FROM Coureurs WHERE genre = 'F' AND Epreuve = 65 ORDER BY TempsPassage ASC LIMIT 3),
     m40 AS (SELECT * FROM Coureurs WHERE genre = 'M' AND Epreuve = 40 ORDER BY TempsPassage ASC LIMIT 3),
     f40 AS (SELECT * FROM Coureurs WHERE genre = 'F' AND Epreuve = 40 ORDER BY TempsPassage ASC LIMIT 3)
SELECT * FROM m125 
    UNION SELECT * FROM f125
    UNION SELECT * FROM m80
    UNION SELECT * FROM f80
    UNION SELECT * FROM m65
    UNION SELECT * FROM f65
    UNION SELECT * FROM m40
    UNION SELECT * FROM f40
    ORDER BY Epreuve DESC,Genre DESC, TempsPassage ASC
;

DROP TABLE IF EXISTS Coureurs;

您可以使用 window 函数 ROW_NUMBER() 对每个 race/genre 的结果进行排名,然后过滤到 return 只有排名 1-3:

SELECT *
FROM (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY Epreuve, Genre ORDER BY TempsPassage) rn
  FROM Coureurs
  WHERE TempsPassage IS NOT NULL
)
WHERE rn <= 3
ORDER BY (Epreuve + 0), Genre, rn

如果出现平局,则可以尝试 RANK() window 函数而不是 ROW_NUMBER()