SQL get count from table 1 where complicated - mysql

I am stuck with a sql code where i need te get data as followed(i hope i can explain it good enough):
There are three tables in my database:
Table 1 is filled with traffic accidents(VKL_NUMBER and TIME)
Table 2 is filled with the locations of the accidents and the closest weather station (VKL_NUMBER, LOCATION and STN_NUMBER )
Table 3 is filled with weather data and the weather station where it came from(STN_NUMBER, TIME, WEATHER_TYPE)
I need to count the amount of accidents where it rains.
The keys to the tables are:
From 1 to 2: VKL_NUMBER(accident number)
From 2 to 3: STN_NUMBER (weather station number)
How can i get the weathertype at the time and closest to the accident in a count
Like :
Count accidents where it rains at the closest weatherstation.
for more info:
The accidents table has VKL_NUMBER(FK to the locations table) TIME(HHMM format) and DATE(YYMMDD format)
The locations table has VKL_NUMBER(FK to accidents), LOCATION(not important for this question) and STN_NUMBER(FK to the weather table)
The weather table had STN_NUMBER(FK to locations table), WEATHERTYPE("rain","snow","hail" ), TIME(HHMM format) and DATE(YYMMDD format)

/* apparently you'll need to combine and cast the <date + time> values */
select count(case when weather.weathertype = 'rain' then 1 end)
from
(
select
accidents.vkl_number,
min(<accidents.date + accidents.time>) as time_of_accident,
min(weather.stn_number) as stn_number,
max(timestampdiff(minute,
<weather.date + weather.time>,
<accidents.date + accidents.time>
)) as differential
from
t1 accidents inner join t2 accident_locations
on accident_locations.vkl_number = accidents.vkl_number
inner join t3 weather
on weather.stn_number = accident_locations.stn_number
and weather.time <= accidents.time
group by accidents.vkl_number
) closest
inner join t3 weather
on weather.stn_number = closest.stn_number
and date_add(
<weather.date + weather.time>,
interval differential second
) = closest.time_of_accident
I'm assuming you want the station time just prior to the accident. Breaking ties is a significant complication and we need more info about your desired matching logic.

Related

MySQL can I in a join calculate and show the results

I have 2 tables in the first I define the equipment possible like fire hydrant or fire alarm system, all of theese are to check within a certain time, in this table I have the name of theese equipments and the time within they are to chec, in the second table I have the date when the last chec was I want create a View List but that contains the information, when the next chec is to realize,
Like this
Create View listafacilitymanagemen AS
SELECT DISTINCT ID, tipo, posicion, checeo, checeo + checeointerval,
JOIN ID, tipo, checeointerval
FROM hotel.facilitymanagemen_equipo
ORDER BY checeo + checeointerval;
My database is very big and I don't want this in another column in the the table "facilitymanagement".
I trust very much in w3school, there I found this https://www.w3schools.com/sql/sql_join_inner.asp.
CREATE VIEW TABLE facilitymanagement
SELECT m.ID,
m.tipo,
e.tipo,
e.ID,
m.posicio,
m.checeo,
m.checeo + e.checeointerval,
m.checeocontrollado,
m.checeooficial,
m.checeooficial + e.checeooficialinterval,
m.checeooficialcontrollado,
m.controllero,
e.checeointerval,
e.checeooficialinterval
FROM hotel.facilitymanagemen (m)
INNER JOIN hotel.facilitymanagement_equipment (e)
ON m.tipo = e.ID
ORDER BY m.checeooficial + e.checeooficialinterval;
I'm a german and by law we are to chec the equipments in a building that has to do with fire to chec by a specialist and a responsable authority, moreover we have for everything an insurance and if the interval are exceeded they don't pay, so this View list is very important, I added some columne aboveall "checeooficial" and this columne with the interval give the ORDER BY criteria.
If my code works I cannot try.
Because I have 2 data bases I precise from whitch one I want the data in the FROM and the INNER JOIN clause it is the data base "hotel".
I added a WHERE claus to filter the lines, so the VIEW TABLE contains only the data after the actual system-date and I ORDERed DESC; so the most urget equipment apears first.
CREATE VIEW TABLE facilitymanagement AS
SELECT m.ID,
m.tipo,
e.tipo,
e.ID,
m.posicio,
m.checeo,
m.checeo + e.checeointerval,
m.checeocontrollado,
m.checeooficial,
m.checeooficial + e.checeooficialinterval,
m.checeooficialcontrollado,
m.controllero,
e.checeointerval,
e.checeooficialinterval
FROM hotel.facilitymanagemen (m)
INNER JOIN hotel.facilitymanagement_equipment (e)
ON m.tipo = e.ID
WHERE m.checeooficial + e.checeooficialinterval > NOW()
ORDER BY m.checeooficial + e.checeooficialinterval DESC;
Instead of NOW() better would be to call the CURDATE()-function.

