SQL:元组比较

buk*_*zor 14 mysql sql aggregate-functions row-value-expression

在我当前的应用程序中,我需要能够执行此类查询:

SELECT MIN((colA, colB, colC)) 
FROM mytable
WHERE (colA, colB, colC) BETWEEN (200, 'B', 'C') AND (1000, 'E', 'F')
Run Code Online (Sandbox Code Playgroud)

并得到(333, 'B', 'B')这个数据的答案:

+------+------+------+
| colA | colB | colC |
+------+------+------+
|   99 | A    | A    |
|  200 | A    | Z    |
|  200 | B    | B    |
|  333 | B    | B    |
|  333 | C    | D    |
|  333 | C    | E    |
|  333 | D    | C    |
| 1000 | E    | G    |
| 1000 | F    | A    |
+------+------+------+
Run Code Online (Sandbox Code Playgroud)

在真正的SQL中实现这一目标的最有效方法是什么?请记住,这是一个玩具示例,我的实际应用程序包含不同列和数据类型的表,以及数亿行.我使用MySQL,如果有帮助的话.您还可以假设这些列对它们具有PRIMARY或UNIQUE索引.

如果解决方案可以轻松扩展到更多/更少的列,那就更好了.


元组比较:

有几个人问过,所以我应该把它放在这个问题上.元组按字典顺序排序,这意味着序列的排序与它们的第一个不同元素的顺序相同.例如,(1,2,x)<(1,2,y)返回与x <y相同的值.

值得注意的是SQL(或至少是mysql)正确实现了这一点:

mysql> select (200, 'B', 'C') < (333, 'B', 'B') and (333, 'B', 'B') < (1000, 'E', 'F');
+--------------------------------------------------------------------------+
| (200, 'B', 'C') < (333, 'B', 'B') and (333, 'B', 'B') < (1000, 'E', 'F') |
+--------------------------------------------------------------------------+
|                                                                        1 |
+--------------------------------------------------------------------------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

这是创建示例所必需的SQL:

create table mytable select 333 colA, 'B' colB, 'B' colC;
insert into mytable values (200, 'B', 'B'), (333, 'C', 'D'), (1000, 'E', 'G'), 
    (200, 'A', 'Z'), (1000, 'F', 'A'), (333, 'C', 'E'), (333, 'D', 'C'),
    (99, 'A', 'A');
alter table mytable add unique index myindex (colA, colB, colC);
Run Code Online (Sandbox Code Playgroud)

添加此索引似乎会导致表按字典顺序排序,这很有趣.在我们的生产系统中并非如此.

ype*_*eᵀᴹ 7

做就是了:

SELECT colA
     , colB
     , colC
FROM mytable
WHERE ( ('A',  'B',  'C') <= (colA, colB, colC ) )
  AND ( (colA, colB, colC) <= ('D',  'E',  'F' ) )
ORDER BY colA, colB, colC
LIMIT 1
;
Run Code Online (Sandbox Code Playgroud)

它工作得很好.而且我怀疑它应该也很快.


这是等效的,但它可能有更好的性能,具体取决于您的表:

SELECT m.colA
     , m.colB
     , m.colC
FROM mytable m
WHERE ( ('A',  'B',  'C') <= (m.colA, m.colB, m.colC) )
  AND ( (m.colA, m.colB, m.colC) <= ('D',  'E',  'F') )
  AND NOT EXISTS
  ( SELECT 1
    FROM mytable b
    WHERE (b.colA, b.colB, b.colC) < (m. colA, m.colB, m.colC)
      AND ( ('A',  'B',  'C') <= (b.colA, b.colB, b.colC) )
  );
Run Code Online (Sandbox Code Playgroud)