2013-02-12 62 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