Could any one help me to make my query more efficient? - mysql

Could any one help me to make my query more efficient?
I'm a beginner of SQL,
as I know for efficient is to use index or primary key to let the B-tree search faster,therefore,I've already set the primary key for rid and index for (Date, Time)
However, my query is still too inefficient to output the result.
I'm trying to find the sensor(sid) which has the largest interval on '2017-03-04'.
Here is my code:
select tmp4.sid as largest_ivl_sensor
from
(
select tmp3.sid, MAX(tmp3.Dif) as max_for_each
from
(
select tmp1.sid, MIN(TIME_TO_SEC(DATE_SUB(tmp2.Time, INTERVAL tmp1.Time HOUR_SECOND))) as Dif
from
(
select se.sid, r.rid, r.Time
from (select rr.rid, rr.Time from records rr where rr.Date = '2017-03-04') as r, send se
where se.rid = r.rid
order by se.sid
) as tmp1
INNER JOIN
(
select se2.sid, r2.rid, r2.Time
from (select rr2.rid, rr2.Time from records rr2 where rr2.Date = '2017-03-04') as r2, send se2
where se2.rid = r2.rid
order by se2.sid
) as tmp2 ON tmp1.sid = tmp2.sid and TIME_TO_SEC(tmp1.Time) <= TIME_TO_SEC(tmp2.Time) and tmp1.rid <> tmp2.rid
GROUP BY tmp1.sid, tmp1.Time
) as tmp3
GROUP BY tmp3.sid
) as tmp4
group by tmp4.max_for_each
having tmp4.max_for_each = MAX(tmp4.max_for_each);
And here is the schema:
records
+-------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+---------+-------+
| rid | int(11) | NO | PRI | NULL | |
| Date | date | YES | MUL | NULL | |
| Time | time | YES | | NULL | |
| Humidity | double(5,2) | YES | | NULL | |
| Temperature | double(5,2) | YES | | NULL | |
| PM1 | int(11) | YES | | NULL | |
| PM10 | int(11) | YES | | NULL | |
| PM25 | int(11) | YES | | NULL | |
+-------------+-------------+------+-----+---------+-------+
send
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| sid | varchar(30) | NO | | NULL | |
| rid | int(11) | NO | PRI | NULL | |
+-------+-------------+------+-----+---------+-------+
Here is a example:
| rid |sid | Time |
| 1 | a | 00:00:00 |
| 2 | a | 00:01:00 |
| 3 | b | 00:05:00 |
| 4 | b | 00:07:00 |
| 5 | b | 00:11:00 |
| 6 | c | 00:00:00 |
| 7 | c | 00:03:00 |
| 8 | c | 00:04:00 |
desired result:
| sid|
| b|
Since it has largest interval 4 minutes
Another example:
| rid |sid | Time |
| 1 | a | 00:00:00 |
| 2 | b | 00:11:00 |
| 3 | c | 00:04:00 |
| 4 | b | 00:07:00 |
| 5 | a | 00:01:00 |
| 6 | c | 00:00:00 |
| 7 | c | 00:03:00 |
| 8 | b | 00:05:00 |
Thanks for helping me.

