3

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

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

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

课程:

  • 内部编号
  • varchar 名称
  • 诠释学校ID

学校:

  • 内部编号
  • varchar 名称

合伙:

  • 内部编号
  • varchar 合伙人名称

学校合作:

  • 内部编号
  • 诠释学校ID
  • 内部伙伴关系 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

如果您需要有关我要实现的目标的更多信息,请参阅针对大量数据的自定义排序算法:此查询将是较大查询的子查询,该查询通过最合适的合作伙伴对学校进行排序。

4

4 回答 4

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 不支持语句TEMPORARY上下文中的关键字,CREATE TABLE而是在表名的开头使用特殊字符 ( #) 来表示该表实际上是一个临时表。

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

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 一样不同时。)

于 2013-02-12T22:35:59.270 回答
0

这是你想要的结构:

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

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

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

于 2013-02-12T20:54:36.317 回答
0

最与 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 的最大合作伙伴分数。

于 2013-02-12T21:34:26.437 回答
0

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

于 2013-02-18T22:43:28.210 回答