2016-03-08 71 views
1

看起來這應該是一個簡單解決方案的常見問題,但我還沒有找到它。 我想計算child_order,這是不同的子表中的行出現的順序如在下面的數據顯示如下:爲前N個不同的子錶行選擇所有行

child_order PK1 PK2  ACCESS  ACCESS_ID 
1    99 Al  NULL  NULL 
2    55 Charles Accounts 1 
2    55 Charles Desktop 2 
2    55 Charles Printer 3 
2    55 Charles Servers 4 
2    55 Charles VMs  5 
3    66 Charles Desktop 2 
3    66 Charles VMs  5 
4    22 Chris  Desktop 2 
4    22 Chris  Printer 3 
4    22 Chris  Servers 4 
5    89 Evan  Desktop 2 

由查詢像檢索:

SELECT sub1.* 
FROM (
    SELECT ??? as child_order, sub2.* 
    FROM (
     SELECT ct.PK1, ct.PK2, pt1.ACCESS, pt1.ACCESS_ID 
     FROM child_table ct 
     LEFT JOIN some_linktable lt ON lt.child_id = ct.id 
     LEFT JOIN parent_table1 pt1 ON lt.parent_id = pt1.id 
     WHERE ct.PK2 IN ('Charles', 'Evan', 'Al', 'Chris') 
     ORDER BY ct.PK2, pt1.ACCESS -- Order must be preserved 
    ) sub2 
) sub1 
WHERE child_order < 10 AND (other_conditions) 

我可以使用子查詢,聚合,分析等,但不是真正的CTE /「WITH」語句或臨時表,因爲動態生成SQL的複雜性。

具體來說,我爲連接多個表的查詢的搜索結果生成分頁SQL(針對多個DBMS)。 我想弄清楚如何簡單地顯示前N行,不包括由於連接而引起的重複(例如,Chris只計爲一行,Access顯示「桌面,打印機,服務器」)。

我試過DENSE_RANK() OVER (ORDER BY PK1, PK2),但我當然得到PK1 PK2順序的排名,這對WHERE子句沒用。例如,Al的值會高於1.

我試過DENSE_RANK() OVER (ORDER BY PK2, ACCESS),但它僅枚舉搜索項,而不是子錶行。

我試過DENSE_RANK() OVER (PARTITION BY PK2, ACCESS ORDER BY (SELECT NULL))(要得到DENSE_RANK使用它給出的行順序,這是我想如何排序值),但只返回「1」。

我會省略我的其他「嘗試隨機的東西」階段的嘗試。

我想避免有一個SELECT DISTINCT PK1, PK2 WHERE (search) ORDER BY (sortorder)子查詢,因爲可能有零或非常多的主鍵字段,所以動態SQL生成會很棘手,另外,我懷疑性能會吸引所有WHERE sub3.field1 = sub2.field1 AND sub3.field2 = sub2.field2...檢查。

回答

1

儘管你對SELECT DISTINCT疑慮,這可能是一個子查詢的最佳選擇:

SELECT row_number() OVER (ORDER BY PK2) AS child_order, PK1, PK2 
FROM (
    SELECT DISTINCT PK1, PK2 
    FROM child_table 
    WHERE PK2 IN ('Charles', 'Evan', 'Al', 'Chris') 
    ORDER BY PK2 
    LIMIT 9) sub2 

child_order場只取決於表child_table,你想只有9行他們的,所以計算child_order僅在該表的子查詢中。在你有了之後,你可以加入其他表格。如果你有一個索引child_table(PK1, PK2)這應該是一個非常快速索引的搜索。它需要一些內部的過濾和限制,所以包絡查詢要簡單得多:

SELECT sub1.child_order, PK1, PK2, pt1.ACCESS, pt1.ACCESS_ID 
FROM child_table ct 
JOIN (
    SELECT row_number() OVER (ORDER BY PK2) AS child_order, PK1, PK2 
    FROM (
    SELECT DISTINCT PK1, PK2 
    FROM child_table 
    WHERE PK2 IN ('Charles', 'Evan', 'Al', 'Chris') 
    ORDER BY PK2 
    LIMIT 9) sub2 
) sub1 USING (PK1, PK2) 
LEFT JOIN some_linktable lt ON lt.child_id = ct.id 
LEFT JOIN parent_table1 pt1 ON lt.parent_id = pt1.id 
WHERE <other conditions> 
ORDER BY sub1.child_order, pt1.ACCESS; -- Faster to order by int 
+0

我很感謝答案,但是這不會滿足要求,因爲不保留正確的順序。 內部查詢僅按子表進行排序,但沒有針對父表進行排序,相應的「前N行」將丟失。 一個可以作爲一個子查詢執行整個過濾,加入,排序的主查詢,然後再次執行它,以反對自身的行順序,但這似乎令人難以置信的複雜,我認爲將是一個非常常見的SQL問題:搜索,排序和來自加入的結果分頁。這看起來不合理嗎? –