MYSQL SELECT most recent of id and ORDER BY - mysql

What I am trying to do is get the most recent result for each resort_id and then ORDER BY snow_depth. The first two parts I have managed. The ordering is the part that doesn't work.
What I have so far
SELECT * FROM snow_conditions t1
NATURAL JOIN ( SELECT MAX(weather_id) AS weather_id, resort_id FROM snow_conditions
GROUP BY resort_id ) t2
ORDER BY snow_depth DESC
weather_id is auto incremented, so I use it instead of save_time to reduce calculation resource.
The thing that is confusing me is the result comes out in some weird partial order of snow_depth.
+-----------+------------+------------+
| resort_id | weather_id | snow_depth |
+-----------+------------+------------+
| 888 | 827 | 90 |
| 943 | 835 | 90 |
| 860 | 839 | 75 |
| 17 | 828 | 71 |
| 26 | 826 | 70 |
| 9 | 852 | 60 |
| 16 | 831 | 292 |
| 296 | 862 | 170 |
| 20 | 843 | 168 |
| 5 | 842 | 165 |
| 36 | 838 | 160 |
| 17 | 17 | 0 |
| 26 | 26 | 0 |
+-----------+------------+------------+
When really it should appear like this
+-----------+------------+------------+
| resort_id | weather_id | snow_depth |
+-----------+------------+------------+
| 16 | 831 | 292 |
| 296 | 862 | 170 |
| 20 | 843 | 168 |
| 5 | 842 | 165 |
| 36 | 838 | 160 |
| 888 | 827 | 90 |
| 943 | 835 | 90 |
| 860 | 839 | 75 |
| 17 | 828 | 71 |
| 26 | 826 | 70 |
| 9 | 852 | 60 |
| 17 | 17 | 0 |
| 26 | 26 | 0 |
+-----------+------------+------------+
I have tried just about every relevant looking MySQL query I could find on here but they all encounter the same issue or don't work.
EDIT: I should mention this table contains thousands of rows, with hundreds of rows for each resort_id. It's done this way so I can use it to generate a snowfall history.

I think you should check your database for data type of snow_depth it should not be varchar. Your result sounds like it is varchar so if it is then change it to int or any numeric type

your result already sorted by snow_depth but the snow_depth column is varchar data type. change it or you can use this:
cast(snow_depth as unsigned) DESC
so, your all query should be:
SELECT * FROM snow_conditions t1
NATURAL JOIN ( SELECT MAX(weather_id) AS weather_id, resort_id FROM snow_conditions
GROUP BY resort_id ) t2
ORDER BY cast(snow_depth as unsigned) DESC

Related

Why does using with rollup give me different results when inside pivot are using sum and pivot and left join?

