mysql multiple column order - mysql

I'm using mysql database to store companies in the same city. And I'm fetching them by using below query. However it doesnt sort the result as I want it to be.
I want the resulting companies to be ordered like this: First priority is the companies which distances are lower than 6 KM and those companies must be the ordered by the FIELD(sectorid,....) parameter , second priority is the companies which distances are greater than 6KM and those companies must be ordered in the same way (with FIELD parameter)
But we couldnt find any solution to our problem. I'm showing the correct results and our query in below. I would be glad if someone points me to right direction
Example Result Must be Something Like This:
Company Name: Distance: SectorID:
CompanyA 3 10
CompanyB 2 11
CompanyC 4 13
CompanyX 8 10
CompanyY 7 11
CompanyZ 9 13
Our Current Query:
SELECT companies.id,
companies.name,
companies.summary,
companies.ratingScore,
companies.companyLogo,
companies.sectorId,
companies.lattitude,
companies.longitude,
companies.address,
company_photos.photoUrl,
(SELECT EXISTS
(SELECT 1
FROM favorites
WHERE favorites.companyId = companies.id
AND userId = 109)) AS favorited,
(SELECT COUNT(*)
FROM ratings
WHERE companies.id = ratings.companyId
AND ratings.isReviewed = 1) AS comments,
(ACOS(SIN(RADIANS(41.212641))*SIN(RADIANS(lattitude))+COS(RADIANS(41.212641))*COS(RADIANS(lattitude))*COS(RADIANS(longitude)-RADIANS(29.020058))) *6371) AS distance
FROM companies
LEFT JOIN company_photos ON company_photos.companyId = companies.id
AND company_photos.photoOrder = 0
WHERE companies.isActivated = 1
AND companies.isReviewed = 1
AND companies.isCustomer = 1
AND companies.isBlocked = 0
ORDER BY FIELD (sectorId, 10, 11, 13, 7, 12, 2, 15, 17), distance
The Result I'm Getting Is:
Company Name: Distance: SectorID:
CompanyA 3 10
CompanyX 8 10
CompanyB 2 11
CompanyY 7 11
CompanyC 4 13
CompanyZ 9 13
Thanks

order by case when distance<6 then 1 else 2 end, --sorts distance < 6 first and then distance > 6
sectorid --with in each group specified above sorting is on sectorid

Related

How to get the average number of cities crossed [duplicate]

