powered by simpleCommunicator - 2.0.59     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / MySQL [игнор отключен] [закрыт для гостей] / FAQ: Нахождение записей, где заданное значение находится между значениями полей
16 сообщений из 16, страница 1 из 1
FAQ: Нахождение записей, где заданное значение находится между значениями полей
    #36805134
an0nym
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Задача
Быстро найти записи, где заданное (пользователем) значение находится между значениями полей.

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

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

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


Реализация
на примере "Выяснить тариф звонка по определенному номеру назначения в определенный момент времени (если хранятся все изменения тарифов во времени)"
+
Подготовим данные
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
>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)

Заполним произвольными данными
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
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 всё равно приходится просматривать достаточное количество записей, из-за этого производительности в большинстве случаев недостаточно
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
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 колонку
Код: plaintext
1.
2.
3.
4.
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 
Заполним её нужными значениями.
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
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-индекс.
Код: plaintext
1.
2.
3.
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-индекс идеально подходит для такого типа запросов, просматривается всегда ровно столько строк, сколько будет в результате запроса.
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
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)

+
Одним скриптом
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
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;
...
Рейтинг: 0 / 0
FAQ: Нахождение записей, где заданное значение находится между значениями полей
    #36805153
an0nym
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В стартовом посте был рассмотрен вопрос оптимизации запроса
Код: plaintext
SELECT ... FROM t WHERE :value BETWEEN col1 AND col2 AND :another_value BETWEEN col3 AND col4

Оптимизация подобным способом запроса вида
Код: plaintext
1.
2.
3.
4.
5.
6.
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 не исполняется и после отбора по индексу записей слишком много или нельзя выделить два условия, по которым всегда отбирается достаточно мало записей, то очень вероятна ошибка проектировщика или уникальный и интересный случай, который надо разобрать отдельно.

Нерассмотренным остался только самый простой вариант
Код: plaintext
SELECT ... FROM t WHERE :value BETWEEN col1 AND col2
Не буду рассматривать его также подробно, лишь скажу, что для его реализации абсциссу (или ординату) начала LINESTRING надо заменить на -1, а конца - на 1. Вот видоизмененный скрипт из первого поста, реализующий пример "Найти биржевые ордера, открытые до или в и закрытые в или после определенного времени":
mysql.exe
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
>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
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
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;
...
Рейтинг: 0 / 0
FAQ: Нахождение записей, где заданное значение находится между значениями полей
    #36806081
miksoft
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
an0nymВыяснить принадлежность IP адреса определенному региону по БД GeoIP.В данном случае, если гарантированно известно, что диапазоны в таблице не пересекаются, и если нужно искать "мгновенное" событие, запрос оптимизируется следующим образом:
Код: plaintext
SELECT ... FROM t WHERE :value >= col1 ORDER BY col1 DESC LIMIT  1 
Потребуется обычный индекс по полю col1. Сойдет и многоколоночный индекс, начинающийся с поля col1, но производительность может быть хуже.
Также в данном случае необязательно переводить таблицу в MyISAM. С InnoDB должно работать тоже.

В случае, если диапазоны в таблице идут не подряд (с пропусками), то нужно не забыть дополнительно проверить полученный результат на условие :value <= col2 . Это можно сделать как на клиенте, так и запросом:
Код: plaintext
1.
2.
3.
SELECT ...
FROM 
  (SELECT ..., col2 FROM t WHERE :value >= col1 ORDER BY col1 DESC LIMIT  1 ) tt
WHERE :value <= tt.col2
...
Рейтинг: 0 / 0
FAQ: Нахождение записей, где заданное значение находится между значениями полей
    #36816050
Фотография javajdbc
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
miksoft,

OFFTOPIC:

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

Аналогично поиск по Н интервалам -- есть поиск
Н-мерного кирпича в Н-мерном пространстве.
Как показал an0nym -- при Н>2 это
теряет практический смысл т.к.
строить 3-мерные (Н-мерные) индексы -- сложно
(если вообше возможно...?)
...
Рейтинг: 0 / 0
FAQ: Нахождение записей, где заданное значение находится между значениями полей
    #36816538
an0nym
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
javajdbcmiksoft,

OFFTOPIC:

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

