在两列上排序的列表中的上一项

时间:2012-02-15 16:45:24

标签: mysql sql

假设我们有一个包含DAY和NUMERO列的表格。

可能有许多行具有相同的DAY值,但NUMERO始终是唯一的。

在由DAY,NUMERO订购的列表中,现有夫妇(DAY,NUMERO)之前的项目最有效的方法是什么?

我确切地知道我需要这个用于mysql而且我不想添加特定的索引(这就是我不仅仅使用DAY和NUMERO的线性函数的原因)。

这是一个有序的测试用例:

DAY | NUMERO
1   | 11
1   | 12
1   | 15
4   | 7
4   | 9
4   | 14
5   | 8
6   | 10
6   | 19

我的要求必须这样做:

(1,11)=>没有

(1,15)=> (1,12)

(4,7)=> (1,15)

(4,9)=> (4,7)

(4,14)=> (4,9)

编辑:

我目前最好的解决方案是连续两次查询:

select * from item where day=? and numero<? order by day desc, numero desc limit 1;

select * from item where day<? order by day desc, numero desc limit 1;

如果第一个查询给出结果,我不必运行第二个查询。

类似的解决方案是使用union,但mysql似乎不会授权具有多个列的联合。

对于看似如此简单的问题,这两个解决方案看起来都太沉重了......

3 个答案:

答案 0 :(得分:4)

更新

这是一个实际上给出正确结果的解决方案!

 SELECT T1.day, 
       T1.numero, 
       COALESCE(MAX(T2.[day]), MAX(T3.[day])) AS prev_day,
       COALESCE(MAX(T2.numero), MAX(T3.numero)) AS prev_numero
FROM @table AS T1
LEFT JOIN @table AS T2 ON 
(
    T2.[day] = T1.[day] AND T2.numero < T1.numero
)
LEFT JOIN @table AS T3 ON  
(
    T3.[day] < T1.[day] 
    AND NOT EXISTS(
        SELECT * 
        FROM @table AS T4
        WHERE T4.[day] > T3.[day] AND T4.[day] < T1.[day]
    )
)
-- Add where clause like so to get specific values
-- WHERE T1.day = 4 AND T1.numero = 7
GROUP BY T1.day, T1.numero
ORDER BY T1.day, T1.numero

结果:

day         numero      prev_day    prev_numero
----------- ----------- ----------- -----------
1           11          NULL        NULL
1           12          1           11
1           15          1           12
4           7           1           15
4           9           4           7
4           11          4           9
5           8           4           11
6           10          5           8
6           19          6           10

答案 1 :(得分:1)

这是一个vanilla查询,避免使用CTE,聚合或窗口函数。

-- (a Before b) := (a.day < b.day
--               OR (a.day = b.day AND a.numero < b.numero))
SELECT d1.day AS DAY
        , d1.numero AS numero
        , d0.day AS day0
        , d0.numero AS numero0
  FROM tmp.lutser d1
  LEFT JOIN tmp.lutser d0
        ON (d0.day < d1.day OR (d0.day = d1.day AND d0.numero < d1.numero ))
  WHERE NOT EXISTS (SELECT *
     FROM tmp.lutser d
     WHERE (dx.day < d1.day OR (dx.day = d1.day AND dx.numero < d1.numero ))
       AND (dx.day > d0.day OR (dx.day = d0.day AND dx.numero > d0.numero ))
     )
ORDER BY day,numero
        ;

作为参考,这是使用窗口函数的查询:

SELECT day
        , numero
        , lag(day) OVER (w1)
        , lag(numero) OVER (w1)
FROM tmp.lutser
WINDOW w1 AS (ORDER BY day, numero)
        ;

结果:

 day | numero | day0 | numero0 
-----+--------+------+---------
   1 |     11 |      |        
   1 |     12 |    1 |      11
   1 |     15 |    1 |      12
   4 |      7 |    1 |      15
   4 |      9 |    4 |       7
   4 |     11 |    4 |       9
   5 |      8 |    4 |      11
   6 |     10 |    5 |       8
   6 |     19 |    6 |      10
(9 rows)

答案 2 :(得分:1)

如果您只是根据一对检索前一对而不是尝试执行整个表,正如其他人所提到的那样,您可以尝试以下简单查询 -

:day = 4
:numero = 9

SELECT day, numero
FROM table
WHERE (day = :day AND numero < :numero)
OR (day < :day)
ORDER BY day DESC, numero DESC
LIMIT 1