This question already has an answer here:
How to write these complex MySQL queries?
(1 answer)
Closed 1 year ago.
I am a beginner at databases. I need to write some SQL queries.
The tables are:
Expedition(id, number, id_captain, id_ship, id_heros)
City(id, name)
Heros(id, family_name, first_name)
Step(id, index, id_expedition, id_city)
sample data :
'Table expedition'
id
number
id_captain
id_ship
id_hero
1
1
1
10
8
2
2
2
1
5
3
3
1
8
3
4
4
10
9
6
5
5
5
7
4
6
6
6
5
4
7
7
7
3
7
8
8
8
2
8
9
9
9
1
3
10
10
1
4
2
11
11
6
3
1
12
12
8
6
1
13
13
5
8
6
14
14
4
9
9
15
15
3
10
4
16
16
10
2
2
17
17
9
3
3
18
18
8
7
7
19
19
9
8
10
20
20
7
2
2
table 'heros'
id
family_name
first_name
1
familyname1
firstname1
2
familyname2
firstname2
3
familyname3
firstname3
4
familyname4
firstname4
5
familyname5
firstname5
6
familyname6
firstname6
7
familyname7
firstname7
8
familyname8
firstname8
9
familyname9
firstname9
10
familyname10
firstname10
query1: The family (based on the family name) with the least travelling (the fewest cities different crossings).
i have done this for the first query:
select expedition.id, id_hero, heros.family_name as Famille_expedition, count(distinct id_city) as city_count
from expedition, step, heros
where expedition.id=step.id and expedition.id_hero=heros.id
group by id_hero
having city_count =
(select count(distinct id_city) as min_city_count
from expedition, step
where expedition.id=step.id
group by id_hero
order by min_city_count asc
limit 1);
query2: The average of cities crossed by an expedition
I have no idea how to answer the second one.
Well, first ask yourself what information do you need to answer your question?
From your question, I'd say the average number of crossings is just the sum of all entries in the steps table, divided by the number of expeditions, since in each step, one city is visited and the average of all visits is what you are looking for:
SELECT (
(SELECT COUNT(s.id_city)
FROM step AS s) /
(SELECT COUNT(e.id)
FROM expedition AS e) ) AS total_average__cities
That being said, it depends on how exactly you define number of cities and crossing. Imagine the following example data for the table step:
id
idx
id_expedition
id_city
1
1
1
1
2
2
1
5
3
3
1
3
4
1
2
5
5
2
2
9
6
1
3
8
7
2
3
5
8
3
3
9
9
4
3
5
10
5
3
8
The table lists the steps for three expeditions. Expedition 1 goes from one city via another to a third. Expedition 2 goes directly from one city to another. And expedition 3 goes through several cities and visits one city twice along the way and also returns to the city that it started in.
The average number of cities over all these steps is (3 + 2 + 5 [cities in all steps]) / 3 [expeditions] = 3.3333. That is the result of the above query.
Now, if you define number of cities as meaning unique cities for each expedition, expedition 3 only visits 3 cities instead of 5. Then your average calculates as (3 + 2 + 3 [unique cities/expedition in all steps]) / 3 [expeditions] = 2.6666. The according query needs to count the distinct cities within each expedition before building the average:
SELECT (
(SELECT SUM(cnt) FROM (SELECT COUNT(DISTINCT s.id_city) AS cnt
FROM step AS s
GROUP BY s.id_expedition) t) /
(SELECT COUNT(e.id)
FROM expedition AS e) ) AS total_average__cities
Now, if you define crossing as only covering cities along the way, expedition 1 only crosses 1 city and expedition 2 crosses none at all.
Then your query also needs to look differently. You need to filter the all cities to exclude the first and the last for each expedition. The subquery could look like this:
SELECT s.* FROM step s
JOIN ( SELECT id_expedition,
MAX(idx) AS max_idx,
MIN(idx) AS min_idx
FROM step s
GROUP BY id_expedition) minmax
ON s.id_expedition = minmax.id_expedition
AND s.idx > minmax.min_idx
AND s.idx < minmax.max_idx
So for the case that you want the number of cities crossed excluding start and stop, your average would be computed as (1 + 0 + 3 [intermediate cities in all steps]) / 3 [expeditions] = 1.3333. The according query would be
SELECT (
(SELECT COUNT(s.id_city)
FROM step s
JOIN ( SELECT id_expedition,
MAX(idx) as max_idx,
MIN(idx) as min_idx
FROM step s
GROUP BY id_expedition) minmax
ON s.id_expedition = minmax.id_expedition
AND s.idx > minmax.min_idx
AND s.idx < minmax.max_idx) /
(SELECT COUNT(e.id)
FROM expedition AS e) ) AS total_average__cities
Finally, in case you want to both exclude start and stop and only want to count unique cities, your average would be computed as (1 + 0 + 2 [unique intermediate cities in all steps]) / 3 [expeditions] = 1. The following query combines the two approaches from above:
SELECT (
(SELECT SUM(cnt) FROM (SELECT COUNT(DISTINCT id_city) AS cnt
FROM step s
JOIN ( SELECT id_expedition,
MAX(idx) AS max_idx,
MIN(idx) AS min_idx
FROM step s
GROUP BY id_expedition) minmax
ON s.id_expedition = minmax.id_expedition
AND s.idx > minmax.min_idx
AND s.idx < minmax.max_idx
GROUP BY s.id_expedition) t) /
(SELECT COUNT(e.id)
FROM expedition AS e) ) AS total_average_cities
You can test all these queries in this db<>fiddle.

Max & AVG MYSQL INNER JOIN for 2 tables

