I w * sh, чтобы добавить к моему запросу на выбор некоторый код для поиска с целым числом в диапазоне 500-2359 для совпадения. Без указания имени каждого столбца, если это возможно (так как запрос станет огромным).
Все последние 63 столбца являются int (4) и имеют имена от bus-time01 до bus-time63.
Эти столбцы должны иметь go.
. Вы храните список значений в виде ряда строк, и это делает их очень трудными для поиска на равных. Списки в SQL реализованы в виде таблиц объединения .
create table bus_lines (
id serial primary key,
name varchar(255) not null unique
);
create table bus_stops (
id serial primary key,
bus_line_id integer not null references bus_lines(id),
name varchar(255) not null unique
);
create table bus_times (
bus_stop_id integer references bus_stops(id),
bus_time time not null
);
Я использую время , а не целое число, потому что оно приносит кучу функции, которые могут быть полезны .
Каждая линия шины имеет много bus_stops. Каждый bus_stops имеет много bus_times. Чтобы найти все времена для одной остановки, по имени, в порядке.
select bus_time
from bus_times
join bus_stops on bus_stops.id = bus_times.bus_stop_id
where bus_stops.name = ?
order by bus_times.bus_time asc
Если вы хотите найти, какие остановки имеют определенное время ...
select bus_stops.name
from bus_times
join bus_stops on bus_stops.id = bus_times.bus_stop_id
where bus_times.bus_time = ?
Без всех эти повторяющиеся столбцы на пути, эта и многие другие проблемы намного проще.
Если вы новичок в SQL, объединения и структурирование данных, поскольку отношения являются наиболее изнурительной частью.
I sh, чтобы найти наиболее близкое совпадение с этим целым числом в каждой строке.
Если нет точного совпадения, то следующее наибольшее число.
После совпадения найден Я хотел бы, чтобы значение столбца находилось слева и справа от найденного.
Здесь есть две части. Во-первых, поиск ближайшего совпадения. Во-вторых, получение предыдущего и следующего.
Вторая часть проста с оконными функциями . Оконные функции подобны скользящему окну вокруг строки результатов, чтобы увидеть, что находится до и после, называемые «отставание» и «отрыв» .
Мы установили до нашей оконной рамы. Мы должны определить порядок для «предыдущего» и «следующего» с помощью order by bus_time asc
, а также обеспечить выбор только из одного и того же останова с partition by bus_stop_id
.
select
bus_stops.name,
lag(bus_time) over w,
bus_time,
lead(bus_time) over w
from bus_times
join bus_stops on bus_stops.id = bus_times.bus_stop_id
window w as (
partition by bus_stop_id
order by bus_time asc
);
+------+----------------------+----------+-----------------------+
| name | lag(bus_time) over w | bus_time | lead(bus_time) over w |
+------+----------------------+----------+-----------------------+
| A | NULL | 05:01:00 | 05:39:00 |
| A | 05:01:00 | 05:39:00 | 11:49:00 |
| A | 05:39:00 | 11:49:00 | 21:38:00 |
| A | 11:49:00 | 21:38:00 | NULL |
| B | NULL | 05:31:00 | NULL |
+------+----------------------+----------+-----------------------+
Для оконных функций требуется MySQL 8,0. Вы можете сделать это без оконных функций, но это сложнее. MySQL 8 приносит много хороших вещей, и его стоит обновить.
Теперь нам нужно найти первую строку, которая соответствует времени или идет после него. Это просто.
select bus_stops.name, bus_time
from bus_times
join bus_stops on bus_stops.id = bus_times.bus_stop_id
where bus_time >= '05:30'
order by bus_time
limit 1;
+------+----------+
| name | bus_time |
+------+----------+
| A | 05:39:00 |
+------+----------+
Но нам также нужны остановки до и после. Вот где мы сталкиваемся с проблемой.
select
bus_stops.name,
lag(bus_time) over w,
bus_time,
lead(bus_time) over w
from bus_times
join bus_stops on bus_stops.id = bus_times.bus_stop_id
where bus_time >= '05:30'
window w as (
partition by bus_stop_id
order by bus_time asc
)
limit 1
+------+----------------------+----------+-----------------------+
| name | lag(bus_time) over w | bus_time | lead(bus_time) over w |
+------+----------------------+----------+-----------------------+
| A | NULL | 05:39:00 | 11:49:00 |
+------+----------------------+----------+-----------------------+
window
работает над результатами, и where (bus_time - '0530') >= 0
исключает любое время до желаемого времени.
Нет проблем, мы просто искать правильное отставание и опережать!
select
bus_stops.name,
lag(bus_time) over w,
bus_time,
lead(bus_time) over w
from bus_times
join bus_stops on bus_stops.id = bus_times.bus_stop_id
where lag(bus_time) < '05:30'
and bus_time >= '05:30'
and lead(bus_time) > '05:30'
window w as (
partition by bus_stop_id
order by bus_time asc
)
limit 1
Нет. Вы не можете использовать оконные функции в предложении where
в MySQL.
Вместо этого мы сначала вычисляем отставание и опережаем, а затем запрашиваем это. Вы можете сделать это, используя производную таблицу , но я предпочитаю общие табличные выражения , так как они имеют меньше ограничений и легче увидеть, как создается запрос.
-- Create a little virtual able with the bus time
-- and their previous and next times.
with around_bus_times as (
select
bus_stop_id,
lag(bus_time) over w as 'prev',
bus_time,
lead(bus_time) over w as 'next'
from bus_times
window w as (
partition by bus_stop_id
order by bus_time asc
)
)
-- Read from that table
select
bus_stops.name,
prev,
bus_time,
next
from around_bus_times
join bus_stops on bus_stops.id = around_bus_times.bus_stop_id
where prev < '05:30'
and bus_time >= '05:30'
and next > '05:30'
+------+----------+----------+----------+
| name | prev | bus_time | next |
+------+----------+----------+----------+
| A | 05:01:00 | 05:39:00 | 11:49:00 |
+------+----------+----------+----------+
Последний штрих - это то, что происходит, если совпадающим временем является время первого или последнего автобуса, и оно не имеет предыдущего или следующего. prev
или next
может быть нулем, а сравнение нуля всегда ложно. Поэтому мы должны принять эту возможность во внимание.
with around_bus_times as (
select
bus_stop_id,
lag(bus_time) over w as 'prev',
bus_time,
lead(bus_time) over w as 'next'
from bus_times
window w as (
partition by bus_stop_id
order by bus_time asc
)
)
select
bus_stops.name,
prev,
bus_time,
next
from around_bus_times
join bus_stops on bus_stops.id = around_bus_times.bus_stop_id
where (prev is null or prev < '05:01')
and bus_time >= '05:01'
and (next is null or next > '05:01')
+------+------+----------+----------+
| name | prev | bus_time | next |
+------+------+----------+----------+
| A | NULL | 05:01:00 | 05:39:00 |
| B | NULL | 05:31:00 | NULL |
+------+------+----------+----------+
Если у вас есть ночные автобусы, которые ходят после полуночи, они не смогут предложить их в качестве следующего. Это еще одна проблема.
Это много, но я надеюсь, что вы сможете следить за его поэтапным построением.