MYSQL: Total values except highest and lowest then multiply - php

I have stumbled upon a problem that I can't get my head around.
I have the following topic related tables:
dive(ID,Type,Diver_id,Difficulty,Contest_id)
results(ID,Points,Judge_Id,Dive_id)
divers(ID,Name,Country)
My SELECT statement is meant to list the diver.Name, diver.Country and "total points"
The total points is supposed to be (total += ((dive_total -min -max)*difficulty))
Let's say a diver did 3 dives. Every dive has been rated by ~4 judges and every dive has it's own difficulty. The lowest/highest score per dive is supposed to be removed. Then added together, multiplied with the difficulty and then added to the persons total points.
I have got a list that shows diver.Name, diver.Country and Total.
But the total is only the actual total without the subtraction and multiplication.
Query:
SELECT
divers.Name,
divers.Country,
SUM(results.Points) AS Total
FROM results
INNER JOIN dive
ON results.Dive_id = dive.ID
INNER JOIN divers
ON dive.Diver_id = divers.ID
WHERE dive.Contest_id = '1'
GROUP BY divers.Name
ORDER BY Total DESC
Don't mind the contest_id. It's always '1'.
Please ask for more information if needed. Maybe the question is very silly. I am not a advanced "database guy". Sorry for bad English in advance.
(It is later printed in php/html. Maybe a language combination between mysql and php is a better way to move forward?)

I think what you want is this
SELECT
divers.Name,
divers.Country,
((SUM(results.Points) - MAX(results.Points) - MIN(results.Points)) * dive.Difficulty) AS Total
FROM results
INNER JOIN dive
ON results.Dive_id = dive.ID
INNER JOIN divers
ON dive.Diver_id = divers.ID
WHERE dive.Contest_id = '1'
GROUP BY divers.Name
ORDER BY Total DESC
Otherwise I would just change the SELECT to include:
MIN(results.Points) as maxPoints,
MAX(results.Points) as minPoints,
SUM(results.Points) as totalPoints
and then do the math side of it using PHP

Thank you ode2k! Very helpful. The end result with your code + some GROUP BY
SELECT
divers.Name,
divers.Country,
SUM(results.Points * dive.Difficulty) - MAX(results.Points * dive.Difficulty) - MIN(results.Points * dive.Difficulty) AS Total
FROM results
INNER JOIN dive
ON results.Dive_id = dive.ID
INNER JOIN divers
ON dive.Diver_id = divers.ID
WHERE dive.Contest_id = '1'
GROUP BY divers.Name,
dive.ID,
dive.Difficulty,
results.Dive_id
ORDER BY Total DESC
And then I added the the scores together in PHP to get every name just once.

Related

MYSQL combining results into single item for display