Аналогично поиск по Н интервалам -- есть поиск
Н-мерного кирпича в Н-мерном пространстве.
Как показал an0nym -- при Н>2 это
теряет практический смысл т.к.
строить 3-мерные (Н-мерные) индексы -- сложно
(если вообше возможно...?)
Сомневаюсь, что это необходимо. Извратиться, думаю можно, но сама необходимости будет указывать на некоторую ошибку в проектировке (другими словами, я, например, не могу представить себе правильно спроектированное что-то, где это может понадобиться).
...
Рейтинг: 0 / 0
FAQ: Нахождение записей, где заданное значение находится между значениями полей
    #36817915
miksoft
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
an0nymСомневаюсь, что это необходимо.Хм, как насчет подбора товара по нескольким диапазонным характеристикам? Яндекс.Маркет, например?
...
Рейтинг: 0 / 0
FAQ: Нахождение записей, где заданное значение находится между значениями полей
    #36818229
Фотография ПЕНСИОНЕРКА
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
an0nym,

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

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

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

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

из нее по заданному времени делаются
-стадартный выбор с уплотнением по сч,номер
-стандартная стыковка с основным массивом по сч и справочниками
...
Рейтинг: 0 / 0
FAQ: Нахождение записей, где заданное значение находится между значениями полей
    #36915086
kestrel
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
miksoftan0nymВыяснить принадлежность IP адреса определённому региону по БД GeoIP.В данном случае, если гарантированно известно, что диапазоны в таблице не пересекаются, и если нужно искать "мгновенное" событие, запрос оптимизируется следующим образом:
Код: plaintext
SELECT ... FROM t WHERE :value >= col1 ORDER BY col1 LIMIT  1 

По-моему, к ORDER BY надо добавить DESC, иначе находиться будет всегда самый 1-й диапазон.
...
Рейтинг: 0 / 0
FAQ: Нахождение записей, где заданное значение находится между значениями полей
    #36915106
miksoft
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
kestrelmiksoftan0nymВыяснить принадлежность IP адреса определённому региону по БД GeoIP.В данном случае, если гарантированно известно, что диапазоны в таблице не пересекаются, и если нужно искать "мгновенное" событие, запрос оптимизируется следующим образом:
Код: plaintext
SELECT ... FROM t WHERE :value >= col1 ORDER BY col1 LIMIT  1 

По-моему, к ORDER BY надо добавить DESC, иначе находиться будет всегда самый 1-й диапазон.Да, совершенно верно! Спасибо за поправку.
...
Рейтинг: 0 / 0
FAQ: Нахождение записей, где заданное значение находится между значениями полей
    #36915133
kestrel
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
an0nymИспользование RTree индекса
Стоит отметить, что такое решение подвержено багу , который был исправлен в 5.0.82, 5.1.35, 6.0.12.
У меня 5.0.67 перегружается при попытке создать SPATIAL индекс.
...
Рейтинг: 0 / 0
FAQ: Нахождение записей, где заданное значение находится между значениями полей
    #37377055
miksoft
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
FAQ: Нахождение записей, где заданное значение находится между значениями полей
    #37474066
an0nym
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
FAQ: Нахождение записей, где заданное значение находится между значениями полей
    #37879917
Nivans
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Прочитал статью, но всё равно немного не понял (наверно сказывается 2 часа сна ;) ).

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

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

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

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

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


Заранее спасибо за помощь.
...
Рейтинг: 0 / 0
FAQ: Нахождение записей, где заданное значение находится между значениями полей
    #37879939
Фотография ScareCrow
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
miksoftan0nymСомневаюсь, что это необходимо.Хм, как насчет подбора товара по нескольким диапазонным характеристикам? Яндекс.Маркет, например?
для этого битмап индесы придумали.
...
Рейтинг: 0 / 0
FAQ: Нахождение записей, где заданное значение находится между значениями полей
    #37882145
an0nym
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Возможно кого-то натолкнет на новые мысли. С 5.6 можно будет делать так
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
$ 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. Есть ли области применения такого решения?
...
Рейтинг: 0 / 0
Период между сообщениями больше года.
FAQ: Нахождение записей, где заданное значение находится между значениями полей
    #39364562
miksoft
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
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 .
...
Рейтинг: 0 / 0
16 сообщений из 16, страница 1 из 1
Форумы / MySQL [игнор отключен] [закрыт для гостей] / FAQ: Нахождение записей, где заданное значение находится между значениями полей
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]