You could make use of variables in your SQL to keep track of the differences on-the-fly. Also, order by in combination with limit 1 is handy for getting the record with the highest value:
select sid,
max(diff) as max_diff
from (
select if(sid = #s, secs - #t, null) as diff,
#t := secs as secs,
#s := sid as sid
from (select sid,
time_to_sec(time) as secs
from records
inner join send on send.rid = records.rid
where date = '2017-03-04'
order by sid, time
) ordered
cross join (select #s := null, #t := null) init
) data
group by sid
order by max_diff desc
limit 1;
See it run on rextester.com.

Related

Error when joining a table to itself

I have a table where the data of measured temperature and humidity is stored. Records with temperature have the field meas_kind set to "T", and for humidity it is set to "H".
mysql> describe meteo;
+-----------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------------------+------+-----+---------+----------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| meas_time | timestamp | YES | | NULL | |
| room | varchar(10) | NO | | NULL | |
| meas_kind | char(1) | NO | | NULL | |
| value | double | NO | | NULL | |
+-----------+---------------------+------+-----+---------+----------------+
mysql> select * from meteo;
+----+---------------------+------+-----------+-------+
| id | meas_time | room | meas_kind | value |
+----+---------------------+------+-----------+-------+
| 35 | 2017-05-24 16:51:47 | 123 | T | 22.61 |
| 36 | 2017-05-24 16:51:47 | 123 | H | 36.93 |
| 37 | 2017-05-24 16:51:51 | 123 | T | 22.61 |
| 38 | 2017-05-24 16:51:51 | 123 | H | 36.94 |
| 39 | 2017-05-24 16:51:56 | 123 | T | 22.61 |
| 40 | 2017-05-24 16:51:56 | 123 | H | 36.94 |
+----+---------------------+------+-----------+-------+
Temperature and humidity are measured in the same time, so I want this table to be like this:
+---------------------+------+-------+-------+
| meas_time | room | Temp | Humid |
+---------------------+------+-------+-------+
| 2017-05-24 16:51:47 | 123 | 22.61 | 36.93 |
| 2017-05-24 16:51:51 | 123 | 22.61 | 36.94 |
| 2017-05-24 16:51:56 | 123 | 22.61 | 36.94 |
+---------------------+------+-------+-------+
I've tried to make this query, but it gives me an error:
ERROR 1242 (21000): Subquery returns more than 1 row
Please help me to get a correct result.
select
m.meas_time,
m.room,
(select value from meteo m1 where m1.meas_kind='T' and m.meas_time=m1.meas_time) as Temp,
(select value from meteo m2 where meas_kind='H' and m.meas_time=m2.meas_time) as Humid
from meteo m
join meteo m1
on m.meas_time=m1.meas_time
and m.room=m1.room
join meteo m2
on m.meas_time=m2.meas_time
and m.room=m2.room
group by m.room, m.meas_time;
You are overcomplicating things. A simple pivot query can give you the results you want.
SELECT
meas_time,
room,
MAX(CASE WHEN meas_kind = 'T' THEN value END) AS Temp,
MAX(CASE WHEN meas_kind = 'H' THEN value END) AS Humid
FROM meteo
GROUP BY meas_time, room

How should i implement ranking system

I am working on a project and wanted to have a quick look by you people if the schema or query needs some changes or does it totally needs to be changed.
The project is about creating a ranking system for the badminton teams where the ranking will be based on the scoring rules in a tournament
+2 Points/match would be awarded to the winning team.
An Extra +1 Point would be awarded to the winning team if the match was a Quarter-finals.
An Extra +2 Points would be awarded to the winning team if the match was a Semi-finals.
An Extra +5 Points would be awarded to the winning team if the match was a Final.
Winning all pool matches will add 4 points to your Team score.
Winning more than 3 tournaments will add 15 points to your team.
I started by creating the following tables
Players
+----------+-------------------------------------+------+-----+--------------
| Field | Type | Null | Key | +----------+-------------------------------------+------+-----+--------------
| id | int(11) | NO | PRI |
| name | varchar(250) | NO | |
| image | text | YES | |
| plays | enum('RH','LH') | NO | |
| added_on | datetime | NO | |
| status | enum('active','inactive','retired') | NO | | +----------+-------------------------------------+------+-----+--------------
Teams
+------------+----------------------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+----------------------------+------+-----+-------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(150) | NO | UNI | NULL | |
| image | text | YES | | NULL | |
| status | enum('active','in-active') | NO | | active | |
| added_on | datetime | NO | | CURRENT_TIMESTAMP | |
| updated_on | datetime | YES | | NULL | |
+------------+----------------------------+------+-----+-------------------+----------------+
Player To Teams
+-----------+---------------------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------------------------+------+-----+-------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| player_id | int(11) | NO | MUL | NULL | |
| team_id | int(11) | NO | | NULL | |
| status | enum('active','inactive') | NO | | NULL | |
| added_on | datetime | NO | | CURRENT_TIMESTAMP | |
+-----------+---------------------------+------+-----+-------------------+----------------+
Tournaments
+-------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| year | int(4) | NO | | NULL | |
+-------+--------------+------+-----+---------+----------------+
Matches
+---------------+---------------------------------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+---------------------------------------+------+-----+-------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| team_one | int(11) | NO | MUL | NULL | |
| team_two | int(11) | NO | | NULL | |
| winner_id | int(11) | NO | | NULL | |
| tournament_id | int(11) | NO | MUL | 1 | |
| added_on | datetime | NO | | CURRENT_TIMESTAMP | |
| match_type | enum('pool','quarter','semi','final') | NO | | pool | |
| sets | smallint(2) | NO | | 1 | |
+---------------+---------------------------------------+------+-----+-------------------+----------------+
Match Score
+----------+-------------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------------+------+-----+-------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| match_id | int(11) | NO | MUL | NULL | |
| team_id | int(11) | NO | MUL | NULL | |
| set_num | enum('1','2','3') | NO | | NULL | |
| score | smallint(2) | NO | | NULL | |
| added_on | datetime | NO | | CURRENT_TIMESTAMP | |
+----------+-------------------+------+-----+-------------------+----------------+
Pools
+---------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(10) | NO | UNI | NULL | |
| tournament_id | int(11) | NO | MUL | NULL | |
+---------------+-------------+------+-----+---------+----------------+
One thing that you will notice that i have not saved rank scoring anywhere and i am calculating it on runtime using the following query
SELECT
T.id, T.name, T.status,
IFNULL(T.image,'no-image.png') as DP,
(SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.team_one = T.id OR M.team_two = T.id)) as played,
(SELECT COUNT(*)
FROM badminton_matches M
WHERE M.winner_id = T.id) as won,
(SELECT COUNT(*)
FROM badminton_matches M
WHERE ((M.team_one = T.id OR M.team_two = T.id)
AND (M.winner_id != T.id))) as lost,
(
((SELECT COUNT(*)
FROM badminton_matches M
WHERE M.winner_id = T.id) * 2) +
(((SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.match_type = 'quarter'
AND M.winner_id = T.id)) * 2) + 1) +
(((SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.match_type = 'semi'
AND M.winner_id = T.id)) * 2) + 2) +
(((SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.match_type = 'final' AND M.winner_id = T.id)) * 2) + 5)) as Points
FROM
badminton_teams T
ORDER BY
(Points) DESC;
First
is it right to calculate the scoring on runtime using query or
should I save update it every time I save a match result in the database
or
should I schedule a cron job for this purpose
Edit:
Updated the query to the following
SELECT
T.id, T.name, T.status,
IFNULL(T.image,'no-image.png') as DP,
(SELECT COUNT(*)
FROM badminton_matches M
WHERE ((M.team_one =T.id or M.team_two = T.id) and M.winner_id IS NOT NULL)) as played,
(SELECT COUNT(*)
FROM badminton_matches M
WHERE M.winner_id=T.id) as won, (SELECT COUNT(*)
FROM badminton_matches M
WHERE ((M.team_one =T.id or M.team_two = T.id) AND (M.winner_id!=T.id))) as lost,
((SELECT (SUM(BMS.points_won)-SUM(BMS.points_lost))
FROM
badminton_match_score BMS
JOIN badminton_matches M ON (M.id=BMS.match_id)
where M.team_one=T.id OR M.team_two=T.id and M.winner_id is not null)/(SELECT COUNT(*)
FROM badminton_matches M
WHERE ((M.team_one =T.id or M.team_two = T.id) and M.winner_id IS NOT NULL))) AS AVG_SCORE,
(
((SELECT COUNT(*)
FROM badminton_matches M
WHERE M.winner_id=T.id)*2) +
(SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.match_type='quarter' AND M.winner_id=T.id))
+
((SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.match_type='semi' AND M.winner_id=T.id))*2)
+
((SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.match_type='final' AND M.winner_id=T.id))*5)
)
as Points
FROM badminton_teams T
order by (Points) DESC, lost ASC, AVG_SCORE DESC
is it right to calculate the scoring on runtime using query
If you want it to be a real time application. In my opinion, yes if the points are not accumulated and will reset after a game. For Sql performance issues.
should i save update it every time i save a match result in the database
Updates are for modification only of data, saving the data after a match for history will be sufficient.
should i schedule a cron job for this purpose
If in terms of larger databases and productivity, yes.

