我正在考虑在 SQL 中提出 4 个新的比较运算符。这些类似于 >
, <
, >=
和 <=
运算符,但仅当每个操作数的值是满足不等式的所有值中的一个 时才为真。最近 到另一个操作数的值。由于一个值几乎是 在 另一个值,我得出的结论是(在意识到没有 first
关键字,并在丢弃 unique
关键字之后)定义这 4 个新运算符是一个不错的选择:
a @> b
: 如果 a > b
为真没有 a' < a
满足a' > b
没有 b' > b
满足a > b'
a @< b
: 如果 b @> a
为真a @>= b
: 如果 a ≥ b
为真没有 a' < a
满足a' ≥ b
没有 b' > b
满足a ≥ b'
a @<= b
: 如果 b @>= a
为真问题是:像这样的运营商不存在有什么好的理由吗?
(2014-03-20) 我重新表述了这个问题,因为上面的表述显然不够清楚:
有没有理由不存在这样的运营商?
以下示例旨在作为找出
@...
问题所在的起点。运营商可能会撒谎。我将使用 3 个 MySQL 表:create table ta (id int auto_increment, ca char, primary key(id), unique index(ca));
create table tb (id int auto_increment, cb char, primary key(id), index(cb));
create table tc (id int auto_increment, cc char, primary key(id));
insert into ta (ca) values ('A'),('E'),('I'),('O'),('U');
insert into tb (cb) values ('C'),('D'),('E'),('F'),('F'),('M'),('N'),('O'),('Z');
insert into tc (cc) values ('C'),('D'),('E'),('F'),('F'),('M'),('N'),('O'),('Z');
示例#1
当列具有唯一值时,
@...
的效果可以通过将查询或子查询的输出限制为 1 行来获得运算符,尽管语法有点笨拙:?> select * from ta where ca @> 'B'; -- currently not valid, equivalent to:
!> select * from ta where ca > 'B' order by ca limit 1;
+----+------+
| id | ca |
+----+------+
| 2 | E |
+----+------+
(
limit 1
特定于 MySQL、MariaDB、PostgreSQL 等,其他 RDBMS 有 select top 1
、 where rownum = 1
等)表中
ta
我们在列 ca
上有一个唯一索引.可以利用该索引来获取所选值 同速至于ca = 'E'
.优化器可能会意识到这一点,但如果没有,则可能会为从选定值开始的不需要的扫描设置数据结构(MySQL 的 explain
说这是一个 range
类型的查询)。示例#2
当列具有非唯一值时,限制输出行是无用的,语法变得更加笨拙:
?> select * from tb where cb @> 'E'; -- currently not valid, equivalent to:
!> select * from tb where cb = (select min(cb) from tb where cb > 'E');
+----+------+
| id | cb |
+----+------+
| 4 | F |
| 5 | F |
+----+------+
幸运的是,如果我正确读取了
explain
的输出, MySQL 足够聪明,可以优化子查询,但如果不是,索引将被使用两次而不是一次。在表
tc
的情况下, 列 cc
上没有索引, MySQL 进行两次表扫描。这是可以理解的,因为单个表扫描意味着使用未知数量的存储来存储临时结果。示例 #3
假设您需要由一个值及其后继值组成的所有对:
?> select t1.ca as c1, t2.ca as c2
from ta t1
join ta t2 on t1.ca @< t2.ca; -- currently not valid, equivalent to:
!> select t1.ca as c1, t2.ca as c2
from ta t1
join ta t2 on t2.ca = (select min(ca) from ta where ca > t1.ca);
+------+------+
| c1 | c2 |
+------+------+
| A | E |
| E | I |
| I | O |
| O | U |
+------+------+
如果我读了
explain
的输出正确的是,MySQL 优化器不能没有相关的子查询,而我们人类会更清楚。也许在 @...
的特殊处理的帮助下运算符(operator)接入,优化器会做一次扫描吗?示例 #4
这很相似,但跨越两个表,其中一个具有非唯一索引:
?> select * from ta join tb on ca @< cb; -- currently not valid, equivalent to:
!> select * from ta join tb on cb = (select min(cb) from tb where cb > ca);
+----+------+----+------+
| id | ca | id | cb |
+----+------+----+------+
| 1 | A | 1 | C |
| 2 | E | 4 | F |
| 2 | E | 5 | F |
| 3 | I | 6 | M |
| 4 | O | 9 | Z |
| 5 | U | 9 | Z |
+----+------+----+------+
在这里,MySQL 优化器也不会优化掉子查询,尽管(可能带有
@<
的提示)它可以。示例#5
(添加于 2014-03-20。)
@...
运营商似乎在任何地方都有意义 @
——少同行做。这是 where
中表达式的人为示例健康)状况:?> select * from ta join tb
where round((ascii(ca)+ascii(cb))/2) @> ascii('E');
-- currently not valid, equivalent to:
!> select * from ta join tb
where round((ascii(ca)+ascii(cb))/2) = (
select min(round((ascii(ca)+ascii(cb))/2)) from ta, tb
where round((ascii(ca)+ascii(cb))/2) > ascii('E')
);
+----+------+----+------+
| id | ca | id | cb |
+----+------+----+------+
| 3 | I | 1 | C |
| 2 | E | 4 | F |
| 2 | E | 5 | F |
+----+------+----+------+
示例 #6
...这是另一个例子,这次是
select
表达:?> select *, cb @< ca
from tb, ta; -- currently not valid, equivalent to:
!> select *, ifnull(cb = (select max(cb) from tb where cb < ca), 0) as 'cb @< ca'
from tb, ta;
+----+------+----+------+----------+
| id | cb | id | ca | cb @< ca |
+----+------+----+------+----------+
| 1 | C | 1 | A | 0 |
| 1 | C | 2 | E | 0 |
| 1 | C | 3 | I | 0 |
| 1 | C | 4 | O | 0 |
| 1 | C | 5 | U | 0 |
| 2 | D | 1 | A | 0 |
| 2 | D | 2 | E | 1 |
| -- (omitting rows with cb @< ca equal to 0 from here on)
| 4 | F | 3 | I | 1 |
| 5 | F | 3 | I | 1 |
| 7 | N | 4 | O | 1 |
| 8 | O | 5 | U | 1 |
我知道以下注意事项:
警告 #1
@...
运算符是“非本地的”,因为它们需要了解其操作数的所有可能值。在上述示例中显示的所有条件下,这似乎不是问题,但在其他地方可能是问题(尽管我还没有找到无法通过附加子查询解决的示例)。警告 #2
@...
运营商,不像他们的 @
-less 对应项,不可传递。他们与 <>
共享此属性运营商,虽然。警告 #3
充分利用
@...
运算符可能意味着引入新的索引和表访问类型(如示例中所述)。请注意,这个问题并不是讨论的起点。我正在寻找类似
@...
的原因运算符不在标准中,也不在我所知的任何 SQL 方言中 - 我希望这些原因与我忽略的这些运算符的定义和/或实现的某些问题有关。我知道一个原因是“奥卡姆 Razor ”(pluralitas non est ponenda sine necessitate),但是,正如我在上面试图展示的,这里的 pluralitas 也带来了一些优势(简洁和易于优化)。我正在寻找更有力的理由。
(2014-03-31)
@>
, @<
, @>=
和 @<=
可以变成|>
, |<
, |>=
和 |<=
或类似(阅读:首先更大/更少 [等于]),以免与 @
的既定用法发生冲突标识符前缀。
最佳答案
我很困惑这是否是这个问题的合适论坛。但是,这些运算符不存在的原因是它们不是特别有用,其他 ANSI SQL 功能取而代之。
一、on
中的比较运算符在我知道的每个数据库中,子句都可以在 where
中找到。和 case
条款。目前尚不清楚这些运算符将如何在这些上下文中使用。
其次,运营商没有指定在关系的情况下该做什么。返回所有行?但是,当这种运算符的用户只期望一行时,这将返回多行。
三、ANSI标准功能,如row_number()
可以产生等价的结果。虽然它可能不是这个特定问题的最佳选择,但它更通用。和标准。
顺便说一句,Postgres 有一个很好的功能 distinct on()
,这通常比等效的解析函数更有效。
我偷偷想要一个新的join
类型, lookup join
,如果有多个记录匹配,那将会失败。但是,我不太确定是否应该为此目的更改整个语言。
关于mysql - 为什么SQL中没有 "first greater/less than [or equal to]"比较运算符?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22438273/