I'm currently having an issue where I run a query on multiple tables getting the results, but they are all being considered independent. I've tried a couple ways of combining them, but because my SQL knowledge is limited I can't seem to get what I want to happen.
SELECT DISTINCT t.*, s.quantity, s.rrp, ts.thumbnail, ts.bigpic, t.rating
FROM tyres t
INNER JOIN stocklevels s
ON t.stockcode = s.stockcode
LEFT JOIN tyre_treads ts
ON t.treadid = ts.recid
LEFT JOIN reseller r
ON s.city=r.recid
WHERE s.quantity> 0 AND s.rrp > 0
I've tried adding GROUP BY t.recid and a couple other basic solutions but this doesn't seem to work. I've added a couple images which might help.
As you can see the bottom Toyo tyres are the same, just with varying cities and quantities.
Here they are on the website.
I'm wanting to combine they so that they say minimum 6 in stock and shows only once on the site.
As long as there is at least one column in your SQK resukt, which contains different values (like city in your example for "the same" tire, group by won't work. You must adapt your SQL statement in a way, that it only picks columns with the same values. Especially, you should remove the t.* from your sql and name all columns (You then will not need the distinct anymore).
Then, you sum over quantity to get the combined value for this column as wanted.
SELECT r.recid, sum(s.quantity), s.rrp, ts.thumbnail, ts.bigpic, t.rating
FROM tyres t
INNER JOIN stocklevels s
ON t.stockcode = s.stockcode
LEFT JOIN tyre_treads ts
ON t.treadid = ts.recid
LEFT JOIN reseller r
ON s.city=r.recid
WHERE s.quantity> 0 AND s.rrp > 0
GROUP BY recid

2table data sum and show order by product name

I have 2 table. one is "goods receive" table. another is "product_use_for" table.
I need sum (goods_receive.quantity) and sum (product_use_for.quentity)
and show it order by product name.
Actually I am new in php.I am confuse about this problem. I will lot of happy if anyone help me. please.
Please see this image >>
You need to use a LEFT JOIN, so that you'll get rows in the result even when there are no matches in the product_use_for table. And you need to do one of the sums in a subquery, otherwise you'll multiply the sum by the number of rows in the other table.
SELECT gr.p_name AS product_name, SUM(gr.quantity) AS stock_quantity, IFNULL(puf.quantity, 0) AS use_quantity
FROM goods_receive AS gr
LEFT JOIN (SELECT p_name, SUM(quantity) AS quantity
FROM product_use_for
GROUP BY p_name) AS puf
ON gr.p_name = puf.p_name
GROUP BY gr.p_name
DEMO

MySQL select statement - How to calculate the current ranking

I have a table called user_rankings where votes (voted) are stored for each user. I want to display the current ranking of users (this week) that depends on how much votes the user got.
example to clarify:
RANK-NR, USERNAME, VOTED,
1, name1, 18 times
2, name1, 16 times
(my ranking here), myname, 13 times
In this example my ranking should be 3. If I'd have 17 votes, I would be number 2. If there would be five users above me, I would be number 8. I guess you get the point.
Now I can display the ranking number easily with an incrementing $i in PHP. But I only want to show a list limited to ten users (a top ten list) and directly after that my current ranking, if I'm not already in that top ten list. So I'm just wondering how to get my exact ranking number using MySQL.
I'm assuming to have hundreds of users in this list with a different amount of votes.
This is my statement at the moment:
SELECT
`voted`
FROM `users_ranking`
WHERE
`uid`='".$_SESSION['uid']."'
AND
WEEKOFYEAR(`date`)=WEEKOFYEAR(NOW())
LIMIT 1
I can't give you the exact code, but i think the following can give you some idea
select 'RANK-NR', 'USERNAME', 'VOTED' from
(
select 'RANK-NR', 'USERNAME', 'VOTED', rank() over (order by 'voted' desc) as rank
from users_ranking
where
uid='".$_SESSION['uid']."'
AND
WEEKOFYEAR(date)=WEEKOFYEAR(NOW())
) as abc
where
rank<11
i think rank() over (order by<>) should work
I just found out myself that this solution works:
SELECT *
FROM
(
SELECT #ranking:= #ranking + 1 rank,
a.`uid`
FROM `users_ranking` a, (SELECT #ranking := 0) b
ORDER BY a.`votes` DESC
) s
WHERE `uid`='".$_SESSION['uid']."'
AND
WEEKOFYEAR(`date`)=WEEKOFYEAR(NOW())
LIMIT 1
OK, example to go with my comment. What you have will often work, but there is nothing to force MySQL to do the sort before it applies the ranking.
As such using an extra level of sub query would give you this (not tested). The inner sub query is getting all the user ids for the relevant week in the right order, while the next outer sub query applies the ranking to this ordered result set. The outer query just gets the single returned row you require.
SELECT c.rank, c.uid
FROM
(
SELECT #ranking:= #ranking + 1 rank, a.uid
FROM
(
SELECT uid, votes
FROM `users_ranking`
WHERE WEEKOFYEAR(`date`) = WEEKOFYEAR(NOW())
ORDER BY votes DESC
) a,
(SELECT #ranking := 0) b
) c
WHERE c.uid = '".$_SESSION['uid']."'
LIMIT 1
Another possibility avoiding the sub query and also avoiding the need for a variable is to do a join. This is (mis)using HAVING to slim down the result to the single row you are interested in. Down side of this solution is that if multiple users have the same score they will each get the same ranking.
SELECT b.uid, COUNT(a.uid)
FROM users_ranking a
LEFT OUTER JOIN users_ranking b
ON WEEKOFYEAR(a.`date`) = WEEKOFYEAR(b.`date`)
AND a.votes >= b.votes
GROUP BY b.uid
HAVING b.uid = '".$_SESSION['uid']."'
EDIT
To give the top 10 rankings:-
SELECT b.uid, COUNT(a.uid) AS rank
FROM users_ranking a
LEFT OUTER JOIN users_ranking b
ON WEEKOFYEAR(a.`date`) = WEEKOFYEAR(b.`date`)
AND a.votes >= b.votes
GROUP BY b.uid
ORDER BY rank
LIMIT 10
Although in this case it might be quicker to use a sub query. You could then put the LIMIT clause in the sub query with the ORDER BY, hence it would only need to use the variables to add a rank to 10 rows.
I am not sure how to combine that with the query for a single user, mainly as I am not sure how you want to merge the 2 results together.

mysql calculate percentage between two sub queries

I am trying to work out the percentage of a number of students who meet certain criteria.
I have 3 separate tables that I need to get data from, and then I need to get the total from one table (student) as the total of students.
Then I need to use this total, to divide the COUNT of the no of students in the 2nd query.
So basically I am trying to get a count of ALL the students that are in the DB first.
Then count the no of students that appear in my main query (the one returning the data).
Then I need to perform the calculation that will take the noOfStudents (2) and divide by the main total (24) (no of students in DB) then *100 to give me the percentage of students who have met the criteria in the main query.
This is what I have so far:
SELECT * FROM (
(
SELECT s.firstname, s.lastname, s.RegistrationDate, s.Email, d.ReviewDate,(r.description) AS "Viva" , COUNT(*) AS "No of Students"
FROM student s
INNER JOIN dates d
ON s.id=d.student_identifier
INNER JOIN reviews r
ON d.review_Identifier=r.id
WHERE r.description = "Viva Date"
GROUP BY s.student_identifier
ORDER BY s.student_identifier)
) AS Completed
WHERE Completed.ReviewDate BETWEEN '2012-01-01' AND '2014-12-01'
;
I need to output the fields following the second SELECT and this data in turn will be displayed via PHP/HTML code on a page (the BETWEEN dates will be sent via '%s').
I wondered if I should be using 2 separate queries and then getting the value (24) from the first query to perform the calculation in the second query, but I have not been able to work out how to save as 2 separate queries and then reference the first query.
I am also not sure if it is possible to display an overall % total at the same time as outputting the individual rows that meet the criteria?
I am trying to teach myself SQL, so I apologise if I have made any glaring mistakes/assumptions in any of the above, and would appreciate any advice that's out there.
Thank you.
Could you do this?
SELECT COUNT(*) as TotalPopulation,
COUNT(d.student_identifier='student') as TotalStudents,
COUNT(d.student_identifier='student')/ count(*) *100 as Percentage of students
from students s
inner join dates d
on s.id = d.student_identifier
inner join reviews r
on r.id = d.review_Identifier
WHERE d.ReviewDate BETWEEN '2012-01-01' AND '2014-12-01' and r.description = 'Viva Date';
You do not need first name last name if you are just looking for counts, necessarily.
This get's the count(*) of table, then whatever flag you use to identify a student in the second count(), you just had it grouped by before, which could give you wrong results considering there's much else in your select before aggregation.
You could also try:
SELECT d.student_identifier, s.firstname, s.lastname,
s.RegistrationDate, s.Email, d.ReviewDate,(r.description) AS "Viva"
FROM student s
INNER JOIN dates d
ON s.id=d.student_identifier
INNER JOIN reviews r
ON d.review_Identifier=r.id
WHERE r.description = "Viva Date" and d.ReviewDate BETWEEN '2012-01-01' AND '2014-12-01'
ORDER BY s.student_identifier
Now, if you want to return a list, that's the second one, if you want to return a count, you would use the first query and adjust to your student_identifier.

Complex SQL Query Help

Given a pretty standard set of related tables like Customers, Invoices and Line Items. I need to make a query like "Select all customers that have invoices that have 5 line items or more" or "Select all customers that have more than 2 invoices" or lastly "Select all customers that have line items totaling more than $100"
I'm doing this the hard way now (walking through all the records manually) and I know it's the most inefficient way but I don't know enough SQL to construct these queries. I'm using PHP5, MySQL and CakePHP 1.25.
Start by joining them all together:
select c.id
from customers c
left join invoices i on i.customer_id = c.id
left join lineitems li on li.invoice_id = i.id
group by c.id
To filter customers with more than 5 line items or more, add:
having count(li.id) >= 5
Filtering customers with two or more invoices is trickier, since we're joining the lineitems table. There may be multiple rows per invoice. So to count only unique invoices, we have to add distinct to the count, like:
having count(distinct i.id) >= 2
To filter customers with more than $100 in items, add:
having sum(li.cost) > 100
You can use math inside the sum, in case you're storing separate line item counts and prices:
having sum(li.itemcount * li.itemcost) > 100
For the first two you can use GROUP BY and HAVING COUNT(*)>4 and for the last one you can use SUM(field).
If you want SQL then please show your attempt.
Say you had:
customers.id
line_items.user_id
line_items.numItems
Query would look like:
SELECT * FROM customers
JOIN line_items
ON line_items.user_id WHERE line_items.numItems > 5
Your WHERE clause will change depending on what you wanted to return.
Haven't worked with SQL Joins in a while, but I think it's something like that.
Here is some help with the second one that should get you going:
SELECT customer_id, COUNT(*) AS num_invoices
FROM invoices
GROUP BY customer_id
HAVING COUNT(*) > 2

Categories