How can handle this ERROR 1242 (21000): Subquery returns more than 1 row

Hello everyone I am having issue with SQL query, have googled a lot but couldnt find any solution. here is my code:
SELECT (SELECT ltp
FROM eod_stock
WHERE company_id IN (1,2)
AND entry_date = '2013-09-24') AS b,
(SELECT ltp
FROM eod_stock
WHERE company_id IN (1,2)
AND entry_date = '2013-09-25') AS e,
CAST(((SELECT ltp
FROM eod_stock
WHERE company_id IN(1,2)
AND entry_date = '2013-09-25') -
(SELECT ltp
FROM eod_stock
WHERE company_id IN (1,2)
AND entry_date = '2013-09-24')) /
(SELECT ltp
FROM eod_stock
WHERE company_id IN (1,2)
AND entry_date = '2013-09-25')*100
AS DECIMAL(10,2)) AS c;
it gives me error
ERROR 1242 (21000): Subquery returns more than 1 row
I understand the error cause my query should return more than 1 row as i am passing 2 company but i need all companies ltp, that i mentioned in my query. The result is need is like:
+--------+--------+--------+
| b | e | c |
+--------+--------+--------+
| 460.00 | 338.00 | -36.09 |
+--------+--------+--------+
| 320.00 | 488.00 | 34.42 |
+--------+--------+--------+
can anyone suggest how can i get multiple result like this from my query? caz i have to pass more than 1 company id in my query.
Table structure:
+-----------------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+------------------+------+-----+---------+-------+
| company_id | varchar(30) | NO | PRI | NULL | |
| entry_date | date | NO | PRI | NULL | |
| entry_timestamp | int(10) unsigned | NO | | NULL | |
| open | decimal(16,2) | NO | | NULL | |
| high | decimal(16,2) | NO | | NULL | |
| low | decimal(16,2) | NO | | NULL | |
| ltp | decimal(16,2) | NO | | NULL | |
| ycp | decimal(16,2) | NO | | NULL | |
| cse_price | decimal(9,2) | NO | | NULL | |
| cse_volume | decimal(18,2) | NO | | NULL | |
| total_trade | int(30) | NO | | NULL | |
| total_volume | int(30) | NO | | NULL | |
| total_value | decimal(18,4) | NO | | NULL | |
+-----------------+------------------+------+-----+---------+-------+
When you select from a subquery you can only get a single row.
You can join the table on itself and select the rows with the two dates you are interested in. Something like this should work.
SELECT a.ltp, b.ltp, CAST(((b.ltp - a.ltp) / b.ltp * 100) AS DECIMAL(10, 2))
FROM eod_stock a
JOIN eod_stock b ON a.company_id = b.company_id
WHERE a.entry_date = "2013-09-24"
AND b.entry_date = "2013-09-25"
AND a.company_id IN (1, 2)

