Как в MySQl как получить наиболее близкое соответствие к переменной и получить только эти данные - PullRequest
1 голос
/ 05 апреля 2020

Редактировать:

Обобщенный вопрос:

База данных содержит 1 таблицу с 70 столбцами и 20 строками.

Все последние 63 столбца являются целыми (4) и имеют именованную шину -time01 - bus-time63.

Минимальное значение для данных в этих столбцах равно 0. Максимальное значение равно 2359.

Любое значение, большее 0, будет минимальным значением 500.

Все значения столбца увеличиваются в каждой строке вверх (например, 0501, 0539, 1149, 2138).

Никогда вниз и никогда не идентичные числа в одной строке (но другие строки могут содержать одинаковые значения).

Я выполняю запрос на выборку, который сокращает количество строк до 1-4.

I wi sh, чтобы добавить к моему запросу для выбора некоторый код для поиска с целым числом от 500 до 2359 для матч. Без указания каждого имени столбца, если это возможно (так как запрос станет огромным)

I sh, чтобы найти наиболее близкое совпадение с этим целым числом в каждой строке.

Если нет точного совпадения, то следующий наибольшее число.

Как только совпадение найдено, я бы хотел, чтобы значение столбца находилось слева и справа от найденного.

Мне не нужно возвращать имена столбцов (если это проще), только возвращаемые значения.

Можно ли сделать это с помощью запроса?

Подробный вопрос:

У меня есть простой Javascript / PHP / MySQL / Ajax веб-приложение, которое отображает время шины.

Я хочу улучшить его, чтобы оно отображало только следующую ожидаемую шину (а также более раннюю и следующую более позднюю шину).

Время шины хранится в базе данных как:

columns:
bus-time01
bus-time02
bus-time03

и т. д. вплоть до шины-time63.

int (4) Значения времени сохраняются как: 202, 930, 1230, 1945 и т. д. c

В базе данных всего одна таблица, всего 20 строк (поэтому совсем не большая).

70 столбцов в целом. Другие столбцы: id, bus_number, stop_id, start_stop, end_stop, special, day.

Мой запрос в том виде, в каком он есть, возвращает от 1 до 4 строк, которые хранятся в таблице. Переменные для запроса поступают из полей выбора и элементов div на странице. Я использую ajax и отдельную страницу php для выполнения запроса:

$result=mysqli_query($conn, "SELECT * FROM `timetable` WHERE `start_stop` = '" . $ss . "' AND `end_stop` = '" . $es . "' AND `day` = '" . $tmpDay . "'");

Мне нужно, чтобы каждая строка, отображаемая в таблице, показывала только время, близкое к тому, что указано в div страницы ( который отображает точное время). Более подробно объяснено ниже.

У меня есть функция javascript, которая получает текущее время. Он отображается на веб-страницах, как и значения базы данных, как. например, 0202, 0930, 1230, 1945 и т. д. c.

Единственное отличие состоит в том, что он показывает начальный ноль (что может вызвать проблему?).

Это функция времени:

(function() {

    function checkTime(i) {
        return (i < 10) ? "0" + i : i;
    }

    function getTimeNow() {

        var today = new Date(),
            hournow = checkTime(today.getHours()),
            minutesnow = checkTime(today.getMinutes()),
            timenow = hournow + "" + minutesnow;
        document.getElementById('time-now').innerHTML = timenow;
        t = setTimeout(function() {
            getTimeNow()
        }, 500);
    }

    getTimeNow();

})();

Я хочу получить текущее время из поля time-now , выполнить запрос к базе данных и отобразить только те записи, которые соответствуют критериям, приведенным ниже.

Например, если время 0913, то получите записи, которые являются ближайшими к этому, для каждой действительной строки. Где один ниже, а 2 выше (или, возможно, 1 из них равен, а 1 выше). т. е. 910, 915 и 920 (или 910, 913 и 920).

Таблица будет выглядеть следующим образом:

Если время в данный момент 09: 13

Bus# | Stop# | Earlier Bus | Next Bus | Later Bus |
---------------------------------------------------
 1   |  Z    |   0910      |  0915    |   0920    |
 5   |  B    |   0905      |  0920    |   0935    |
 3   |  A    |   0900      |  0930    |   1000    |

Это все ново для меня, и я действительно не знаю, с чего начать. Я думаю, мне нужно получить целый ряд раз и каким-то образом искать в них значения, которые мне нужны? Или есть способ попросить SQL напрямую получить нужные мне записи.

Спасибо за любые указатели в правильном направлении.

1 Ответ

4 голосов
/ 05 апреля 2020

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     |
+------+------+----------+----------+

Если у вас есть ночные автобусы, которые ходят после полуночи, они не смогут предложить их в качестве следующего. Это еще одна проблема.


Это много, но я надеюсь, что вы сможете следить за его поэтапным построением.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...