Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / MySQL Новый топик    Ответить
 FAQ: Нахождение записей, где заданное значение находится между значениями полей  [new]
an0nym
Member

Откуда:
Сообщений: 7078
Задача
Быстро найти записи, где заданное (пользователем) значение находится между значениями полей.

Требования (если вы не сможете этого проделать в своём случае, дальше можно не читать)
Перевести таблицу, в которой идет поиск, на MyISAM или создать дополнительную таблицу на MyISAM, заполняемую при изменении первой триггерами или вручную.

Примеры
Найти звонки, начавшиеся, длившиеся или оконченные в определенное время.
Выяснить принадлежность IP адреса определенному региону по БД GeoIP.
Выяснить тариф звонка по определенному номеру назначения.
Выяснить тариф звонка по определенному номеру назначения в определенное время суток.
Выяснить тариф звонка по определенному номеру назначения в определенный момент времени (если хранятся все изменения тарифов во времени).
Найти биржевые ордера, открытые до или в и закрытые в или после определенного времени.

Формально
Оптимизировать запросы
SELECT ... FROM t WHERE :value BETWEEN col1 AND col2
SELECT ... FROM t WHERE :value BETWEEN col1 AND col2 AND :another_value BETWEEN col3 AND col4


Реализация
на примере "Выяснить тариф звонка по определенному номеру назначения в определенный момент времени (если хранятся все изменения тарифов во времени)"
+
+ Подготовим данные

>mysql --host=localhost --user=... --password
Enter password: ...
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.1.35-community-debug MySQL Community Server - Debug (GPL)

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> USE test;
Database changed
mysql> CREATE TABLE price(
    ->   id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    ->   start_number BIGINT NOT NULL
    ->     COMMENT 'destination number first (inclusive)',
    ->   end_number BIGINT NOT NULL
    ->     COMMENT 'destination number last (inclusive)',
    ->   start_d DATE NOT NULL
    ->     COMMENT 'price became active (inclusive)',
    ->   end_d DATE NOT NULL
    ->     COMMENT 'price became inactive (inclusive)',
    ->   price DECIMAL(10, 6) NOT NULL DEFAULT 1,
    ->   # notice, we create all possible index combinations
    ->   #         however these indexes won`t help us much
    ->   INDEX(start_number),
    ->   INDEX(end_number),
    ->   INDEX(start_d),
    ->   INDEX(end_d),
    ->   INDEX(start_number, end_number),
    ->   INDEX(end_number, start_number),
    ->   INDEX(start_d, end_d),
    ->   INDEX(end_d, start_d),
    ->   INDEX(start_number, end_number, start_d, end_d),
    ->   INDEX(start_number, end_number, end_d, start_d),
    ->   INDEX(end_number, start_number, start_d, end_d),
    ->   INDEX(end_number, start_number, end_d, start_d),
    ->   INDEX(start_d, end_d, start_number, end_number),
    ->   INDEX(end_d, start_d, start_number, end_number),
    ->   INDEX(start_d, end_d, end_number, start_number),
    ->   INDEX(end_d, start_d, end_number, start_number)
    -> )
    -> ENGINE = InnoDB
    -> COMMENT 'historized pricelist by destination';
Query OK, 0 rows affected (0.08 sec)

Заполним произвольными данными
mysql> DELIMITER $$
mysql> CREATE FUNCTION fill_price(
    ->     # period to generate pricelist for
    ->     first_d DATE,
    ->     last_d  DATE,
    ->     # how many days a price stays actual
    ->     # random-generated between min and max for each price
    ->     days_actual_min INT,
    ->     days_actual_max INT,
    ->     # how many numbers there are in a price entry
    ->     # random-generated between min and max for each price
    ->     step_min INT,
    ->     step_max INT
    ->   )
    ->   RETURNS INT
    ->   COMMENT 'fills pricelist with randomly generated rates'
    -> BEGIN
    ->   DECLARE last_number BIGINT DEFAULT 99999999999;
    ->
    ->   DECLARE cnt INT DEFAULT 0;
    ->
    ->   DECLARE start_d DATE;
    ->   DECLARE end_d DATE;
    ->   DECLARE start_number BIGINT DEFAULT 0;
    ->   DECLARE end_number BIGINT;
    ->
    ->   WHILE start_number < last_number DO
    ->     SET end_number = start_number
    ->                      + FLOOR(step_min + RAND() * (step_max - step_min));
    ->     SET start_d    = first_d;
    ->     WHILE start_d < last_d DO
    ->       SET end_d = start_d
    ->                   + INTERVAL FLOOR(
    ->                       days_actual_min
    ->                       + RAND() * (days_actual_max - days_actual_min)
    ->                     ) DAY;
    ->       INSERT INTO price(start_d, end_d, start_number, end_number)
    ->         VALUES(start_d, end_d, start_number, end_number);
    ->       SET cnt = cnt + 1;
    ->       SET start_d = end_d + INTERVAL 1 DAY;
    ->     END WHILE;
    ->     SET start_number = end_number + 1;
    ->   END WHILE;
    ->
    ->   RETURN cnt;
    -> END$$
Query OK, 0 rows affected (0.00 sec)

mysql> DELIMITER ;
mysql> SELECT fill_price(
    ->     @first_d, @last_d, 30, 60, 10000000, 1000000000
    ->   ) entries
    -> FROM (
    ->     SELECT @first_d := '2008-01-01' + INTERVAL 0 DAY first_d,
    ->            @last_d  := '2010-12-31' + INTERVAL 0 DAY last_d
    ->   ) v;
+---------+
| entries |
+---------+
|    5133 |
+---------+
1 row in set (9.58 sec)

mysql> DROP FUNCTION fill_price;
Query OK, 0 rows affected (0.00 sec)
+
+ Обычные запросы работают плохо

индексы не сильно помогают: разброс дат-номеров достаточно равномерен и избавившись от full table scan всё равно приходится просматривать достаточное количество записей, из-за этого производительности в большинстве случаев недостаточно
mysql> SELECT @destination_number := FLOOR(RAND() * 99999999999) number,
    ->        @d := @first_d + INTERVAL FLOOR(
    ->                                         RAND()
    ->                                         * DATEDIFF(@last_d, @first_d)
    ->                                       ) DAY d
    -> FROM (SELECT @desination_number := 0, @d := '0000-00-00') v;