Empty rows for MySQL Query but using a WHERE

I have a table names lcMovimientos and what I need is a query where I get as a result the sum of the quantity of cantidadMovimientos but I want each row to be by the day of the week and only to sum the days of the current week, so after researching I found the best way to do this was creating a new table with the days of the week, so I now have a table called diasSemana
SELECT * FROM diasSemana
+-----------+
| diaSemana |
+-----------+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
+-----------+
and a table called lcMovimientos
mysql> DESCRIBE lcMovimientos;
+-----------------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------------+---------------+------+-----+---------+----------------+
| idMovimiento | int(11) | NO | PRI | NULL | auto_increment |
| idUsuario | int(11) | YES | | NULL | |
| tipoMovimiento | tinyint(4) | YES | | NULL | |
| cantidadMovimiento | decimal(20,2) | YES | | NULL | |
| idCategoria | int(11) | YES | | NULL | |
| fechaMovimiento | date | YES | | NULL | |
| idCuenta | int(11) | YES | | NULL | |
| descripcionMovimiento | varchar(255) | YES | | NULL | |
| etiquetasMovimiento | varchar(255) | YES | | NULL | |
+-----------------------+---------------+------+-----+---------+----------------+
I can make a query where I do get the sum of the cantidadMovimiento but when I add the where clause so I only get results from the current week, I no longer get the rows by day fo the week, so here is my query:
mysql> SELECT SUM( cantidadMovimiento ) , diaSemana, fechaMovimiento -> FROM diasSemana
-> LEFT JOIN lcMovimientos ON diaSemana = WEEKDAY( fechaMovimiento )
-> GROUP BY diaSemana;
+---------------------------+-----------+-----------------+
| SUM( cantidadMovimiento ) | diaSemana | fechaMovimiento |
+---------------------------+-----------+-----------------+
| 280.00 | 0 | 2012-02-20 |
| 800.00 | 1 | 2012-02-21 |
| 7000.00 | 2 | 2012-02-29 |
| NULL | 3 | NULL |
| NULL | 4 | NULL |
| -3300.78 | 5 | 2012-02-18 |
| 600.00 | 6 | 2012-02-26 |
+---------------------------+-----------+-----------------+
and when I use the WHERE clause:
mysql> SELECT SUM( cantidadMovimiento ) , diaSemana, fechaMovimiento
-> FROM diasSemana
-> LEFT JOIN lcMovimientos ON diaSemana = WEEKDAY( fechaMovimiento )
-> WHERE WEEK( fechaMovimiento, 1 ) = WEEK( CURRENT_DATE, 1 )
-> GROUP BY diaSemana;
+---------------------------+-----------+-----------------+
| SUM( cantidadMovimiento ) | diaSemana | fechaMovimiento |
+---------------------------+-----------+-----------------+
| 265.00 | 0 | 2012-02-20 |
| 800.00 | 1 | 2012-02-21 |
| 600.00 | 6 | 2012-02-26 |
+---------------------------+-----------+-----------------+
So my question is how can i make a query where I will get the results by the day of the week using the where to only get dates of the current week??? thank you so much in advance!
try this
SELECT
COALESCE(SUM( cantidadMovimiento ),0) AS cantidadMovimiento,
diaSemana,
DATE_ADD(DATE(NOW()), INTERVAL diaSemana-WEEKDAY(NOW()) DAY) AS weekday
FROM diasSemana
LEFT JOIN lcMovimientos
ON diaSemana = WEEKDAY( fechaMovimiento )
AND WEEK(fechaMovimiento) = WEEK(NOW()) AND YEAR(fechaMovimiento) = YEAR(NOW())
GROUP BY diaSemana;

