mysql - 为什么SQL中没有 "first greater/less than [or equal to]"比较运算符?

标签 mysql sql

我正在考虑在 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 1where 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/

    相关文章:

    php - 只是在 mysql 错误中插入空白数组()

    SQL 创建用户 FOR 与 FROM

    sql - 如何在postgresql中每两周选择和分组

    mysql - mysqld.cnf 是我当前的 Mysql 配置文件吗?

    mysql - 索引 MySQL View 的替代方法

    mysql 所有数据来自 5 个表 WHERE

    mysql - 计数大于总行数

    java - JAVA(DAO)和SQL中的聚合关系

    sql - 在数据库中创建表时向管理员发送邮件?

    mysql - #1442 - 无法更新存储函数/触发器中的表 'sale_price',因为它已被语句使用