+-------------+------------+
| number      | d          |
+-------------+------------+
| 81110108904 | 2010-11-17 |
+-------------+------------+
1 row in set (0.00 sec)

mysql> EXPLAIN EXTENDED
    -> SELECT p.start_number, p.end_number, p.start_d, p.end_d, p.price
    -> FROM price p
    -> WHERE @destination_number BETWEEN p.start_number AND p.end_number
    ->   AND @d                  BETWEEN p.start_d      AND p.end_d;
+----+-------------+-------+-------+--------------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | type  | possible_keys      | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+-------+--------------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | p     | range | start_number,      | end_d_3 | 3       | NULL |  394 |   100.00 | Using where |
|    |             |       |       | end_number,        |         |         |      |      |          |             |
|    |             |       |       | start_d,end_d,     |         |         |      |      |          |             |
|    |             |       |       | start_number_2,    |         |         |      |      |          |             |
|    |             |       |       | end_number_2,      |         |         |      |      |          |             |
|    |             |       |       | start_d_2,end_d_2, |         |         |      |      |          |             |
|    |             |       |       | start_number_3,    |         |         |      |      |          |             |
|    |             |       |       | start_number_4,    |         |         |      |      |          |             |
|    |             |       |       | end_number_3,      |         |         |      |      |          |             |
|    |             |       |       | end_number_4,      |         |         |      |      |          |             |
|    |             |       |       | start_d_3,end_d_3, |         |         |      |      |          |             |
|    |             |       |       | start_d_4,end_d_4  |         |         |      |      |          |             |
+----+-------------+-------+-------+--------------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)

mysql> SELECT p.start_number, p.end_number, p.start_d, p.end_d, p.price
    -> FROM price p
    -> WHERE @destination_number BETWEEN p.start_number AND p.end_number
    ->   AND @d                  BETWEEN p.start_d      AND p.end_d;
+--------------+-------------+------------+------------+----------+
| start_number | end_number  | start_d    | end_d      | price    |
+--------------+-------------+------------+------------+----------+
|  80985716868 | 81183917450 | 2010-11-07 | 2010-12-23 | 1.000000 |
+--------------+-------------+------------+------------+----------+
1 row in set (0.02 sec)
Просмотреть 394 строки из 5133 (=~7%) - не очень хороший результат. При равномерном распределении и росте количества строк, процент выбираемых от общего числа строк не сильно изменится. Будет 100 000 строк - придется просмотреть 7000. Будет 1 млн. - придется просмотреть 70 тыс. Т. о. по мере накопления данных в таблице, запрос будет работать всё медленнее и медленнее.
Посмотрим, что с этим можно сделать. +
+ Использование RTree индексов

В MySQL есть сильно урезанная поддержка RTree индексов. Речь идет о (геометрических) spatial-индексах для MyISAM таблиц. К сожалению, для других типов таблиц данные индексы не поддерживаются и скорее всего в ближайшее время не будут.
Преобразуем наш прайс-лист в MyISAM и добавим LINESTRING колонку
mysql> ALTER TABLE price
    ->   ENGINE = MyISAM,
    ->   ADD COLUMN start_end_number_d LINESTRING NOT NULL;
Query OK, 5133 rows affected (1.35 sec)
Records: 5133  Duplicates: 0  Warnings: 0
Заполним её нужными значениями.
mysql> UPDATE price p
    -> SET p.start_end_number_d
    ->       = LINESTRING(
    ->           POINT(p.start_number, p.start_d),
    ->           POINT(p.end_number,   p.end_d)
    ->         );
Query OK, 5133 rows affected (0.89 sec)
Rows matched: 5133  Changed: 5133  Warnings: 0
Добавим RTree-индекс.
mysql> ALTER TABLE price
    ->   ADD SPATIAL INDEX(start_end_number_d);
Query OK, 5133 rows affected (3.51 sec)
Records: 5133  Duplicates: 0  Warnings: 0

Запросы по RTree индексу отрабатывают моментально
RTree-индекс идеально подходит для такого типа запросов, просматривается всегда ровно столько строк, сколько будет в результате запроса.
mysql> EXPLAIN EXTENDED
    -> SELECT p.start_number, p.end_number, p.start_d, p.end_d, p.price
    -> FROM price p
    -> WHERE MBRContains(
    ->         start_end_number_d,
    ->         POINT(@destination_number, DATE(@d))
    ->       );
+----+-------------+-------+-------+--------------------+--------------------+---------+------+------+----------+-------------+
| id | select_type | table | type  | possible_keys      | key                | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+-------+--------------------+--------------------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | p     | range | start_end_number_d | start_end_number_d | 34      | NULL |    1 |   100.00 | Using where |
+----+-------------+-------+-------+--------------------+--------------------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)

mysql> SELECT p.start_number, p.end_number, p.start_d, p.end_d, p.price
    -> FROM price p
    -> WHERE MBRContains(
    ->         start_end_number_d,
    ->         POINT(@destination_number, DATE(@d))
    ->       );
+--------------+-------------+------------+------------+----------+
| start_number | end_number  | start_d    | end_d      | price    |
+--------------+-------------+------------+------------+----------+
|  80985716868 | 81183917450 | 2010-11-07 | 2010-12-23 | 1.000000 |
+--------------+-------------+------------+------------+----------+
1 row in set (0.00 sec)

