2013-02-12 104 views
3

我试图计算返回表中的最大值,以及该表中的其他值。然而,我这样做的表不是一个“真正的”表,它是由子查询生成的。这给我带来了问题,因为我不认为我可以加入它两次,而无需重新指定整个子查询。子查询上的Groupwise MAX()

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

这里的架构和我的SQL Server特定的查询,如果他们是有用的:

课程:

  • INT ID
  • VARCHAR名
  • INT schoolId

派息:

  • INT ID
  • VARCHAR名

合作伙伴:

  • INT ID
  • VARCHAR partnershipName

SchoolPartnership:

  • INT ID
  • INT schoolId
  • INT partnershipId

这里的查询:

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:该查询将是一个较大查询的子查询,通过最合适的伙伴关系对学校进行排序。

回答

0

我无法找到解决方案(除了复制子查询,这正是我试图避免的),所以我刚刚为PHP中的每个partnershipScore标识了MAX行,并丢弃了其他任何行。这不是一个理想的解决方案,但是由于我需要跨平台的方法,所以没有太多其他选择对我开放。

0

这是你想要的结构:

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

这是一个有点很难看到它如何适应您的查询,因为我不能告诉底座子查询是什么。

至于不止一次加入子查询,可以使用SQL Server调用“公用表表达式”的方式来执行此操作 - 上述with子句。大多数其他合理的数据库支持这一点(MySQL和MS Access开始两个显着的例外)。

+0

谢谢,这将是理想的,但不幸的是Sqlite不支持CTE。 – ChrisC 2013-02-12 23:11:27

+0

@ChrisC。 。 。当我回答这个问题时,它没有被标记为SQLite。为什么它使用两个数据库进行标记? – 2013-02-13 04:42:53

+0

它由某人编辑。编辑很好,原始问题解释了为什么:我目前有一个SQL Server特定的查询,我需要隐藏它,因此它在SQL Server和SQLite中都可以工作。 – ChrisC 2013-02-13 07:51:18

0

最SQL不可知论的方法将使用“非存在”:

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

这会给你schoolPartnership行,每次使用schoolId最大partnershipScore。

+0

我不知道这有什么帮助我很害怕:t1(schoolPartnership)上不存在partnershipScore字段,它在子查询中,这就是我遇到这个问题的原因。 – ChrisC 2013-02-12 23:12:22

+0

@ChrisC这只是一个例子来说明你如何做到这一点。我建议为具有大型SQL的问题创建sqlfiddle。 – Bulat 2013-02-13 07:51:48

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关键字,而是在表名的开头使用一个特殊字符(#)来表示该表实际上是临时表。

因此,我可以看到的唯一解决方法是使用正常的表作为临时存储。运行查询时,只需插入临时结果集之前,你可以一次创建它,然后删除其内容每次:

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

+0

非常感谢您的详细回复。由于并发问题,我不认为我能够继续正常/临时表路由,所以我认为我(不幸的是)可能不得不复制子查询。完成后我会更新这个问题。 – ChrisC 2013-02-12 23:17:37