从不同的行中提取排名值 - SQL

时间:2016-08-08 20:52:21

标签: sql-server join

我有一个包含各种类别的数据库。对于每个类别,我有三个数量,我想从每个类别的每个数量中提取包含第25个最大值的行(可以安全地忽略关系)。

例如,我可能有一个数据库,其行是来自多个国家之一的城镇或城市。类别是国家,数量可能是人口,土地面积和纬度。然后数据看起来像:

TownName    Country    Population    LandArea    Latitude
Paris       France     500,715       47.9        45.76
Manchester  USA        110,229       90.6        42.99
Calais      France     72,589        33.5        50.95
Leicester   England    337,653       73.3        52.63
Dunkirk     France     90,995        43.9        51.04
...         ...        ...           ...         ...

在这个例子中,我想要的最终结果将是列表中的每个国家,以及他们的第25大人口,第25大土地面积和第25大纬度。这不再像某些特定的城镇或城市,而是提供有关每个国家的一些信息。这可能看起来像:

Country    Population    LandArea    Latitude
France     144,548       83.95       50.21
Poland     141,080       88.3        54.17
Australia  68,572        146         -21.35
...        ...           ...         ...

我找到了一种方法来做到这一点,即做以下事情:

  1. 使用ROW_NUMBER功能按降序排列Population,LandArea和Latitude之一,按国家/地区划分。

  2. 重复三次(每个数量一次),并将三个数据库JOIN放在一起。在ON语句中,确保Country列的值相等,以及rank列的值。

  3. 使用WHERE语句为每个排名为25的国家/地区提取行。

  4. 我不喜欢这种方法,因为它涉及创建三个几乎完全相同大小的代码块的副本,以获得我加入的三个独立数据库(连接语句中的每个代码块都是一个不错的大小,因为这是一个简化的例子,我不得不做其他事情来达到这样的阶段。)

    我想知道是否有一种方法不会让我用JOIN语句重复大块代码,因为这会使我的代码变得又大又丑。此外,这似乎可能会一次又一次地出现,所以一种更有效的方法将是美妙的。

    感谢您的时间

1 个答案:

答案 0 :(得分:1)

也许如果你找不到消除3连接方法的方法,你可以通过为每个不同的元组分配GroupID来简化连接条件:

;WITH
    MasterCTE AS
    (
        SELECT      *,
                    DENSE_RANK() OVER (ORDER BY Country) AS GroupID -- Don't use ROW_NUMBER here. RANK or DEMSE_RANK only
        FROM        MyTable
    ),
    cte1 AS
    (
        SELECT      GroupID, [Population],
                    ROW_NUMBER() OVER (PARTITION BY GroupID ORDER BY [Population] DESC) AS PopulationRank
        FROM        MasterCTE
    ),
    cte2 AS
    (
        SELECT      GroupID, LandArea,
                    ROW_NUMBER() OVER (PARTITION BY GroupID ORDER BY LandArea DESC) AS LandAreaRank
        FROM        MasterCTE
    ),
    cte3 AS
    (
        SELECT      GroupID, Latitude,
                    ROW_NUMBER() OVER (PARTITION BY GroupID ORDER BY Latitude DESC) AS LatitudeRank
        FROM        MasterCTE
    )


SELECT DISTINCT  -- Remember to include DISTINCT
            MasterCTE.Country,
            cte1.Population, cte2.LandArea, cte3.Latitude
FROM        MasterCTE   
INNER JOIN  cte1        ON MasterCTE.GroupID = cte1.GroupID AND cte1.PopulationRank = 25
INNER JOIN  cte2        ON MasterCTE.GroupID = cte2.GroupID AND cte2.LandAreaRank   = 25
INNER JOIN  cte3        ON MasterCTE.GroupID = cte3.GroupID AND cte3.LatitudeRank   = 25