+
+ Одним скриптом
USE test;
CREATE TABLE price(
  id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  start_number BIGINT NOT NULL 
    COMMENT 'destination number first (inclusive)',
  end_number BIGINT NOT NULL 
    COMMENT 'destination number last (inclusive)',
  start_d DATE NOT NULL 
    COMMENT 'price became active (inclusive)',
  end_d DATE NOT NULL 
    COMMENT 'price became inactive (inclusive)',
  price DECIMAL(10, 6) NOT NULL DEFAULT 1,
  # notice, we create all possible index combinations
  #         however these indexes won`t help us much
  INDEX(start_number),
  INDEX(end_number),
  INDEX(start_d),
  INDEX(end_d),
  INDEX(start_number, end_number),
  INDEX(end_number, start_number),
  INDEX(start_d, end_d),
  INDEX(end_d, start_d),
  INDEX(start_number, end_number, start_d, end_d),
  INDEX(start_number, end_number, end_d, start_d),
  INDEX(end_number, start_number, start_d, end_d),
  INDEX(end_number, start_number, end_d, start_d),
  INDEX(start_d, end_d, start_number, end_number),
  INDEX(end_d, start_d, start_number, end_number),
  INDEX(start_d, end_d, end_number, start_number),
  INDEX(end_d, start_d, end_number, start_number)
) 
ENGINE = InnoDB
COMMENT 'historized pricelist by destination';

DELIMITER $$
CREATE FUNCTION fill_price(
    # period to generate pricelist for
    first_d DATE,
    last_d  DATE,
    # how many days a price stays actual
    # random-generated between min and max for each price
    days_actual_min INT, 
    days_actual_max INT, 
    # how many numbers there are in a price entry
    # random-generated between min and max for each price
    step_min INT, 
    step_max INT
  )
  RETURNS INT
  COMMENT 'fills pricelist with randomly generated rates'
BEGIN
  DECLARE last_number BIGINT DEFAULT 99999999999;
  
  DECLARE cnt INT DEFAULT 0;
  
  DECLARE start_d DATE;
  DECLARE end_d DATE;
  DECLARE start_number BIGINT DEFAULT 0;
  DECLARE end_number BIGINT;
  
  WHILE start_number < last_number DO
    SET end_number = start_number 
                     + FLOOR(step_min + RAND() * (step_max – step_min));
    SET start_d    = first_d;
    WHILE start_d < last_d DO
      SET end_d = start_d 
                  + INTERVAL FLOOR(
                      days_actual_min 
                      + RAND() * (days_actual_max – days_actual_min)
                    ) DAY;
      INSERT INTO price(start_d, end_d, start_number, end_number) 
        VALUES(start_d, end_d, start_number, end_number);
      SET cnt = cnt + 1;
      SET start_d = end_d + INTERVAL 1 DAY;
    END WHILE;
    SET start_number = end_number + 1;
  END WHILE;
  
  RETURN cnt;
END$$
DELIMITER ;
SELECT fill_price(
    @first_d, @last_d, 30, 60, 10000000, 1000000000
  ) entries
FROM (
    SELECT @first_d := '2008-01-01' + INTERVAL 0 DAY first_d,
           @last_d  := '2010-12-31' + INTERVAL 0 DAY last_d
  ) v;
DROP FUNCTION fill_price;

SELECT @destination_number := FLOOR(RAND() * 99999999999) number, 
       @d := @first_d + INTERVAL FLOOR(
                                        RAND() 
                                        * DATEDIFF(@last_d, @first_d)
                                      ) DAY d
FROM (SELECT @desination_number := 0, @d := '0000-00-00') v;

EXPLAIN EXTENDED 
SELECT p.start_number, p.end_number, p.start_d, p.end_d, p.price
FROM price p
WHERE @destination_number BETWEEN p.start_number AND p.end_number
  AND @d                  BETWEEN p.start_d      AND p.end_d;

SELECT p.start_number, p.end_number, p.start_d, p.end_d, p.price
FROM price p
WHERE @destination_number BETWEEN p.start_number AND p.end_number
  AND @d                  BETWEEN p.start_d      AND p.end_d;

ALTER TABLE price 
  ENGINE = MyISAM,
  ADD COLUMN start_end_number_d LINESTRING NOT NULL;

UPDATE price p
SET p.start_end_number_d
      = LINESTRING(
          POINT(p.start_number, p.start_d), 
          POINT(p.end_number,   p.end_d)
        );

ALTER TABLE price 
  ADD SPATIAL INDEX(start_end_number_d);

EXPLAIN EXTENDED 
SELECT p.start_number, p.end_number, p.start_d, p.end_d, p.price
FROM price p
WHERE MBRContains(
        start_end_number_d, 
        POINT(@destination_number, DATE(@d))
      );

SELECT p.start_number, p.end_number, p.start_d, p.end_d, p.price
FROM price p
WHERE MBRContains(
        start_end_number_d, 
        POINT(@destination_number, DATE(@d))
      );

DROP TABLE price;
22 авг 10, 18:57    [9305479]     Ответить | Цитировать Сообщить модератору
 Re: Нахождение записей, где заданное значение находится между значениями полей  [new]
an0nym
Member

Откуда:
Сообщений: 7078
В стартовом посте был рассмотрен вопрос оптимизации запроса
SELECT ... FROM t WHERE :value BETWEEN col1 AND col2 AND :another_value BETWEEN col3 AND col4

Оптимизация подобным способом запроса вида
SELECT ... 
FROM t 
WHERE :value BETWEEN col1 AND col2 
  AND :another_value BETWEEN col3 AND col4 
  AND :another_value2 BETWEEN col5 AND col6 
  ... 
  AND :another_valueN BETWEEN col{N*2} AND col{N*2+1}
обычно не требуется, так как:
1. при наличии RTree индекса и проверки по нему любых двух условий, должно быть отобрано достаточно мало записей, чтобы остальные условия проверить не по индексу, а простым перебором, (по колонкам, участвующим в этих двух условиях, и следует построить LINESTRING колонку и RTree индекс по ней),
2. если п. 1 не исполняется и после отбора по индексу записей слишком много или нельзя выделить два условия, по которым всегда отбирается достаточно мало записей, то очень вероятна ошибка проектировщика или уникальный и интересный случай, который надо разобрать отдельно.

Нерассмотренным остался только самый простой вариант
SELECT ... FROM t WHERE :value BETWEEN col1 AND col2
Не буду рассматривать его также подробно, лишь скажу, что для его реализации абсциссу (или ординату) начала LINESTRING надо заменить на -1, а конца - на 1. Вот видоизмененный скрипт из первого поста, реализующий пример "Найти биржевые ордера, открытые до или в и закрытые в или после определенного времени":
+ mysql.exe
>mysql --host=localhost --user=... --password
Enter password: ...
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 6
Server version: 5.1.35-community-debug MySQL Community Server - Debug (GPL)

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> USE test;
Database changed
mysql> CREATE TABLE orders(
    ->   id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    ->   open_ts TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
    ->   close_ts TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
    ->   INDEX(open_ts),
    ->   INDEX(close_ts),
    ->   INDEX(open_ts, close_ts),
    ->   INDEX(close_ts, open_ts)
    -> ) ENGINE = InnoDB;