I have 2 tables (example):
users:
ID company_ID
1 7
2 6
3 7
activity_rewards:
user_ID points activity_type_ID
1 1 7
1 2 7
1 1 7
1 1 8
2 1 7
2 1 7
2 2 8
2 1 7
3 2 7
3 1 7
3 2 8
3 1 8
(There are also tables for company and activity_types, but they shouldn't be relevant here)
I need a MYSQL query that will sum the total points for each user WHERE all users have a certain company_ID and for a certain activity_type_ID AND it will return the MAX and the AVG of the sum of all the users points
I have for example:
SELECT SUM(activity_rewards.points) AS totalpoints,
MAX(activity_rewards.points) AS maxpoints,
AVG(activity_rewards.points) AS avgpoints
FROM activity_rewards
INNER JOIN users
ON activity_rewards.user_ID = users.ID
WHERE ( (users.company_ID = "7") && (activity_rewards.activity_type_ID LIKE '8') )
In the example query only 3 results are involved. They are:
user_ID points activity_type_ID
1 1 8
3 2 8
3 1 8
I want to get:
user 1 has 1 point
user 3 has 3 points
Max is 3 average is 2
instead I'm getting Max is 2 and average is 1.33
The results are in line with your query, but your query is not in line with your requirements.
Your query calculates the max and the average of points across the relevant records. But you seem to want the max and the average of the points summed by user id.
This means that you need to calculate the sum of points per user in a subquery and then calculate the max and average in the outer query.
SELECT SUM(sumpoints) as totalpoints, max(sumpoints) as maxpoints, avg(sumpoints) as avgpoints
FROM
(SELECT users.ID, SUM(activity_rewards.points) AS sumpoints
FROM activity_rewards
INNER JOIN users ON activity_rewards.user_ID = users.ID
WHERE users.company_ID = 7 and activity_rewards.activity_type_ID = 8
GROUP BY users.ID) t
Do it like this:
SELECT MAX(sum_points) max, AVG(sum_points) avg, SUM(sum_points) sum_all FROM (SELECT SUM(t2.points) sum_points FROM users t1 JOIN activity_rewards t2 ON (t1.ID = t2.user_ID) WHERE ( (t1.company_ID = "7") AND (t2.activity_type_ID = '8') ) GROUP by t1.ID) as summation

MYSQL Left Join COUNTS AND SUMS from multiple tables

I have tried looking for similar questions here in SO and have not found any so far. Feel free to tag the duplicate or similar question to this one if you'd like.
I want to join several columns to display a report.
I have 3 tables.
Users
ID Name
1 Chris
2 John
3 Rick
InMessages
ID Content
1 Hello1
2 Hello2
3 Response1
4 Response2
5 Hello3
6 Hello4
OutMessages
ID UserID InMessageID Content CurrentRate
1 1 1 ReplyHello1 10
2 2 2 Reply1Hello2 10
3 3 2 Reply2Hello2 10
4 3 2 Reply3Hello2 10
5 1 3 ReplyResponse1 10
6 2 4 ReplyResponse2 10
7 1 5 ReplyHello3 4
8 3 6 ReplyHello4 4
And the report I'd like to see would be something like:
User InMessagesCount OutMessagesCount Rate10 Rate4
Chris 3 3 2 1
John 2 2 1 1
Rick 2 3 2 1
I have tried the existing queries like:
count(distinct InMessages.ID) as InMessagesCount,
count(distinct OutMessages.ID) as OutMessagesCount
But I am stuck as to how I could make the one for the rates.
Thanks in advance!
You issue seems to be that your multiple joins are creating duplicate records, hence needing DISTINCT in your current counts. But you can't use that for counting the number of occurrences of a non distinct field
As such I would be tempted to try something like this:-
COUNT(DISTINCT IF(OutMessages.CurrentRate = 10, OutMessages.ID, NULL) ) AS Rate10,
COUNT(DISTINCT IF(OutMessages.CurrentRate = 4, OutMessages.ID, NULL) ) AS Rate4
Ie, if the id is 10 then use the id, otherwise use NULL. COUNT like this should (I think) only count the non null values.
For Mysql you can use sum() with expression so it will evaluate as a count that you need for your 2 rates
select
u.Name,
count(distinct o.InMessageID) as InMessagesCount,
count(distinct o.ID) as OutMessagesCount,
sum(o.CurrentRate = 10) rate10,
sum(o.CurrentRate = 4) rate10
from OutMessages o
joins users u on(u.id = o.user_id)
group by u.id
You can sum up those records that match a specific rate:
SUM(IF(OutMessages.CurrentRate = 10, 1,0)) AS Rate10,
SUM(IF(OutMessages.CurrentRate = 4, 1,0)) AS Rate4
You can use IF and SUM, like this:
SELECT UserID,
COUNT(DISTINCT InMessages.ID) AS InMessagesCount,
COUNT(DISTINCT OutMessages.ID) AS OutMessagesCount,
SUM(IF(OutMessages.CurrentRate = 10, 1, 0)) AS Rate10,
SUM(IF(OutMessages.CurrentRate = 4, 1, 0)) AS Rate4
FROM OutMessages
GROUP BY UserID

Inner join on a distinct set of parameters

I've tried a few of the similar SO questions, but I can't seem to figure it out.
On the first inner join, I only want to bring in DISTINCT function columns code and serial_id. So when I do my SUM selects, it calculates one per distinct. Ie there are multiple rows with the same func.code and func.serial_id. I only want 1 of them.
SELECT
sl.imp_id,
lat.version,
SUM(IF(lat.status = 'P',1,0)) AS powered,
SUM(IF(lat.status = 'F',1,0)) AS functional
FROM slots sl
INNER JOIN functions func ON sl.id = func.slot_id
INNER JOIN latest_status lat ON lat.code = func.code
AND lat.serial_id = func.serial_id
WHERE sl.id=55
GROUP BY sl.imp_id, lat.version
EDIT 2 - sample data explanation -------------------
slots - id, imp_id, name
functions - id, slot_id, code, serial_id
latest_status - id, code, serial_id, version, status
**slots**
id imp_id name
1 5 'the name'
2 5 'another name'
3 5 'name!'
4 5 'name!!'
5 5 'name!!!'
6 5 'testing'
7 5 'hi'
8 5 'test'
**functions**
id slot_id code serial_id
1 1 11HRK 10
2 2 22RMJ 11
3 3 26OLL 01
4 4 22RMJ 00
6 6 11HRK 10
7 7 11HRK 10
8 8 22RMJ 00
**latest_status**
id code serial_id version status
1 11HRK 10 1 F
1 11HRK 10 2 P
3 22RMJ 11 1 P
4 22RMJ 11 2 F
5 26OLL 01 1 F
6 26OLL 01 2 P
7 22RMJ 00 1 F
8 22RMJ 00 2 F
After running the query, the result should look like this:
imp_id version powered functional
5 1 1 3
5 2 2 2
The function table gets rolled up based on the code, serial_id. 1 row per code, serial_id.
It then gets joined onto the latest_status table based on the serial_id and code, which is a one (functions) to many (latest_status) relationship, so two rows come out of this, one for each version.
How about using DISTINCT?
SELECT
SUM(IF(lat.status = 'P',1,0)) AS powered,
SUM(IF(lat.status = 'F',1,0)) AS functional
FROM slots sl
INNER JOIN (Select DISTINCT id1, code, serial_id from functions) f On sl.rid = f.id1
INNER JOIN latest_status lat ON lat.code = f.code
AND lat.serial_id = f.serial_id
WHERE sl.id=55
GROUP BY sl.imp_id, lat.version
If you want only the distinct code and serial_id, you need to group by those not the imp_id and version. And end up with something like
SELECT
SUM(IF(lat.status = 'P',1,0)) AS powered,
SUM(IF(lat.status = 'F',1,0)) AS functional
FROM slots sl
INNER JOIN functions func ON sl.rid = func.id1
INNER JOIN latest_status lat ON lat.code = func.code
AND lat.serial_id = func.serial_id
WHERE sl.id=55
GROUP BY func.code, func.serial_id
However, this could all be rubish, without more data as tgo what some of those other columns are, but they dont seem to be the ones you wanted to group by.

A database question for storing and computing with historical data

I have an application that polls different bins at different times and computes the number of widgets in each bin based on the weight. Polling is done every few minutes and the result is timestamped and added to a MySQL table. The table contains the 3 columns below. The example shows 3 bins (A,B and C) but there could be anywhere from 1 to 10 bins for the same widget. (Tiny widgets could be just in 1 or 2 bins and larger widgets may take up more bins)
timestamp bin Widget_Count
--------------------------
1 A 8
2 B 7
3 C 4
4 A 1
5 B 3
6 C 5
7 A 6
8 B 7
9 C 2
The application needs to generate a 'stock history' report - this would involve computing at each timestep what is the total no. of items from all bins at that timestep. For this example, the report will contain only the timestamp column and the CountHistory column (last column) below (the other columns are shown just to show the computations)
At time 1, A has been polled and has 8 widgets. B and C have not been polled. So the total is 8.
At time 2, B has been polled and has 7 widgets. So the total is 17
At time 3, C has been polled and has 4 widgets. So the total is 19
At time 4, A polled again and has only 1 widget. So the total is now 1+4+7=12
..and so on.
timestamp bin Widget_ A B C CountHistory
Count (stock flow)
--------------------------------------------------------
1 A 8 8 0 0 8
2 B 7 8 7 0 15
3 C 4 8 7 4 19
4 A 1 1 7 4 12
5 B 3 1 3 4 8
6 C 5 1 3 5 9
7 A 6 6 3 5 14
8 B 7 6 7 5 18
9 C 2 6 7 2 15
I would appreciate any help on how best to go about this. I tried to create a temporary table, scroll through each record using a cursor but could not get the correct queries.
(I had asked a related question before but I did not frame the question correctly and goofed up the example as well. Need Help with queries (views) in Microsoft Access)
I think this will do what you need. The inner query finds the most recent timestamp at which each bin was changed for each timestamp.
SELECT m.timestamp, SUM(w.Widget_Count) AS CountHistory
FROM (
SELECT a.timestamp, b.bin, MAX(b.timestamp) AS intMostRecentTimestamp
FROM WidgetTable a
CROSS JOIN WidgetTable b
WHERE a.timestamp >= b.timestamp
GROUP BY a.timestamp, b.bin, a.Widget_Count
) m
INNER JOIN WidgetTable w ON m.intMostRecentTimestamp = w.timestamp
GROUP BY m.timestamp
ORDER BY m.timestamp
Without an actual model (DDL) and data (including expected output), I'm not sure I would attempt to provide actual, syntax-checked sample SQL. And although I do think this would be easy to do in application code or with a cursor, I think it could be done in a single query.
That said, it would be something like this--consider this 100% pseudocode:
SELECT
timestamp,
bin,
widget_count,
(SELECT A that is most recent and <= current row's timestamp) A,
(SELECT B that is most recent and <= current row's timestamp) B,
(SELECT C that is most recent and <= current row's timestamp) C,
(SELECT sum of last three columns) CountHistory
FROM
widget_bin_table
It could probably be done with joins instead of subqueries, and joins would liekly be more efficient. But you get the idea.
Edit: ok, the question got the brain blood flowing, and I couldn't rest until I solved it to my satisfaction. Then I came back to post an update and Paul had already answwered with a more elegent solution. :)
Here is my solution with working SQL (using SQL Server) in case anyone cares:
SELECT
timestamp, bin, widget_count, A, B, C, A + B + C CountHistory
FROM
(
SELECT
wb.timestamp,
wb.bin,
wb.widget_count,
ISNULL((SELECT TOP 1 widget_count FROM widget_bin wb1 WHERE wb1.bin = 'A'
AND wb1.timestamp <= wb.timestamp ORDER BY wb1.timestamp DESC), 0) A,
ISNULL((SELECT TOP 1 widget_count FROM widget_bin wb1 WHERE wb1.bin = 'B'
AND wb1.timestamp <= wb.timestamp ORDER BY wb1.timestamp DESC), 0) B,
ISNULL((SELECT TOP 1 widget_count FROM widget_bin wb1 WHERE wb1.bin = 'C'
AND wb1.timestamp <= wb.timestamp ORDER BY wb1.timestamp DESC), 0) C
FROM
widget_bin wb
) sub