【问题标题】:Groupwise MAX() on a subquery子查询上的 Groupwise MAX()
【发布时间】:2013-02-12 20:48:31
【问题描述】:

我正在尝试计算返回表中的最大值以及该表中的其他值。但是,我正在为其执行此操作的表不是“真实”表,它是由子查询生成的表。这给我带来了问题,因为我不认为我可以加入它两次,而不重新指定整个子查询。

我目前有一个使用ROW_NUMBER() OVER (PARTITION BY providerId ORDER BY partnershipSetScore DESC) rnk 的SQL Server 解决方案,但如果可能的话,我正在寻找一个与DBMS 无关的版本,因为项目的单元测试在没有此功能的Sqlite DB 中运行。

这是架构和我的 SQL Server 特定查询,以防它们有用:

课程:

  • int id
  • varchar 名称
  • int schoolId

学校:

  • int id
  • varchar 名称

合作伙伴关系:

  • int id
  • varchar 伙伴关系名称

学校合作伙伴关系:

  • int id
  • int schoolId
  • int合作伙伴ID

这是查询:

SELECT
    schoolId,
    partnershipId AS bestPartnershipSetId,
    partnershipScore AS bestPartnershipScore
FROM
(
    SELECT
        pp.schoolId,
        partnershipScores.partnershipId,
        partnershipScores.partnershipScore,
        ROW_NUMBER() OVER (PARTITION BY schoolId ORDER BY partnershipScore DESC) rnk
    FROM schoolPartnership pp
    INNER JOIN (
        SELECT
            pp.partnershipId,
            (
                (CASE WHEN SUM(CASE WHEN c.name LIKE '%French%' THEN 1 ELSE 0 END) > 0 THEN 1 ELSE 0 END)
                + (CASE WHEN SUM(CASE WHEN c.name LIKE '%History%' THEN 1 ELSE 0 END) > 0 THEN 1 ELSE 0 END)
            ) AS partnershipScore
        FROM schoolPartnership pp
        INNER JOIN course c ON c.schoolId = pp.schoolId
        GROUP BY partnershipId
    ) AS partnershipScores ON partnershipScores.partnershipId = pp.partnershipId
) AS schoolPartnershipScores
WHERE rnk = 1

如果您需要有关我想要实现的目标的更多信息,请参阅Custom sorting algorithm for a large amount of data:此查询将是一个更大查询的子查询,该查询按最合适的合作伙伴对学校进行排序。

【问题讨论】:

    标签: sql sql-server sqlite


    【解决方案1】:

    也许,在谈到两次加入子查询时,您的脑海中已经有了这种技巧:

    SELECT a.*
    FROM atable a
    INNER JOIN (
      SELECT
        col1,
        MAX(col2) AS max_col2
      FROM atable
      GROUP BY col1
    ) m
    ON a.col1 = m.col1 AND a.col2 = m.max_col2
    ;
    

    如果将其用作与 DBMS 无关的方式(至少,在 SQL Server 和 SQLite 中都可以使用)来完成这项工作,那将是非常好的如果这是关于单个表的.

    相反,您有一个子查询。但是,我看不到其他方法可以完成您的要求。因此,在这种情况下,我可以为您看到两个选项(一个可能不适用于您的特定情况,但通常仍然是一个选项):

    1. 做你试图避免的事情,即复制子查询专门用于查找每个组的聚合值,然后将其加入到同一个子查询中,就像上面一样。

    2. 暂时保留子查询的结果,然后将上述技术应用于临时结果集。

    第一个选项确实不是很有吸引力,更不用说因为第二个可能会起作用。

    第二个选项的一个问题是临时数据集在 SQL Server 和 SQLite 中的实现方式不同。在 SQLite 中,您为此使用 CREATE TEMPORARY TABLE 语句。 SQL Server 在CREATE TABLE 语句的上下文中不支持TEMPORARY 关键字,而是在表名的开头使用一个特殊字符(#) 来表示该表实际上是一个临时表。

    所以,我能看到的唯一解决方法是使用 normal 表作为临时存储。您可以在每次运行查询时创建一次,然后在插入临时结果集之前删除其内容:

    DELETE FROM TempTable;
    INSERT INTO TempTable (
      schoolId,
      bestPartnershipSetId,
      bestPartnershipScore
    )
    SELECT
      pp.schoolId,
      partnershipScores.partnershipId,
      partnershipScores.partnershipScore,
    FROM
      ...
    ;
    SELECT ...
    FROM TempTable
    ...
    ;
    

    或者您可以在每次运行查询时创建并删除它:

    CREATE TABLE TempTable (
      ...
    );
    INSERT INTO TempTable (...)
    SELECT ...
    FROM ...
    ;
    SELECT ...
    FROM TempTable
    ...
    ;
    DROP TABLE TempTable;
    

    请注意,像这样使用普通表作为临时存储在 SQL Server 中对并发不友好。如果这可能会造成问题,您可能不得不放弃此选项并最终选择第一个选项。 (但这可能是您想要一个独立于平台的解决方案时必须支付的成本,尤其是当平台与 SQL Server 和 SQLite 一样不同时。)

    【讨论】:

    • 非常感谢您的详细回复。由于并发问题,我认为我无法使用正常/临时表路由,所以我认为我(不幸的是)可能不得不复制子查询。完成后我会更新这个问题。
    【解决方案2】:

    这是你想要的结构:

    with t as (<subquery goes here>)
    select t.*,
           max(col) over () as MaxVal
    from t
    

    很难看出它如何适合您的查询,因为我不知道基本子查询是什么。

    至于多次加入子查询,您可以使用 SQL Server 所称的“通用表表达式”——上面的 with 子句。大多数其他合理的数据库都支持这一点(MySQL 和 MS Access 开始有两个值得注意的例外)。

    【讨论】:

    • 谢谢,这将是理想的,但不幸的是 Sqlite 不支持 CTE。
    • @ChrisC 。 . .当我回答这个问题时,它没有被标记为 SQLite。为什么要标记两个数据库?
    • 已被某人编辑。编辑很好,原始问题解释了原因:我目前有一个 SQL Server 特定查询,我需要对其进行隐蔽,以便它在 SQL Server 和 SQLite 中都有效。
    【解决方案3】:

    与 SQL 最无关的方式是使用“NON EXISTS”:

    SELECT * FROM schoolPartnership t1
    WHERE NOT EXISTS 
           (SELECT * FROM schoolPartnership t2 
            WHERE t1.schoolId = t2.schoolId 
                  AND t1.partnershipScore < t2.partnershipScore)
    

    这将为您提供来自 schoolPartnership 的行,每个 schoolId 的最大合作伙伴分数。

    【讨论】:

    • 我不确定这有什么帮助,恐怕:t1 (schoolPartnership) 上不存在合作伙伴分数字段,它位于子查询中,这就是我遇到此问题的原因。跨度>
    • @ChrisC 这只是一个展示如何做到这一点的例子。我建议为大型 SQL 的问题创建 sqlfiddle。
    【解决方案4】:

    我无法找到解决方案(除了复制子查询,这是我试图避免的),所以我刚刚确定了 PHP 中每个合作伙伴分数的 MAX 行,并丢弃了任何其他行。这不是一个理想的解决方案,但由于我需要一种跨平台的方法,因此我没有太多其他选择。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-23
      • 2011-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-14
      相关资源
      最近更新 更多