I have 3 tables Here the location http://rextester.com/PED43367
I failed in with roll up, can some one giving me the way?
the output i want is :
enter image description here
The result is ok, but I can't make rollup with that
Thanks for your Help
You should read up on mysql order of execution (MySQL query / clause execution order) and https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html paying particular attention to "If ONLY_FULL_GROUP_BY is disabled, a MySQL extension to the standard SQL use of GROUP BY permits the select list, HAVING condition, or ORDER BY list to refer to nonaggregated columns even if the columns are not functionally dependent on GROUP BY columns. This causes MySQL to accept the preceding query. In this case, the server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate, which is probably not what you want."
In brief then your from and joins are executed first, the group by is dodgy and the rollup adds issues.
I would separate out the group by for pivot and then add the joins using group by "properly"
select m.codes,m.version,sum(m.headcount) headount,sum(m.reghrs) reghrs, sum(m.hrsbdgt) hrsbudget,
sum(w.workhrs) workhours, sum(w.reghrs) wreghrs,
sum(d1) '02-04-2017',
sum(d2) '09-04-2017',
sum(d3) '16-04-2017',
sum(d4) '23-04-2017',
sum(d5) '30-04-2017',
sum(p.hours) as Total,
SUM(p.hours) - sum(m.HrsBdgt) RsltBdgt
from mtarget m
left join
(
select CODEPivot,categoryPivot ,
SUM(IF(pivot.selesai = '2017-04-02',pivot.hours,0)) d1,
SUM(IF(pivot.selesai = '2017-04-09',pivot.hours,0)) d2,
SUM(IF(pivot.selesai = '2017-04-16',pivot.hours,0)) d3,
SUM(IF(pivot.selesai = '2017-04-23',pivot.hours,0)) d4,
SUM(IF(pivot.selesai = '2017-04-30',pivot.hours,0)) d5,
sum(pivot.hours) hours
from pivot
group by CODEPivot,categoryPivot
) p on
m.codeS = p.CODEPivot
and m.version = p.categoryPivot
left join whweek w on
w.Code = p.CODEPivot
and w.version = p.CategoryPivot
group by codes,version with rollup
+-------+---------+----------+--------+-----------+-----------+---------+------------+------------+------------+------------+------------+-------+----------+
| codes | version | headount | reghrs | hrsbudget | workhours | wreghrs | 02-04-2017 | 09-04-2017 | 16-04-2017 | 23-04-2017 | 30-04-2017 | Total | RsltBdgt |
+-------+---------+----------+--------+-----------+-----------+---------+------------+------------+------------+------------+------------+-------+----------+
| FII | YAA | 3 | 432 | 35 | 144 | 432 | 28 | 28 | 14 | 24 | 41 | 135 | 100 |
| FII | NULL | 3 | 432 | 35 | 144 | 432 | 28 | 28 | 14 | 24 | 41 | 135 | 100 |
| IDS | YAA | 3 | 432 | 35 | 144 | 432 | 8 | 0 | 0 | 0 | 0 | 8 | -27 |
| IDS | NULL | 3 | 432 | 35 | 144 | 432 | 8 | 0 | 0 | 0 | 0 | 8 | -27 |
| RRT | BKK | 1 | 144 | 12 | 144 | 144 | 8 | 3 | 16 | 15 | 32 | 74 | 62 |
| RRT | WESEL | 1 | 144 | 12 | 144 | 144 | 0 | 14 | 7 | 2 | 0 | 23 | 11 |
| RRT | YAA | 9 | 1296 | 104 | 144 | 1296 | 67 | 98 | 135 | 103 | 119 | 522 | 418 |
| RRT | NULL | 11 | 1584 | 128 | 432 | 1584 | 75 | 115 | 158 | 120 | 151 | 619 | 491 |
| NULL | NULL | 17 | 2448 | 198 | 720 | 2448 | 111 | 143 | 172 | 144 | 192 | 762 | 564 |
+-------+---------+----------+--------+-----------+-----------+---------+------------+------------+------------+------------+------------+-------+----------+
9 rows in set (0.00 sec)

MySQL get multiple rows into columns

I have a table called visits where concat(s_id, c_id) is unique and id is the primary key. s_id is the ID number of a website and c_id is a campaign ID number. I want to show all the hits each campaign is getting and group by the site. I want each site on a single row
+-----+------+------+------+
| id | s_id | c_id | hits |
+-----+------+------+------+
| 1 | 13 | 8 | 245 |
| 2 | 13 | 8 | 458 |
| 3 | 13 | 3 | 27 |
| 4 | 13 | 4 | 193 |
| 5 | 14 | 1 | 320 |
| 6 | 14 | 1 | 183 |
| 7 | 14 | 3 | 783 |
| 8 | 14 | 4 | 226 |
| 9 | 5 | 8 | 671 |
| 10 | 5 | 8 | 914 |
| 11 | 5 | 3 | 548 |
| 12 | 5 | 4 | 832 |
| 13 | 22 | 8 | 84 |
| 14 | 22 | 1 | 7 |
| 15 | 22 | 3 | 796 |
| 16 | 22 | 4 | 0 |
+----+------+------+-------+
I would like to have the following result set:
s_id | hits | hits | hits| hits
13 | 245 | 458 | 27 | 193
14 | 320 | 183 | 783 | 226
5 | 671 | 914 | 548 | 832
22 | 84 | 7 | 796 | 0
Here is what I have tried which does not pull all the hits columns back.
SELECT v.*, v2.* FROM visits v
INNER JOIN visits v2 on v.s_id = v2.s_id
GROUP BY s_id
How can I get multiple rows into columns?
If your'e data set is not crazy huge and you are just trying to get the multiple rows as a single row.... one way to do this...
SELECT
s_id,
GROUP_CONCAT(hits SEPARATOR ',') as hits_list
FROM
visits
GROUP BY s_id
Since it doesn't use any joins or subqueries etc, i find this way to be quite fast.
you can later split/explode the data based on the ',' separator in PHP or whatever language you are using.
$hits = explode($hits_list, ','); //get them in an array

