2011-01-25 50 views
2

我在StackOverflow中看到過類似的其他問題,但所有這些問題都基於自動增量ID,但我沒有。MySQL查詢 - 自定義排序結果的行之前和之後

我有這樣一個查詢:

SELECT field_a, 
     field_b 
    from table 
    where field_m = '100' 
    and field_n = '200' 
order by field_x 

導致該

field_a field_b 
------------------- 
john  12  
marty  7  
peter  2  
carl  9  
mark  11  
bob  10  
neil  1  
louis  14  

所以,我想要的是完成原始查詢和使用一個查詢取記錄之前和AFTER其中之一...讓我們說「carl」,但重要的是,在每種情況下,都是不同的,我的意思是,在「bob」之前和之後需要其他時間...

因此,讓我們說「carl」 ... 我需要 創建一個唯一的SQL查詢,其中我使用field_x描述的順序,並採取前後行的時候field_a ='carl'

回答

0

您可以使用一個查詢,但需要兩個集之間的UNION來獲取結果合併在一起

SELECT * FROM 
(
SELECT b.* 
from tbl a 
inner join tbl b on 
     b.field_m = '100' 
    and b.field_n = '200' 
where a.field_m = '100' 
    and a.field_n = '200' 
    and a.field_a = 'carl' 
    and b.field_x <= a.field_x # comes before a sorted on x 
order by b.field_x DESC 
limit 2 
) A 
UNION 
SELECT * FROM 
(
SELECT b.* 
from tbl a 
inner join tbl b on 
     b.field_m = '100' 
    and b.field_n = '200' 
where a.field_m = '100' 
    and a.field_n = '200' 
    and a.field_a = 'carl' 
    and b.field_x >= a.field_x # comes after a sorted on x 
order by b.field_x ASC 
limit 2 
) B 

注:這包括 '卡爾' 本身。 UNION負責移除第二個'carl'。

性能 - 應至少創建一個索引至少(field_m, field_n),如果是(field_m, field_n, field_x)則更好,以使該查詢合理執行。只要field_m + field_n削減表的威風,性能

(size after filter m/n) x (size after filter m/n) // triangular 
x2 

這個工程的方式是,它穿過設置爲自身,其中在「卡爾」錨定和B僅保持行在位置之前(設置1)或之後(設置2)。爲了妥善排序,然後採取LIMIT 2將包括'carl'以及另一個(除非在允許重複時也是'carl')。

+0

嗨,你認爲會走的快的表2.000.000記錄? – FlamingMoe 2011-01-25 23:45:58

+0

如果你有一個關於(field_m,field_n,field_x)的複合索引,那就沒事了,每個分支只看一條記錄 – RichardTheKiwi 2011-01-25 23:47:55

1

在大型表格上它會相當沉重,但是您可以使用排名並連接兩次以獲得上一個和下一個記錄,然後使用在哪裏對其進行過濾。

SET @rank_prev = 0; 
SET @rank_cur = 0; 
SET @rank_next = 0; 
SELECT 
    prev.field_a as prev_a, 
    prev.field_b as prev_b, 
    next.field_a as next_a, 
    next.field_b as next_b 
FROM 
( 
    SELECT 
     @rank_cur:[email protected]_cur+1 AS rank, 
     field_a, 
     field_b 
    FROM dd 
    WHERE field_m = '100' 
    AND field_n = '200' 
    ORDER BY field_x 
) as cur 
INNER JOIN 
(
    SELECT 
     @rank_prev:[email protected]_prev+1 AS rank, 
     field_a, 
     field_b 
    FROM dd 
    WHERE field_m = '100' 
    AND field_n = '200' 
    ORDER BY field_x 
) as prev 
ON prev.rank + 1 = cur.rank 
INNER JOIN 
(
    SELECT 
     @rank_next:[email protected]_next+1 AS rank, 
     field_a, 
     field_b 
    FROM dd 
    WHERE field_m = '100' 
    AND field_n = '200' 
    ORDER BY field_x 
) as next 
ON cur.rank+1 = next.rank 
WHERE cur.field_a = 'carl'; 

作品在MySQL