Query OK, 0 rows affected (0.09 sec)

mysql>
mysql> DELIMITER $$
mysql> CREATE FUNCTION fill_order(
    ->     first_ts TIMESTAMP,
    ->     last_ts  TIMESTAMP,
    ->     seconds_open_min INT,
    ->     seconds_open_max INT,
    ->     cnt INT
    ->   )
    ->   RETURNS INT
    -> BEGIN
    ->   DECLARE counter INT DEFAULT 0;
    ->
    ->   DECLARE open_ts TIMESTAMP DEFAULT first_ts;
    ->   DECLARE close_ts TIMESTAMP;
    ->
    ->   WHILE counter < cnt DO
    ->     IF open_ts >= last_ts THEN
    ->       SET open_ts = first_ts;
    ->     END IF;
    ->     SET close_ts = open_ts
    ->                  + INTERVAL FLOOR(
    ->                                   seconds_open_min
    ->                                   + RAND()
    ->                                     * (
    ->                                        seconds_open_max
    ->                                        - seconds_open_min
    ->                                       )
    ->                                  ) SECOND;
    ->     INSERT INTO orders(open_ts, close_ts) VALUES(open_ts, close_ts);
    ->     SET open_ts = close_ts + INTERVAL 1 SECOND;
    ->     SET counter = counter + 1;
    ->   END WHILE;
    ->
    ->   RETURN counter;
    -> END$$
Query OK, 0 rows affected (0.00 sec)

mysql> DELIMITER ;
mysql> SELECT fill_order(@first_ts, @last_ts, 10, 14400, 10000) entries
    -> FROM (
    ->     SELECT @first_ts := '2010-08-01 00:00:00' + INTERVAL 0 DAY first_ts,
    ->            @last_ts  := '2010-08-31 23:59:59' + INTERVAL 0 DAY last_ts
    ->   ) v;
+---------+
| entries |
+---------+
|   10000 |
+---------+
1 row in set (11.67 sec)

mysql> DROP FUNCTION fill_order;
Query OK, 0 rows affected (0.00 sec)

mysql>
mysql> SELECT @ts := FROM_UNIXTIME(
    ->                             UNIX_TIMESTAMP(@first_ts)
    ->                             + FLOOR(
    ->                                     RAND()
    ->                                     * (UNIX_TIMESTAMP(@last_ts)
    ->                                        - UNIX_TIMESTAMP(@first_ts))
    ->                                    )
    ->                            ) ts
    -> FROM (SELECT @ts := '0000-00-00 00:00:00' ts) v;
+---------------------+
| ts                  |
+---------------------+
| 2010-08-04 05:50:24 |
+---------------------+
1 row in set (0.00 sec)

mysql>
mysql> EXPLAIN EXTENDED
    -> SELECT o.id, o.open_ts, o.close_ts
    -> FROM orders o
    -> WHERE @ts BETWEEN o.open_ts AND o.close_ts;
+----+-------------+-------+-------+---------------------------------------+-----------+---------+------+------+----------+--------------------------+
| id | select_type | table | type  | possible_keys                         | key       | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+-------+-------+---------------------------------------+-----------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | o     | range | open_ts,close_ts,open_ts_2,close_ts_2 | open_ts_2 | 4       | NULL | 1954 |    56.14 | Using where; Using index |
+----+-------------+-------+-------+---------------------------------------+-----------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

mysql>
mysql> SELECT o.id, o.open_ts, o.close_ts
    -> FROM orders o
    -> WHERE @ts BETWEEN o.open_ts AND o.close_ts;
+------+---------------------+---------------------+
| id   | open_ts             | close_ts            |
+------+---------------------+---------------------+
| 3396 | 2010-08-04 02:22:02 | 2010-08-04 06:01:33 |
|   41 | 2010-08-04 02:25:28 | 2010-08-04 06:01:27 |
| 3026 | 2010-08-04 02:28:24 | 2010-08-04 06:02:18 |
| 6713 | 2010-08-04 02:35:07 | 2010-08-04 06:31:24 |
| 8583 | 2010-08-04 02:36:22 | 2010-08-04 05:52:38 |
| 4495 | 2010-08-04 03:12:39 | 2010-08-04 06:51:43 |
| 8971 | 2010-08-04 03:18:49 | 2010-08-04 06:09:47 |
| 7827 | 2010-08-04 03:37:14 | 2010-08-04 06:23:26 |
| 4137 | 2010-08-04 04:04:51 | 2010-08-04 06:35:22 |
| 6332 | 2010-08-04 04:10:27 | 2010-08-04 08:03:13 |
|  802 | 2010-08-04 04:14:13 | 2010-08-04 07:20:38 |
| 9731 | 2010-08-04 04:16:21 | 2010-08-04 06:38:11 |
| 9363 | 2010-08-04 04:16:49 | 2010-08-04 06:18:46 |
| 5246 | 2010-08-04 04:18:56 | 2010-08-04 08:15:42 |
| 4867 | 2010-08-04 04:21:30 | 2010-08-04 06:16:31 |
| 7076 | 2010-08-04 04:30:09 | 2010-08-04 05:54:03 |
| 2284 | 2010-08-04 04:45:28 | 2010-08-04 07:25:18 |
| 1177 | 2010-08-04 04:46:27 | 2010-08-04 08:33:55 |
| 5615 | 2010-08-04 04:48:57 | 2010-08-04 06:09:47 |
| 1924 | 2010-08-04 04:51:34 | 2010-08-04 07:01:12 |
| 2659 | 2010-08-04 05:01:37 | 2010-08-04 07:22:52 |
| 1552 | 2010-08-04 05:04:26 | 2010-08-04 06:32:52 |
| 7445 | 2010-08-04 05:10:03 | 2010-08-04 08:42:29 |
| 8204 | 2010-08-04 05:15:03 | 2010-08-04 06:45:12 |
| 3775 | 2010-08-04 05:25:36 | 2010-08-04 06:17:01 |
|  419 | 2010-08-04 05:32:45 | 2010-08-04 09:02:41 |
| 5977 | 2010-08-04 05:42:40 | 2010-08-04 08:42:56 |
+------+---------------------+---------------------+
27 rows in set (0.10 sec)

