Я рассматриваю предложение 4 новых операторов сравнения в SQL. Они похожи на операторы >
, <
, >=
и <=
, но являются истинными, только если значение каждого операнда является среди всех значений, удовлетворяющих неравенству, одной ближайшей к значению другого операнда. Поскольку одно значение почти на другое значение, я пришел к выводу (после осознания отсутствия ключевого слова first
и после отбрасывания ключевого слова unique
), что хорошим выбором будет для определения этих 4 новых операторов:
-
a @> b
: true, еслиa > b
и noa' < a
удовлетворяетa' > b
, а nob' > b
удовлетворяетa > b'
-
a @< b
: true, еслиb @> a
-
a @>= b
: true, еслиa ≥ b
и noa' < a
удовлетворяетa' ≥ b
, а nob' > b
удовлетворяетa ≥ b'
-
a @<= b
: true, если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
Когда столбцы имеют уникальные значения, эффект операторов @...
можно получить, ограничив вывод запросов или подзапросов одной строкой, хотя с несколько более неуклюжим синтаксисом:
?> 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 и т.д., другие РСУБД имеют 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 не сможет обойтись без коррелированного подзапроса, тогда как мы, люди, узнаем лучше. Возможно, с помощью специальной обработки операторов @...
, подключенных к сети, оптимизатор выполнит одно сканирование?
Пример # 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
Операторы @...
, в отличие от своих @
-свободных аналогов, не являются транзитивными. Они делят это свойство с оператором <>
.
Предостережение № 3
Полностью использование операторов @...
может означать введение новых типов доступа к индексам и таблицам (как описано в примерах).
Обратите внимание, что этот вопрос не означает отправную точку для обсуждения. Я ищу причины, по которым что-то вроде операторов @...
не находится в стандарте или в любом диалоговом диалекте SQL, который мне известен. Я ожидаю, что эти причины будут связаны с некоторой проблемой с определением и/или внедрением этих операторов, что я упустили.
Я знаю, что одной из причин является "бритва Оккама" (pluralitas non est ponenda sine необходимо), но, как я попытался показать выше, плюралиты здесь также приносят некоторые преимущества (краткость и легкость оптимизации). Я ищу более веские причины.
(2014-03-31) @>
, @<
, @>=
и @<=
могут стать |>
, |<
, |>=
и |<=
или аналогичными (читайте: first more/менее [равно]), чтобы не сталкиваться с установленными обычаями префикса идентификатора @
.