count and group by first n(minimum) items in group

I've been through several of the "n from M" type solutions and not been able to get close to what I'm after though it's possible that the question has been asked before in some other format.
I've tried examples from this MySQL Group By with top N number of each kind and this http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/ none of which appear to apply to what I'm trying to do.
What I'm trying to do is determine the best teams in a running race, individual runners aren't a problem, gender, age categories can be taken care of. The rules for team prizes are based on membership of a club.
Clubs must have at least 3 runners to qualify for the team competition.
Only the first 3 runners from each club count towards the competition.
The team position is determined by the sum of the qualifying runners so runners from club A who finish 2nd 9th & 10th get 21pts, runners from club B who finish 4th, 5th & 6th get 15pts, etc.
I've a table with the following fields:
+---------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| runner_id | int(11) | YES | | NULL | |
| club_id | int(11) | YES | | NULL | |
| race_id | int(11) | YES | | NULL | |
| race_number | int(11) | YES | | NULL | |
| category | varchar(20) | YES | | NULL | |
| finish_time | int(11) | YES | | NULL | |
| race_position | int(11) | YES | | NULL | |
+---------------+-------------+------+-----+---------+----------------+
Only club_id and race_position are relevant to the query. runner_id, club_id and race_id are foreign keys and I need to be able to extract data (given_name, family_name, age, club_name, etc.) from those tables when creating the results.
This is typical data:
+----+-----------+---------+---------+-------------+-----------+-------------+---------------+
| id | runner_id | club_id | race_id | race_number | category | finish_time | race_position |
+----+-----------+---------+---------+-------------+-----------+-------------+---------------+
| 53 | 26 | 1 | 85 | 17 | Msenior | 1666 | 11 |
| 35 | 39 | 1 | 85 | 4 | Munder_18 | 1503 | 4 |
| 63 | 61 | 2 | 85 | 27 | Mvet_50 | 1610 | 9 |
| 42 | 46 | 2 | 85 | 11 | Lvet_40 | 1773 | 14 |
| 38 | 42 | 2 | 85 | 7 | Lunder_18 | 1793 | 17 |
| 56 | 36 | 9 | 85 | 20 | Msenior | 1561 | 6 |
| 44 | 48 | 9 | 85 | 13 | Msenior | 1667 | 12 |
| 64 | 62 | 9 | 85 | 28 | Msenior | 1660 | 10 |
| 49 | 52 | 9 | 85 | 18 | Msenior | 1432 | 1 |
| 47 | 51 | 10 | 85 | 16 | Msenior | 1779 | 15 |
| 61 | 59 | 11 | 85 | 25 | Mvet_50 | 1502 | 3 |
| 33 | 38 | 11 | 85 | 2 | Munder_18 | 1440 | 2 |
| 65 | 63 | 11 | 85 | 29 | Mvet_40 | 1566 | 8 |
| 54 | 54 | 12 | 85 | 19 | Msenior | 1785 | 16 |
| 58 | 56 | 12 | 85 | 23 | Msenior | 1546 | 5 |
| 37 | 41 | 12 | 85 | 6 | Munder_18 | 1668 | 13 |
| 45 | 49 | 14 | 85 | 14 | Mvet_50 | 1565 | 7 |
+----+-----------+---------+---------+-------------+-----------+-------------+---------------+
What I want to end up with is this:
+----+-----------+---------+---------+-------------+-----------+-------------+---------------+
| id | runner_id | club_id | race_id | race_number | category | finish_time | race_position |
+----+-----------+---------+---------+-------------+-----------+-------------+---------------+
| 33 | 38 | 11 | 85 | 2 | Munder_18 | 1440 | 2 |
| 61 | 59 | 11 | 85 | 25 | Mvet_50 | 1502 | 3 |
| 65 | 63 | 11 | 85 | 29 | Mvet_40 | 1566 | 8 |
| 49 | 52 | 9 | 85 | 18 | Msenior | 1432 | 1 |
| 56 | 36 | 9 | 85 | 20 | Msenior | 1561 | 6 |
| 64 | 62 | 9 | 85 | 28 | Msenior | 1660 | 10 |
| 58 | 56 | 12 | 85 | 23 | Msenior | 1546 | 5 |
| 37 | 41 | 12 | 85 | 6 | Munder_18 | 1668 | 13 |
| 54 | 54 | 12 | 85 | 19 | Msenior | 1785 | 16 |
| 63 | 61 | 2 | 85 | 27 | Mvet_50 | 1610 | 9 |
| 42 | 46 | 2 | 85 | 11 | Lvet_40 | 1773 | 14 |
| 38 | 42 | 2 | 85 | 7 | Lunder_18 | 1793 | 17 |
+----+-----------+---------+---------+-------------+-----------+-------------+---------------+
So even though runner_id of 52 won the race, he wasn't in the winning team.
I'm running all this under Codeigniter/Datamapper ORM but I can pass a full SQL query string down through this layer.
I hope all this makes sense.
MySQL lacks important features to get this solved (CTEs, window functions) but you can workaround them with some user defined variables and by paying the performance cost:
SELECT s1.id, s1.runner_id, s1.club_id, s1.race_id, s1.race_number, s1.category,
s1.finish_time, s1.race_position
FROM (
SELECT t1.*,
#club_rank := if(#prev_club = t1.club_id, #club_rank + 1, 1) club_rank,
#prev_club := t1.club_id
FROM t t1
CROSS JOIN (SELECT #prev_club := NULL, #club_rank := 1) init
ORDER BY t1.club_id, t1.race_position
) s1
JOIN (
SELECT club_id, count(*) teamSize, sum(race_position) teamPosition FROM t
GROUP BY club_id
) s2 ON s1.club_id = s2.club_id
WHERE club_rank <= 3 AND teamSize >= 3
ORDER BY teamPosition, race_position
Output:
| ID | RUNNER_ID | CLUB_ID | RACE_ID | RACE_NUMBER | CATEGORY | FINISH_TIME | RACE_POSITION |
|----|-----------|---------|---------|-------------|-----------|-------------|---------------|
| 33 | 38 | 11 | 85 | 2 | Munder_18 | 1440 | 2 |
| 61 | 59 | 11 | 85 | 25 | Mvet_50 | 1502 | 3 |
| 65 | 63 | 11 | 85 | 29 | Mvet_40 | 1566 | 8 |
| 49 | 52 | 9 | 85 | 18 | Msenior | 1432 | 1 |
| 56 | 36 | 9 | 85 | 20 | Msenior | 1561 | 6 |
| 64 | 62 | 9 | 85 | 28 | Msenior | 1660 | 10 |
| 58 | 56 | 12 | 85 | 23 | Msenior | 1546 | 5 |
| 37 | 41 | 12 | 85 | 6 | Munder_18 | 1668 | 13 |
| 54 | 54 | 12 | 85 | 19 | Msenior | 1785 | 16 |
| 63 | 61 | 2 | 85 | 27 | Mvet_50 | 1610 | 9 |
| 42 | 46 | 2 | 85 | 11 | Lvet_40 | 1773 | 14 |
| 38 | 42 | 2 | 85 | 7 | Lunder_18 | 1793 | 17 |
Fiddle here.
A bit late as I've been indisposed.
I came up with an inelegant solution. I added a club_total column to the table. I then loop over the table with one query for each club getting the first N runners with a query like:
select * from entries where race_id=? and club_id=? LIMIT ? order by race_position;
I then ignore those clubs with less than N finishers and sum the race positions of the other clubs and write this value back to the table.
Finally I run another query to extract only those rows with club totals:
select * from entries where club_total > 0 and race_id=? order by club_total, race_position;
Like I said, it's not elegant and it's certainly not quick (I've not timed it) but it's only going to get run a handful of times a year on one machine and the record set is a couple of hundred rows at a maximum. With a small data set it's not appreciably slower than a simple query with the data being displayed via AJAX. Getting the job done in this case is more important than speed. I wouldn't use this method for any situation where performance was an issue

mysql: If a value appears more than once, get values from the others similar rows and create new columns

Here is an example of my table patients:
id | case_id | patient_id | syndrome | age |
------------------------------------------------------
1 | 23 | 24 | stable | 45 |
2 | 24 | 25 | stable | 64 |
3 | 25 | 24 | coronary | 46 |
4 | 26 | 27 | stable | 73 |
5 | 27 | 24 | stable | 48 |
6 | 28 | 25 | coronary | 67 |
7 | 29 | 40 | coronary | 68 |
If patient_id appears more than once, i only want to show the first row and add extra columns to this row containing the values of the others similar patient_ids.
For example
id | case_id | patient_id | syndrome | age | syndrome2 | age2 | syndrome3 | age3
--------------------------------------------------------------------------------------------
1 | 23 | 24 | stable | 45 | coronary | 46 | stable | 48
2 | 24 | 25 | stable | 64 | coronary | 67 | |
7 | 29 | 40 | coronary | 68 |
Is this feasible using mysql code?
Currently, I have this code:
SELECT *, count(patients.patient_id) as PatientsNo FROM patients
GROUP BY patient_id
HAVING COUNT(patient_id) > 1
Any suggestions please?
Thank you in advance,
Zinon

SQL/MySQL SELECT and average over certain values

I have to work with an analysis tool that measures the Web Service calls to a server per hour. These measurments are inserted in a database. The following is a snippet of such a measurement:
mysql> SELECT * FROM sample s LIMIT 4;
+---------+------+-------+
| service | hour | calls |
+---------+------+-------+
| WS04 | 04 | 24 |
| WS12 | 11 | 89 |
| WSI64 | 03 | 35 |
| WSX52 | 01 | 25 |
+---------+------+-------+
4 rows in set (0.00 sec)
As the end result I would like to know the sum of all web services completions per hour of day. Obviously, this can be easily done with SUM() and GROUP BY:
mysql> SELECT hour, SUM(calls) FROM sample s GROUP BY hour;
+------+------------+
| hour | SUM(calls) |
+------+------------+
| 00 | 634 |
| 01 | 642 |
| 02 | 633 |
| 03 | 624 |
| 04 | 420 |
| 05 | 479 |
| 06 | 428 |
| 07 | 424 |
| 08 | 473 |
| 09 | 434 |
| 10 | 485 |
| 11 | 567 |
| 12 | 526 |
| 13 | 513 |
| 14 | 555 |
| 15 | 679 |
| 16 | 624 |
| 17 | 796 |
| 18 | 752 |
| 19 | 843 |
| 20 | 827 |
| 21 | 774 |
| 22 | 647 |
| 23 | 533 |
+------+------------+
12 rows in set (0.00 sec)
My problem is that in old sets, the web service calls in the hours from [00-11] were already summed up. The simple statement as listed above would therefore lead to
mysql> SELECT hour, SUM(calls) FROM sample s GROUP BY hour;
+------+------------+
| hour | SUM(calls) |
+------+------------+
| 00 | 6243 | <------ sum of hours 00-11!
| 12 | 526 |
| 13 | 513 |
| 14 | 555 |
| 15 | 679 |
| 16 | 624 |
| 17 | 796 |
| 18 | 752 |
| 19 | 843 |
| 20 | 827 |
| 21 | 774 |
| 22 | 647 |
| 23 | 533 |
+------+------------+
13 rows in set (0.00 sec)
This is an undesirable result. To make the old sets [00,12,...,23] comparable to the new sets [00,01,...,23] I would like to have one statement that averages the value of [00] and distributes it over the missing hours, e.g.:
+------+------------+
| hour | SUM(calls) |
+------+------------+
| 00 | 6243/11 |
| 01 | 6243/11 |
[...]
| 12 | 526 |
[...]
| 23 | 533 |
+------+------------+
I can easily do this using temporary tables or views, but i don't know how to accomplish this without them.
Any ideas? Cause this is driving me crazy :P
You'll need a rowset with 12 rows in it to make a join.
The most simple solution will be combining 12 SELECT statements in a union:
SELECT COALESCE(morning.hour, sample.hour),
SUM(CASE WHEN morning.hour IS NULL THEN calls ELSE calls / 12 END) AS calls
FROM sample
LEFT JOIN
(
SELECT 0 AS hour
UNION ALL
SELECT 1
...
UNION ALL
SELECT 11
) AS morning
ON sample.hour = 0 AND sample.service IN ('old_service1', 'old_service2')
GROUP BY
1
You're probably best doing this with temp tables / views (I'd recommend a view over a temp table) or you will end up with a nasty case specific statement that will be a nightmare to manage over time.