mysql>
mysql> ALTER TABLE orders
    ->   ENGINE = MyISAM,
    ->   ADD COLUMN open_close_ts LINESTRING NOT NULL;
Query OK, 10000 rows affected (1.30 sec)
Records: 10000  Duplicates: 0  Warnings: 0

mysql>
mysql> UPDATE orders o
    -> SET o.open_close_ts
    ->       = LINESTRING(
    ->           POINT(o.open_ts, -1),
    ->           POINT(o.close_ts, 1)
    ->         );
Query OK, 10000 rows affected (1.46 sec)
Rows matched: 10000  Changed: 10000  Warnings: 0

mysql>
mysql> ALTER TABLE orders
    ->   ADD SPATIAL INDEX(open_close_ts);
Query OK, 10000 rows affected (3.34 sec)
Records: 10000  Duplicates: 0  Warnings: 0

mysql>
mysql> EXPLAIN EXTENDED
    -> SELECT o.id, o.open_ts, o.close_ts
    -> FROM orders o
    -> WHERE MBRContains(o.open_close_ts, POINT(TIMESTAMP(@ts), 0));
+----+-------------+-------+-------+---------------+---------------+---------+------+------+----------+-------------+
| id | select_type | table | type  | possible_keys | key           | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+-------+---------------+---------------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | o     | range | open_close_ts | open_close_ts | 34      | NULL |    1 |   100.00 | Using where |
+----+-------------+-------+-------+---------------+---------------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql>
mysql> SELECT o.id, o.open_ts, o.close_ts
    -> FROM orders o
    -> WHERE MBRContains(o.open_close_ts, POINT(TIMESTAMP(@ts), 0));
+------+---------------------+---------------------+
| id   | open_ts             | close_ts            |
+------+---------------------+---------------------+
|   41 | 2010-08-04 02:25:28 | 2010-08-04 06:01:27 |
| 3026 | 2010-08-04 02:28:24 | 2010-08-04 06:02:18 |
| 3396 | 2010-08-04 02:22:02 | 2010-08-04 06:01:33 |
| 4495 | 2010-08-04 03:12:39 | 2010-08-04 06:51:43 |
| 6713 | 2010-08-04 02:35:07 | 2010-08-04 06:31:24 |
| 7076 | 2010-08-04 04:30:09 | 2010-08-04 05:54:03 |
| 7827 | 2010-08-04 03:37:14 | 2010-08-04 06:23:26 |
| 8204 | 2010-08-04 05:15:03 | 2010-08-04 06:45:12 |
| 8583 | 2010-08-04 02:36:22 | 2010-08-04 05:52:38 |
| 8971 | 2010-08-04 03:18:49 | 2010-08-04 06:09:47 |
| 9363 | 2010-08-04 04:16:49 | 2010-08-04 06:18:46 |
| 9731 | 2010-08-04 04:16:21 | 2010-08-04 06:38:11 |
|  419 | 2010-08-04 05:32:45 | 2010-08-04 09:02:41 |
|  802 | 2010-08-04 04:14:13 | 2010-08-04 07:20:38 |
| 1177 | 2010-08-04 04:46:27 | 2010-08-04 08:33:55 |
| 1552 | 2010-08-04 05:04:26 | 2010-08-04 06:32:52 |
| 1924 | 2010-08-04 04:51:34 | 2010-08-04 07:01:12 |
| 2284 | 2010-08-04 04:45:28 | 2010-08-04 07:25:18 |
| 2659 | 2010-08-04 05:01:37 | 2010-08-04 07:22:52 |
| 3775 | 2010-08-04 05:25:36 | 2010-08-04 06:17:01 |
| 4137 | 2010-08-04 04:04:51 | 2010-08-04 06:35:22 |
| 4867 | 2010-08-04 04:21:30 | 2010-08-04 06:16:31 |
| 5246 | 2010-08-04 04:18:56 | 2010-08-04 08:15:42 |
| 5615 | 2010-08-04 04:48:57 | 2010-08-04 06:09:47 |
| 5977 | 2010-08-04 05:42:40 | 2010-08-04 08:42:56 |
| 6332 | 2010-08-04 04:10:27 | 2010-08-04 08:03:13 |
| 7445 | 2010-08-04 05:10:03 | 2010-08-04 08:42:29 |
+------+---------------------+---------------------+
27 rows in set (0.00 sec)

mysql>
mysql> DROP TABLE orders;
Query OK, 0 rows affected (0.00 sec)

mysql> exit
Bye
+ queries
USE test;
CREATE TABLE orders(
  id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  open_ts TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
  close_ts TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
  INDEX(open_ts),
  INDEX(close_ts),
  INDEX(open_ts, close_ts),
  INDEX(close_ts, open_ts)
) ENGINE = InnoDB;

DELIMITER $$
CREATE FUNCTION fill_order(
    first_ts TIMESTAMP,
    last_ts  TIMESTAMP,
    seconds_open_min INT, 
    seconds_open_max INT,
    cnt INT
  )
  RETURNS INT
BEGIN
  DECLARE counter INT DEFAULT 0;
  
  DECLARE open_ts TIMESTAMP DEFAULT first_ts;
  DECLARE close_ts TIMESTAMP;
  
  WHILE counter < cnt DO
    IF open_ts >= last_ts THEN
      SET open_ts = first_ts;
    END IF;
    SET close_ts = open_ts 
                 + INTERVAL FLOOR(
                                  seconds_open_min 
                                  + RAND() 
                                    * (
                                       seconds_open_max 
                                       - seconds_open_min
                                      )
                                 ) SECOND;
    INSERT INTO orders(open_ts, close_ts) VALUES(open_ts, close_ts);
    SET open_ts = close_ts + INTERVAL 1 SECOND;
    SET counter = counter + 1;
  END WHILE;

  RETURN counter;