Help with MySQL join 3 Tables

I am working on a registration system that uses the following tables:
Person (name etc.), Course (course date), Registration (association table pid, cid)
Person
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | text | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
Example Data:
select * from person;
+-----+--------+
| id | name |
+-----+--------+
| 101 | Graham |
| 102 | Lisa |
| 103 | John |
+-----+--------+
Course
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | text | YES | | NULL | |
| date | date | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
select * from course;
+----+---------+------------+
| id | name | date |
+----+---------+------------+
| 1 | Hip Hop | 2011-06-08 |
| 2 | Dancing | 2006-06-23 |
| 3 | Running | 2007-07-08 |
+----+---------+------------+
Registration
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| pid | int(11) | YES | | NULL | |
| cid | int(11) | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
select * from registration;
+----+------+------+
| id | pid | cid |
+----+------+------+
| 1 | 101 | 1 |
| 2 | 101 | 2 |
| 3 | 103 | 2 |
+----+------+------+
I would like to find person(s) that have no registration records within the past two years. I am attempting to join the tables based on date calculation but it does not seem to work this way. Is this possible with mysql or is my approach of trying this with one query wrong?
query I have come up with:
select * from
(person left join registration on person.id = registration.pid)
left join course on course.id = registration.cid
AND DATE_FORMAT(`course`.`date`, "%m.%Y") > DATE_FORMAT( DATE_SUB(NOW(), INTERVAL 2 YEAR),"%m.%Y")
WHERE
registration.id IS NULL;
+-----+------+------+------+------+------+------+------+
| id | name | id | pid | cid | id | name | date |
+-----+------+------+------+------+------+------+------+
| 102 | Lisa | NULL | NULL | NULL | NULL | NULL | NULL |
+-----+------+------+------+------+------+------+------+
It should list person 102 and 103 since both registrations are older than 2 years and no other records of newer course dates can be found...
Give this a shot, using a NOT EXISTS clause:
select p.* from person p
where not exists (select 1 from person px
join registration rx on px.id = rx.pid
join course cx on rx.cid = cx.id
where px.id = p.id
and cx.date > DATE_SUB(NOW(), INTERVAL 2 YEAR))