How select count distinct (unique truckers) without group by function and maybe without using Having (not sure about last)

I have a task, but couldn't solve it:
There are truckers and they have to travel between cities.
We have data of these travels in our database in 2 tables:
trucker_traffic
tt_id (key)
date
starting_point_coordinate
destination_coordinate
traveller_id
event_type ('travel', 'accident')
parent_event_id (For 'accident' event type it's tt_id of the original travel. There might be few accidents within one travel.)
trucker_places
coordinate (key)
country
city
I need SQL query to pull the number of all unique truckers who travelled more than once from or to London city in June 2020.
In the same query pull the number of these travels who got into an accident.
Example of my tries
SELECT
count(distinct(tt.traveller_id)),
FROM trucker_traffic tt
JOIN trucker_places tp
ON tt.starting_point_coordinate = tp.coordinate
OR tt.destination_coordinate = tp.coordinate
WHERE
tp.city = 'London'
AND month(tt.date) = 6
AND year(tt.date) = 2020
GROUP BY tt.traveller_id
HAVING count(tt.tt_id) > 1
But it's select count distinct truckers with grouping and works only if I had one tracker in db
For second part of task (where I have select number of travels with accident - I think that good to use function like this
SUM(if(count(tt_id = parent_event_id),1,0))
But I'm not sure
This is rather complicated, so make sure you do this step by step. WITH clauses help with this.
Steps
Find travels from and to London in June 2020. You can use IN or EXISTS in order to see whether a travel had accidents.
Group the London travels by traveller, count travels and accident travels and only keep those travellers with more than one travel.
Take this result set to count the travellers and sum up their travels.
Query
with london_travels as
(
select
traveller_id,
case when tt_id in
(select parent_event_id from trucker_traffic where event_type = 'accident')
then 1 else 0 end as accident
from trucker_traffic tt
where event_type = 'travel'
and month(tt.date) = 6
and year(tt.date) = 2020
and exists
(
select
from trucker_places tp
where tp.coordinate in (tt.starting_point_coordinate, tt.destination_coordinate)
and tp.city = 'London'
)
)
, london_travellers as
(
select
traveller_id,
count(*) as travels,
sum(accident) as accident_travels
from london_travels
group by traveller_id
having count(*) > 1;
)
select
count(*) as total_travellers,
sum(travels) as total_travels,
sum(accident_travels) as total_accident_travels
from london_travellers;
If your MySQL version doesn't support WITH clauses, you can of course just nest the queries. I.e.
with a as (...), b as (... from a) select * from b;
becomes
select * from (... from (...) a) b;
You say in the request title that you don't want GROUP BY in the query. This is possible, but makes the query more complicated. If you want to do this I leave this as a task for you. Hint: You can select travellers and count in subqueries per traveller.

How to update the score column in one table with the average from columns in other three tables?

I have 4 tables that have an score column (10 points max as score)
This structure is similar to the one I have in my real database:
- master_entity(id, name, **score**, other-columns...)
- scores_table1(id, score_giver1_id, master_entity_id, **score**)
- scores_table2(id, score_giver2_id, master_entity_id, **score**)
- scores_table3(id, score_giver3_id, master_entity_id, **score**)
I would like to execute a simple SQL script (for MySQL database) daily with a cron job. This script must update the master_entity.score column with the average of the averages of the score column in other three tables.
But the average from score column in scores_table1 represent just 1/6 of the average score column value in master_entity.
Also, the average from score column in scores_table2 must represent just 2/6 of the average score column value in master_entity and finally the average from score column in scores_table3 must represent just 3/6 of the average score column value in master_entity
What would be the recommended SQL to update the master_entity.score column using the proportional averages of scores in the other 3 tables?
I have not tried, but based on your question, your sql query should looks like this:
update master_entity m
join(
select master_entity_id,avg(score) as score
from score_table1
group by master_entity_id
) as s1 on s1.master_entity_id = m.id
join(
select master_entity_id,avg(score) as score
from score_table2
group by master_entity_id
) as s2 on s2.master_entity_id = m.id
join(
select master_entity_id,avg(score) as score
from score_table3
group by master_entity_id
) as s3 on s3.master_entity_id = m.id
set m.score = (1/6)*s1.score + (2/6)*s2.score + (3/6)*s3.score
which master_entity.score = (1 /6)*avg(tbl1.score) + (2 /6)*avg(tbl2.score) + (3 /6)*avg(tbl3.score)

MySQL find minimum and maximum date associated with a record in another table

I am trying to write a query to find the number of miles on a bicycle fork. This number is calculated by taking the distance_reading associated with the date that the fork was installed on (the minimum reading_date on or after the Bicycle_Fork.start_date associated with the Bicycle_Fork record) and subtracting that from the date that the fork was removed (the maximum reading_date on or before the Bicycle_Fork.end_date or, if that is null, the reading closest to today's date). I've managed to restrict the range of odometer_readings to the appropriate ones, but I cannot figure out how to find the minimum and maximum date for each odometer that represents when the fork was installed. It was easy when I only had to look at records matching the start_date or end_date, but the user is not required to enter a new odometer reading for each date that a part is changed. I've been working on this query for several hours now, and I can't find a way to use MIN() that doesn't just take the single smallest date out of all of the results.
Question: How can I find the minimum reading_date and the maximum reading_date associated with each odometer_id while maintaining the restrictions created by my WHERE clause?
If this is not possible, I plan to store the values retrieved from the first query in an array in PHP and deal with it from there, but I would like to be able to find a solution solely in MySQL.
Here is an SQL fiddle with the database schema and the current state of the query: http://sqlfiddle.com/#!2/015642/1
SELECT OdometerReadings.distance_reading, OdometerReadings.reading_date,
OdometerReadings.odometer_id, Bicycle_Fork.fork_id
FROM Bicycle_Fork
INNER JOIN (Bicycles, Odometers, OdometerReadings)
ON (Bicycles.bicycle_id = Bicycle_Fork.bicycle_id
AND Odometers.bicycle_id = Bicycles.bicycle_id AND OdometerReadings.odometer_id = Odometers.odometer_id)
WHERE (OdometerReadings.reading_date >= Bicycle_Fork.start_date) AND
((Bicycle_Fork.end_date IS NOT NULL AND OdometerReadings.reading_date<= Bicycle_Fork.end_date) XOR (Bicycle_Fork.end_date IS NULL AND OdometerReadings.reading_date <= CURRENT_DATE()))
This is the old query that didn't take into account the possibility of the database lacking a record that corresponded with the start_date or end_date:
SELECT MaxReadingOdo.distance_reading, MinReadingOdo.distance_reading
FROM
(SELECT OdometerReadings.distance_reading, OdometerReadings.reading_date,
OdometerReadings.odometer_id
FROM Bicycle_Fork
LEFT JOIN (Bicycles, Odometers, OdometerReadings)
ON (Bicycles.bicycle_id = Bicycle_Fork.bicycle_id
AND Odometers.bicycle_id = Bicycles.bicycle_id AND OdometerReadings.odometer_id = Odometers.odometer_id)
WHERE Bicycle_Fork.start_date = OdometerReadings.reading_date) AS MinReadingOdo
INNER JOIN
(SELECT OdometerReadings.distance_reading, OdometerReadings.reading_date,
OdometerReadings.odometer_id
FROM Bicycle_Fork
LEFT JOIN (Bicycles, Odometers, OdometerReadings)
ON (Bicycles.bicycle_id = Bicycle_Fork.bicycle_id AND Odometers.bicycle_id
= Bicycles.bicycle_id AND OdometerReadings.odometer_id = Odometers.odometer_id)
WHERE Bicycle_Fork.end_date = OdometerReadings.reading_date) AS
MaxReadingOdo
ON MinReadingOdo.odometer_id = MaxReadingOdo.odometer_id
I'm trying to get the following to return from the SQL schema:
I will eventually sum these into one number, but I've been working with them separately to make it easier to check the values.
min_distance_reading | max_distance_reading | odometer_id
=============================================================
75.5 | 2580.5 | 1
510.5 | 4078.5 | 2
17.5 | 78.5 | 3
I don't understand the final part of the puzzle, but this seems close...
SELECT MIN(ro.distance_reading) min_val
, MAX(ro.distance_reading) max_val
, ro.odometer_id
FROM OdometerReadings ro
JOIN odometers o
ON o.odometer_id = ro.odometer_id
JOIN Bicycle_Fork bf
ON bf.bicycle_id = o.bicycle_id
AND bf.start_date <= ro.reading_date
GROUP
BY ro.odometer_id;
http://sqlfiddle.com/#!2/015642/8

SELECT to get two entries, from same table, differentiated by date, in one row

I have a table in which i keep different meters (water meter, electricity meter) and in another table i keep the readings for each meter.
The table structure is like this :
The meter table
MeterID | MeterType | MeterName
The readings Table:
ReadingID | MeterID | Index | DateOfReading
The readings for a meter are read monthly. The thing I am trying to do now is to get the Meter information, the current reading and the previous reading in just one row. So if i would have a query, the following row would result:
MeterID | MeterType | MeterName | CurrnetIndex | LastIndex
I have the following query so far :
SELECT Meter.MeterID, Meter.MeterType, Meter.MeterName, CurrentReading.Index, PreviousReading.Index
FROM Meters AS Meter
LEFT OUTER JOIN Readings AS CurrentReading ON Meter.MeterID = CurrentReading.MeterID
LEFT OUTER JOIN Readings AS PreviousReading ON Meter.MeterID = PreviouseReading.MeterID
WHERE CurrentReading.ReadingID != PreviousReading.ReadingID AND DIMESTAMPDIFF(MONTH, CurrentReading.DateOfReading, PreviousReding.DateOfReading)=-1
The problem is that I may not have the current reading or the previous, or both, but I would still need to have the meter information retrieved. It is perfectly acceptable for me to get NULL columns, but i still need a row :)
Use:
SELECT m.meterid,
m.metertype,
m.metername,
current.index,
previous.index
FROM METER m
LEFT JOIN READING current ON current.meterid = m.meterid
AND MONTH(current.dateofreading) = MONTH(NOW())
LEFT JOIN READING previous ON previous.meterid = m.meterid
AND MONTH(current.dateofreading) = MONTH(NOW())-1
Being an OUTER JOIN - if the MONTH filtration is done in the WHERE clause, it can produce different results than being done in the ON clause.
You could use a subquery to grab the value from a month ago:
select *
, (
select Index
from Readings r2
where r2.MeterID = m.MeterID
and DIMESTAMPDIFF(MONTH, r1.DateOfReading,
r2.DateOfReading) = -1
) as LastIndex
from Meter m
left join
Readings r1
on r1.MeterID = m.MeterID
Another solution is to allow the second left join to fail. You can do that by just changing your where clause to:
WHERE PreviousReading.ReadingID is null
or
(
CurrentReading.ReadingID != PreviousReading.ReadingID
and
DIMESTAMPDIFF(MONTH, CurrentReading.DateOfReading,
PreviousReding.DateOfReading) = -1
)
well, sql philosophy is to store what you know. if you don't know it, then there isn't any row for it. if you do a filter on the record set that you search for, and find nothing, then there isn't any month reading for it. Or that i didnt understand the question