END$$
DELIMITER ;
SELECT fill_order(@first_ts, @last_ts, 10, 14400, 10000) entries
FROM (
    SELECT @first_ts := '2010-08-01 00:00:00' + INTERVAL 0 DAY first_ts,
           @last_ts  := '2010-08-31 23:59:59' + INTERVAL 0 DAY last_ts
  ) v;
DROP FUNCTION fill_order;

SELECT @ts := FROM_UNIXTIME(
                            UNIX_TIMESTAMP(@first_ts) 
                            + FLOOR(
                                    RAND() 
                                    * (UNIX_TIMESTAMP(@last_ts) 
                                       - UNIX_TIMESTAMP(@first_ts))
                                   )
                           ) ts
FROM (SELECT @ts := '0000-00-00 00:00:00' ts) v;

EXPLAIN EXTENDED 
SELECT o.id, o.open_ts, o.close_ts
FROM orders o
WHERE @ts BETWEEN o.open_ts AND o.close_ts;

SELECT o.id, o.open_ts, o.close_ts
FROM orders o
WHERE @ts BETWEEN o.open_ts AND o.close_ts;

ALTER TABLE orders
  ENGINE = MyISAM,
  ADD COLUMN open_close_ts LINESTRING NOT NULL;

UPDATE orders o
SET o.open_close_ts
      = LINESTRING(
          POINT(o.open_ts, -1), 
          POINT(o.close_ts, 1)
        );

ALTER TABLE orders
  ADD SPATIAL INDEX(open_close_ts);

EXPLAIN EXTENDED 
SELECT o.id, o.open_ts, o.close_ts
FROM orders o
WHERE MBRContains(o.open_close_ts, POINT(TIMESTAMP(@ts), 0));

SELECT o.id, o.open_ts, o.close_ts
FROM orders o
WHERE MBRContains(o.open_close_ts, POINT(TIMESTAMP(@ts), 0));

DROP TABLE orders;
22 авг 10, 19:48    [9305548]     Ответить | Цитировать Сообщить модератору
 Re: Нахождение записей, где заданное значение находится между значениями полей  [new]
miksoft
Member

Откуда:
Сообщений: 36390
an0nym
Выяснить принадлежность IP адреса определенному региону по БД GeoIP.
В данном случае, если гарантированно известно, что диапазоны в таблице не пересекаются, и если нужно искать "мгновенное" событие, запрос оптимизируется следующим образом:
SELECT ... FROM t WHERE :value >= col1 ORDER BY col1 DESC LIMIT 1
Потребуется обычный индекс по полю col1. Сойдет и многоколоночный индекс, начинающийся с поля col1, но производительность может быть хуже.
Также в данном случае необязательно переводить таблицу в MyISAM. С InnoDB должно работать тоже.

В случае, если диапазоны в таблице идут не подряд (с пропусками), то нужно не забыть дополнительно проверить полученный результат на условие :value <= col2. Это можно сделать как на клиенте, так и запросом:
SELECT ...
FROM 
  (SELECT ..., col2 FROM t WHERE :value >= col1 ORDER BY col1 DESC LIMIT 1) tt
WHERE :value <= tt.col2
23 авг 10, 13:04    [9308178]     Ответить | Цитировать Сообщить модератору
 Re: Нахождение записей, где заданное значение находится между значениями полей  [new]
javajdbc
Member [заблокирован]

Откуда: Montreal
Сообщений: 17798
miksoft,

OFFTOPIC:

хороший пост, интуитивно понятно что нахождение
по двум интервалам -- ето поиск прямоугольника на
плоской поверхности. Т.е. аналог с ГЕО простои прямой.

Аналогично поиск по Н интервалам -- есть поиск
Н-мерного кирпича в Н-мерном пространстве.
Как показал an0nym -- при Н>2 это
теряет практический смысл т.к.
строить 3-мерные (Н-мерные) индексы -- сложно
(если вообше возможно...?)
27 авг 10, 16:38    [9339488]     Ответить | Цитировать Сообщить модератору
 Re: Нахождение записей, где заданное значение находится между значениями полей  [new]
an0nym
Member

Откуда:
Сообщений: 7078
javajdbc
miksoft,

OFFTOPIC:

хороший пост, интуитивно понятно что нахождение
по двум интервалам -- ето поиск прямоугольника на
плоской поверхности. Т.е. аналог с ГЕО простои прямой.

Аналогично поиск по Н интервалам -- есть поиск
Н-мерного кирпича в Н-мерном пространстве.
Как показал an0nym -- при Н>2 это
теряет практический смысл т.к.
строить 3-мерные (Н-мерные) индексы -- сложно
(если вообше возможно...?)

Сомневаюсь, что это необходимо. Извратиться, думаю можно, но сама необходимости будет указывать на некоторую ошибку в проектировке (другими словами, я, например, не могу представить себе правильно спроектированное что-то, где это может понадобиться).
27 авг 10, 22:07    [9341007]     Ответить | Цитировать Сообщить модератору
 Re: Нахождение записей, где заданное значение находится между значениями полей  [new]
miksoft
Member

Откуда:
Сообщений: 36390
an0nym
Сомневаюсь, что это необходимо.
Хм, как насчет подбора товара по нескольким диапазонным характеристикам? Яндекс.Маркет, например?
29 авг 10, 20:32    [9345845]     Ответить | Цитировать Сообщить модератору
 Re: Нахождение записей, где заданное значение находится между значениями полей  [new]
ПЕНСИОНЕРКА
Member

Откуда: Владимирская обл
Сообщений: 4083
an0nym,

автор
Задача
Быстро найти записи, где заданное (пользователем) значение находится между значениями полей.


вижу прямую связь с приемом, который я изложила в посекудной базе

данные по форме счетчик,номер, время нач,время кон переводятся в рабоч табл

счномер время нач
+1сек
+2сек
время кон


из нее по заданному времени делаются
-стадартный выбор с уплотнением по сч,номер
-стандартная стыковка с основным массивом по сч и справочниками
30 авг 10, 09:07    [9347070]     Ответить | Цитировать Сообщить модератору
 Re: Нахождение записей, где заданное значение находится между значениями полей  [new]
