Mysql

根據兩個條件 SQL 獲取某行上方和下方(相鄰行)的行

  • January 17, 2021

假設我有一張這樣的桌子:

+---+-------+------+---------------------+
|id | level |score |      timestamp      |
+---+-------+------+---------------------+
| 4 |   1   |  70  | 2021-01-14 21:50:38 |
| 3 |   1   |  90  | 2021-01-12 15:38:0  |
| 1 |   1   |  20  | 2021-01-14 13:10:12 |
| 5 |   1   |  50  | 2021-01-13 12:32:11 |
| 7 |   1   |  50  | 2021-01-14 17:15:20 |
| 8 |   1   |  55  | 2021-01-14 09:20:00 |
| 10|   2   |  99  | 2021-01-15 10:50:38 |
| 2 |   1   |  45  | 2021-01-15 10:50:38 |
+---+-------+------+---------------------+

我想要做的是在一個表中顯示其中的 5 行(在 html 中),中間有某一行(例如 id=5),並在其上方和下方有兩行(以正確的順序)。還有級別= 1。這就像一個計分板,但只顯示使用者的分數,上面兩個,下面兩個。所以因為分數可以相同,所以還需要使用時間戳列——所以如果兩個分數相等,那麼第一個獲得分數的人會顯示在另一個人的上方。

例如說使用者是 id=5,我想顯示

+---+-------+------+---------------------+
|id | level |score |      timestamp      |
+---+-------+------+---------------------+
| 4 |   1   |  70  | 2021-01-14 21:50:38 |
| 8 |   1   |  55  | 2021-01-14 09:20:00 |
| 5 |   1   |  50  | 2021-01-13 12:32:11 |
| 7 |   1   |  50  | 2021-01-14 17:15:20 |
| 2 |   1   |  45  | 2021-01-15 10:50:38 |
| 1 |   1   |  20  | 2021-01-14 13:10:12 |
+---+-------+------+---------------------+

注意 id=7 低於 id=5

我想知道有人知道這樣做的方法嗎?

我在下面嘗試過,但它沒有輸出我需要的東西(它輸出的地方 level_id=2 和 id=5,其他行不按順序排列)

((SELECT b.* FROM table a JOIN table b ON b.score > a.score OR (b.score = a.score AND b.timestamp < a.timestamp)
 WHERE a.level_id = 1 AND a.id = 5 ORDER BY score ASC, timestamp DESC LIMIT 3)
UNION ALL 
(SELECT b.* FROM table a JOIN table b ON b.score < a.score OR (b.score = a.score AND b.timestamp > a.timestamp)
 WHERE a.level_id = 1 AND a.id = 5 ORDER BY score DESC, timestamp ASC LIMIT 2)) 
order by score 

如果更容易輸出表中的所有行,說哪裡 level = 1,所以它是一個滿分板.. 然後使用 PHP 獲取某一行和上下兩行我也想知道請 :) !(可能認為這可能會使 SQL 更簡單)?

id 5 和 LIMIT 的值必須預先計算並傳遞給查詢,因為 LIMIT 必須具有固定值,否則您必須使用準備好的語句

CREATE TABLE table1
    (`id` int, `level` int, `score` int, `timestamp` varchar(19))
;
    
INSERT INTO table1
    (`id`, `level`, `score`, `timestamp`)
VALUES
    (4, 1, 70, '2021-01-14 21:50:38'),
    (3, 1, 90, '2021-01-12 15:38:0'),
    (1, 1, 20, '2021-01-14 13:10:12'),
    (5, 1, 50, '2021-01-13 12:32:11'),
    (7, 1, 50, '2021-01-14 17:15:20'),
    (8, 1, 55, '2021-01-14 09:20:00'),
    (10, 2, 99, '2021-01-15 10:50:38'),
    (2, 1, 45, '2021-01-15 10:50:38')
;
SELECT 
    `id`, `level`, `score`
FROM
    ((SELECT 
        `id`, `level`, `score`, 1 orderby
    FROM
        table1 a
    WHERE
        level = 1
            AND `score` > (SELECT 
                `score`
            FROM
                table1
            WHERE
                id = 5)
    ORDER BY `score` ASC
    LIMIT 2) UNION ALL (SELECT 
        `id`, `level`, `score`, 2 orderby
    FROM
        table1 a
    WHERE
        id = 5) UNION ALL (SELECT 
        `id`, `level`, `score`, 3 orderby
    FROM
        table1 a
    WHERE
        level = 1
            AND `score` <= (SELECT 
                `score`
            FROM
                table1
            WHERE
                id = 5)
            AND id <> 5
    ORDER BY `score` DESC
    LIMIT 3)) t1
ORDER BY orderby ASC , score DESC
編號 | 水平 | 分數
-: | ----: | ----:
 4 | 1 | 70
 8 | 1 | 55
 5 | 1 | 50
 7 | 1 | 50
 2 | 1 | 45
 1 | 1 | 20

db<>在這裡擺弄

SELECT z.id, z.level, z.score, z.`timestamp`
 FROM ( SELECT w.*
             , @num := @num+1 AS num
             -- just to get the 'num' for middle line: 
             , IF( w.id = 5, @hit := @num, NULL ) AS unused
          FROM table AS w
          WHERE @num := 1 -- UDV initialization
            AND w.level = 1
          ORDER BY w.score DESC
                 , w.`timestamp` ASC
      ) AS z
WHERE z.num BETWEEN @hit-2 AND @hit+2 -- how many rows above and below  
ORDER BY z.num ASC
;

Subselect 列舉按分數和時間戳排序的行,並將@num給定 ID 的值儲存在使用者定義的變數@hit中。外部選擇僅過濾給定範圍內的行。

Dbfiddle 對 UDV 的工作非常糟糕,因此您必須在真實數據庫上測試該程式碼。

引用自:https://dba.stackexchange.com/questions/283352