сколько времени потребуется этому автомобилю, чтобы проехать около 5000 миль
Ну, я думаю, это главная проблема.
Откуда взялись "5000 миль"?Это ваш рекомендуемый интервал между заменами масла?Возможно, вы захотите запланировать другие операции обслуживания, которые происходят через разные промежутки времени (шины, что угодно).Так что вам нужен способ кодировать эти интервалы.
Я приведу небольшой пример:
CREATE TABLE service_types (
st_id SERIAL PRIMARY KEY,
st_name TEXT,
st_interval_miles INTEGER,
st_interval_time INTERVAL
);
CREATE TABLE vehicles ( veh_id SERIAL PRIMARY KEY, birth_date DATE );
CREATE TABLE service_sales (
ss_id SERIAL PRIMARY KEY,
ss_mileage INTEGER NOT NULL,
ss_date DATE NOT NULL,
veh_id INTEGER NOT NULL REFERENCES vehicles( veh_id )
);
CREATE TABLE service_details(
ss_id INTEGER NOT NULL REFERENCES service_sales( ss_id ),
st_id INTEGER NOT NULL REFERENCES service_types( st_id )
);
Давайте заполним это:
INSERT INTO service_types (st_name,st_interval_miles,st_interval_time) VALUES
('oil change', 5000, '2 YEAR'),
('timing belt', 60000, '5 YEAR');
INSERT INTO vehicles (veh_id,birth_date) VALUES (1,'2009-09-01'),(2,'2009-08-01');
INSERT INTO service_sales (ss_id, veh_id, ss_mileage, ss_date) VALUES
(1, 1, 5000, '2010-01-01'),
(2, 1, 12000, '2010-04-01'),
(3, 1, 18000, '2010-09-01'),
(4, 2, 5000, '2010-01-01'),
(5, 2, 10000, '2010-08-01'),
(6, 2, 16000, '2010-12-01');
INSERT INTO service_details (ss_id,st_id) VALUES
(1,1),
(2,1),
(3,1),
(4,1),
(5,1),
(6,1);
SELECT * FROM vehicles v
JOIN service_sales USING (veh_id)
JOIN service_details USING (ss_id)
JOIN service_types USING (st_id);
st_id | ss_id | veh_id | birth_date | ss_mileage | ss_date | st_name | st_interval_miles | st_interval_time
-------+-------+--------+------------+------------+------------+------------+-------------------+------------------
1 | 1 | 1 | 2009-09-01 | 5000 | 2010-01-01 | oil change | 5000 | 2 years
1 | 2 | 1 | 2009-09-01 | 12000 | 2010-04-01 | oil change | 5000 | 2 years
1 | 3 | 1 | 2009-09-01 | 18000 | 2010-09-01 | oil change | 5000 | 2 years
1 | 4 | 2 | 2009-08-01 | 5000 | 2010-01-01 | oil change | 5000 | 2 years
1 | 5 | 2 | 2009-08-01 | 10000 | 2010-08-01 | oil change | 5000 | 2 years
1 | 6 | 2 | 2009-08-01 | 16000 | 2010-12-01 | oil change | 5000 | 2 years
Теперь для вашей оценки: вам нужно вычислить «среднюю скорость»транспортного средства, то есть расстояние, которое он сделал, разделенный на время, но вам нужно выбрать интервал.Вы можете использовать интервал между двумя последними услугами или среднее значение за все время, когда ваш магазин видел этот автомобиль.Это зависит от вас.
Как только вы это получите, вы можете рассчитать, сколько времени потребуется этому транспортному средству, чтобы преодолеть 5000 миль с момента последней зафиксированной замены масла.
Допустим, вы оцените среднюю скорость по всемраз ваш магазин увидел этот автомобиль.
Это означает, что для каждого транспортного средства вы берете последнюю и первую служебную документацию и вычисляете разницу в пробеге и времени:
SELECT foo.veh_id,
(last.ss_mileage-first.ss_mileage) AS miles,
(last.ss_date-first.ss_date) AS days FROM (
SELECT veh_id, min( ss_id ) AS first_ss_id, max( ss_id ) AS last_ss_id
FROM service_sales GROUP BY veh_id
) foo
JOIN service_sales first ON (first.ss_id=first_ss_id)
JOIN service_sales last ON (last.ss_id=last_ss_id)
WHERE first_ss_id != last_ss_id AND last.ss_date <= first.ss_date + '1 WEEK'::INTERVAL;
veh_id | miles | days
--------+-------+------
1 | 13000 | 243
2 | 11000 | 334
Таким образом, мы знаем, сколько миль было пройдено каждым транспортным средством за сколько дней.Условие WHERE исключает транспортные средства, которые имеют только одну служебную запись или о которых ваш магазин знает только менее чем за 1 неделю, что не дает очень хорошей оценки.Вы можете использовать LEFT JOIN и некоторую работу COALESCE, чтобы заменить это ненадежным значением по умолчанию.
Теперь из последней замены масла и средней скорости мы можем получить значение для следующей замены масла:
WITH
avgspeeds AS (SELECT foo.veh_id,
(last.ss_mileage-first.ss_mileage) AS miles,
(last.ss_date-first.ss_date) AS days FROM (
SELECT veh_id, min( ss_id ) AS first_ss_id, max( ss_id ) AS last_ss_id
FROM service_sales GROUP BY veh_id
) foo
JOIN service_sales first ON (first.ss_id=first_ss_id)
JOIN service_sales last ON (last.ss_id=last_ss_id)
WHERE first_ss_id != last_ss_id AND last.ss_date >= first.ss_date + '1 WEEK'::INTERVAL
),
last_services AS (SELECT
veh_id,
st_id,
max(ss_date) AS last_service_date
FROM service_sales
JOIN service_details USING (ss_id)
GROUP BY veh_id, st_id)
SELECT *,
COALESCE( last_service_date, birth_date ) + least(
(st.st_interval_miles * a.days / a.miles) * '1 DAY'::INTERVAL,
st.st_interval_time) AS next_service_date
FROM
service_types st
CROSS JOIN vehicles v
JOIN avgspeeds a USING (veh_id)
LEFT JOIN last_services ls USING (veh_id,st_id);
Монстр, а?Кажется, дает правильные результаты.
Удачи;)
Почему вы ставите ненужные префиксы перед каждым именем столбца?Я действительно ненавижу, когда люди делают это, это делает каждый запрос чертовски уродливым,
Потому что: - Это делает запросы читабельными - я ненавижу иметь несколько полей "id" в наборах результатов, поступающих из любоготаблицы - Внешние ключи должны иметь одинаковые имена в обеих таблицах, что позволяет использовать NATURAL JOIN и очень полезное JOIN .. ИСПОЛЬЗОВАНИЕ - Мне все равно, что вам это не понравится
иэто еще хуже, если вы используете Orm.Псевдоним столбца уже в стандарте sql, используйте его
Если ваш ORM не позволяет вам определять имена столбцов таблицы, которые отличаются от имен членов объекта, и писать большие (но читабельные) запросы SQLвам нужен настоящий ORM.