kestrel
Member

Откуда: Москва
Сообщений: 84
miksoft
an0nym
Выяснить принадлежность IP адреса определённому региону по БД GeoIP.
В данном случае, если гарантированно известно, что диапазоны в таблице не пересекаются, и если нужно искать "мгновенное" событие, запрос оптимизируется следующим образом:
SELECT ... FROM t WHERE :value >= col1 ORDER BY col1 LIMIT 1

По-моему, к ORDER BY надо добавить DESC, иначе находиться будет всегда самый 1-й диапазон.
22 окт 10, 16:44    [9659082]     Ответить | Цитировать Сообщить модератору
 Re: Нахождение записей, где заданное значение находится между значениями полей  [new]
miksoft
Member

Откуда:
Сообщений: 36390
kestrel
miksoft
an0nym
Выяснить принадлежность IP адреса определённому региону по БД GeoIP.
В данном случае, если гарантированно известно, что диапазоны в таблице не пересекаются, и если нужно искать "мгновенное" событие, запрос оптимизируется следующим образом:
SELECT ... FROM t WHERE :value >= col1 ORDER BY col1 LIMIT 1

По-моему, к ORDER BY надо добавить DESC, иначе находиться будет всегда самый 1-й диапазон.
Да, совершенно верно! Спасибо за поправку.
22 окт 10, 16:51    [9659170]     Ответить | Цитировать Сообщить модератору
 Re: Нахождение записей, где заданное значение находится между значениями полей  [new]
kestrel
Member

Откуда: Москва
Сообщений: 84
an0nym
Использование RTree индекса

Стоит отметить, что такое решение подвержено багу, который был исправлен в 5.0.82, 5.1.35, 6.0.12.
У меня 5.0.67 перегружается при попытке создать SPATIAL индекс.
22 окт 10, 17:04    [9659301]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Нахождение записей, где заданное значение находится между значениями полей  [new]
miksoft
Member

Откуда:
Сообщений: 36390
Ссылка на тему:
MySQL: оптимизация конструкции between
2 авг 11, 15:03    [11059294]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Нахождение записей, где заданное значение находится между значениями полей  [new]
an0nym
Member

Откуда:
Сообщений: 7078
Еще задача, смежная с обсуждаемой: нахождение точек в двумерном пространстве, находящихся на заданной удаленности от заданной точки.
8 окт 11, 17:38    [11407251]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Нахождение записей, где заданное значение находится между значениями полей  [new]
Nivans
Member

Откуда:
Сообщений: 1
Прочитал статью, но всё равно немного не понял (наверно сказывается 2 часа сна ;) ).

Вопрос: мне нужно сделать выборку из базы данных основываясь на большом количестве параметров. (фильтр каталога техники (PHP)).

Я предполагаю, что нужно делать так:

$query = mysql_query("SELECT id FROM table_name WHERE rate >= 100, wifi = 1 и т.п. ");

всё ли верно в запросе и синтаксисе?

и как сделать такой запрос: WHERE имя_колонки от 100 до 200


Заранее спасибо за помощь.
15 июл 12, 19:12    [12869706]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Нахождение записей, где заданное значение находится между значениями полей  [new]
ScareCrow
Member

Откуда: Белый город
Сообщений: 15772
miksoft
an0nym
Сомневаюсь, что это необходимо.
Хм, как насчет подбора товара по нескольким диапазонным характеристикам? Яндекс.Маркет, например?

для этого битмап индесы придумали.
15 июл 12, 20:35    [12869853]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Нахождение записей, где заданное значение находится между значениями полей  [new]
an0nym
Member

Откуда:
Сообщений: 7078
Возможно кого-то натолкнет на новые мысли. С 5.6 можно будет делать так
+
$ mysql
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 11
Server version: 5.6.5-m8 MySQL Community Server (GPL)

Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> USE test;
Database changed
mysql> CREATE TABLE test(
    ->   field0 INT NOT NULL,
    ->   field1 INT NOT NULL,
    ->   field2 INT NOT NULL,
    ->   field3 INT NOT NULL,
    ->   field4 INT NOT NULL,
    ->   field5 INT NOT NULL,
    ->   field6 INT NOT NULL,
    ->   field7 INT NOT NULL,
    ->   field8 INT NOT NULL,
    ->   field9 INT NOT NULL,
    ->   start_uts INT NOT NULL,
    ->   end_uts INT NOT NULL
    -> ) ENGINE = MyISAM
    -> SELECT RAND() * 1024 field0,
    ->   RAND() * 1024 field1,
    ->   RAND() * 1024 field2,
    ->   RAND() * 1024 field3,
    ->   RAND() * 1024 field4,
    ->   RAND() * 1024 field5,
    ->   RAND() * 1024 field6,
    ->   RAND() * 1024 field7,
    ->   RAND() * 1024 field8,
    ->   RAND() * 1024 field9,
    ->   RAND() * (~0 >> 33) start_uts,
    ->   0 end_uts
    -> FROM (SELECT null UNION ALL SELECT null) t01,
    ->   (SELECT null UNION ALL SELECT null) t02,
    ->   (SELECT null UNION ALL SELECT null) t03,
    ->   (SELECT null UNION ALL SELECT null) t04,
    ->   (SELECT null UNION ALL SELECT null) t05,
    ->   (SELECT null UNION ALL SELECT null) t06,
    ->   (SELECT null UNION ALL SELECT null) t07,
    ->   (SELECT null UNION ALL SELECT null) t08,
    ->   (SELECT null UNION ALL SELECT null) t09,
    ->   (SELECT null UNION ALL SELECT null) t10,
    ->   (SELECT null UNION ALL SELECT null) t11,
    ->   (SELECT null UNION ALL SELECT null) t12,
    ->   (SELECT null UNION ALL SELECT null) t13,
    ->   (SELECT null UNION ALL SELECT null) t14,
    ->   (SELECT null UNION ALL SELECT null) t15,
    ->   (SELECT null UNION ALL SELECT null) t16,
    ->   (SELECT null UNION ALL SELECT null) t17,
    ->   (SELECT null UNION ALL SELECT null) t18,
    ->   (SELECT null UNION ALL SELECT null) t19,
    ->   (SELECT null UNION ALL SELECT null) t20;
Query OK, 1048576 rows affected (1,76 sec)
Records: 1048576  Duplicates: 0  Warnings: 0

mysql> UPDATE test
    -> SET end_uts = start_uts + RAND() * ((~0 >> 33) - start_uts);
Query OK, 1048576 rows affected (1,41 sec)
Rows matched: 1048576  Changed: 1048576  Warnings: 0

mysql> ALTER TABLE test ADD ix MULTILINESTRING;
Query OK, 1048576 rows affected (0,83 sec)
Records: 1048576  Duplicates: 0  Warnings: 0

mysql> UPDATE test SET ix = MultiLinestring(
    ->     Linestring(Point(start_uts, field0 + 0.0), Point(end_uts, field0 + 0.0)),
    ->     Linestring(Point(start_uts, field1 + 0.1), Point(end_uts, field1 + 0.1)),
    ->     Linestring(Point(start_uts, field2 + 0.2), Point(end_uts, field2 + 0.2)),
    ->     Linestring(Point(start_uts, field3 + 0.3), Point(end_uts, field3 + 0.3)),
    ->     Linestring(Point(start_uts, field4 + 0.4), Point(end_uts, field4 + 0.4)),
    ->     Linestring(Point(start_uts, field5 + 0.5), Point(end_uts, field5 + 0.5)),
    ->     Linestring(Point(start_uts, field6 + 0.6), Point(end_uts, field6 + 0.6)),
    ->     Linestring(Point(start_uts, field7 + 0.7), Point(end_uts, field7 + 0.7)),
    ->     Linestring(Point(start_uts, field8 + 0.8), Point(end_uts, field8 + 0.8)),
    ->     Linestring(Point(start_uts, field9 + 0.9), Point(end_uts, field9 + 0.9))
    ->   );
Query OK, 1048576 rows affected (20,66 sec)
Rows matched: 1048576  Changed: 1048576  Warnings: 0

mysql> ALTER TABLE test MODIFY ix MULTILINESTRING NOT NULL, ADD SPATIAL INDEX(ix);
Query OK, 1048576 rows affected (16,63 sec)
Records: 1048576  Duplicates: 0  Warnings: 0

mysql> EXPLAIN EXTENDED SELECT SQL_NO_CACHE COUNT(*)
    -> FROM test
    -> WHERE ST_Touches(
    ->     ix,
    ->     MultiPoint(
    ->       Point(UNIX_TIMESTAMP(), 500 + 0.0),
    ->       Point(UNIX_TIMESTAMP(), 500 + 0.1),
    ->       Point(UNIX_TIMESTAMP(), 500 + 0.2),
    ->       Point(UNIX_TIMESTAMP(), 500 + 0.3),
    ->       Point(UNIX_TIMESTAMP(), 500 + 0.4),
    ->       Point(UNIX_TIMESTAMP(), 500 + 0.5),
    ->       Point(UNIX_TIMESTAMP(), 500 + 0.6),
    ->       Point(UNIX_TIMESTAMP(), 500 + 0.7),
    ->       Point(UNIX_TIMESTAMP(), 500 + 0.8),
    ->       Point(UNIX_TIMESTAMP(), 500 + 0.9)
    ->     )
    ->   );
+----+-------------+-------+-------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+-------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | test  | range | ix            | ix   | 34      | NULL |    1 |   100.00 | Using where |
+----+-------------+-------+-------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0,00 sec)

mysql> SELECT SQL_NO_CACHE COUNT(*)
    -> FROM test
    -> WHERE ST_Touches(
    ->     ix,
    ->     MultiPoint(
    ->       Point(UNIX_TIMESTAMP(), 500 + 0.0),
    ->       Point(UNIX_TIMESTAMP(), 500 + 0.1),
    ->       Point(UNIX_TIMESTAMP(), 500 + 0.2),
    ->       Point(UNIX_TIMESTAMP(), 500 + 0.3),
    ->       Point(UNIX_TIMESTAMP(), 500 + 0.4),
    ->       Point(UNIX_TIMESTAMP(), 500 + 0.5),
    ->       Point(UNIX_TIMESTAMP(), 500 + 0.6),
    ->       Point(UNIX_TIMESTAMP(), 500 + 0.7),
    ->       Point(UNIX_TIMESTAMP(), 500 + 0.8),
    ->       Point(UNIX_TIMESTAMP(), 500 + 0.9)
    ->     )
    ->   );
+----------+
| COUNT(*) |
+----------+
|     3749 |
+----------+
1 row in set (3,83 sec)

mysql> SELECT SQL_NO_CACHE COUNT(*)
    -> FROM test
    -> WHERE UNIX_TIMESTAMP() BETWEEN start_uts AND end_uts
    ->   AND (
    ->     field0 = 500
    ->     OR field1 = 500
    ->     OR field2 = 500
    ->     OR field3 = 500
    ->     OR field4 = 500
    ->     OR field5 = 500
    ->     OR field6 = 500
    ->     OR field7 = 500
    ->     OR field8 = 500
    ->     OR field9 = 500
    ->   );
+----------+
| COUNT(*) |
+----------+
|     3749 |
+----------+
1 row in set (0,46 sec)

mysql> exit
Bye
В данном конкретном случае работает медленнее чем FTS. Есть ли области применения такого решения?
17 июл 12, 13:33    [12877542]     Ответить | Цитировать Сообщить модератору
Между сообщениями интервал более 1 года.
 Re: FAQ: Нахождение записей, где заданное значение находится между значениями полей  [new]
miksoft
Member

Откуда:
Сообщений: 36390
an0nym
Речь идет о (геометрических) spatial-индексах для MyISAM таблиц. К сожалению, для других типов таблиц данные индексы не поддерживаются и скорее всего в ближайшее время не будут.
Будущее наступило.
http://dev.mysql.com/doc/refman/5.7/en/create-index.html
Available only for MyISAM and (as of MySQL 5.7.5) InnoDB tables.
11 дек 16, 23:22    [19991071]     Ответить | Цитировать Сообщить модератору
Все форумы / MySQL Ответить