How to write a query for reatriving results - php

I need to find the points to make division (student results). It works fine but it selects only 7 the best scores to be appear on the table of results. I have two problems:
What can I do so that all scores to appear but only seven the best scores to be taken to make division?
This query is for a single student, how can I do to get results of the whole class?
SELECT sum(( case when((test+exam)/2)>=75 then 1 when ((test+exam)/2)>=65 then 2 when ((test+exam)/2)>=45 then 3 when ((test+exam)/2)>=30 then 4 when ((test+exam)/2)<30 then 5 END)) as points
FROM (SELECT test,exam FROM results u
WHERE regno='$regno' and term='$term' and form='$form' and year='$year' ORDER BY ((test+exam)/2) DESC LIMIT 0,7)k;

Related

PHP MariaDB PDO count duplicate values

Maybe a stupid question, but I can't find anything on it. I know you can do counts and some basic math with queries.
Is there a way to make the database count and return results with the highest or lowest number of occurrences?
Lets say you had all the pages of a library in a database and you wanted to know the top 5 characters used or maybe the least used.
I guess the easiest example would be the lottery. Lets say you had a table of past lottery results. Lets say you wanted to return a list of the top 10 most drawn numbers. Lets say the numbers can range from 1 to 100.
It wouldn't be very efficient to run the following query 100 different times and then run some php to sort the data.
SELECT COUNT(*) FROM mytable WHERE ball=1
Surely there must be a way to return a count of the most duplicated data.
Perhaps I am over thinking this. Anyway, thanks for any help in the matter.
That's called grouping, the group by clause will let you do that, make sure you don't just pull out the count but also the thing you're counting, in this case ball.
Get the lowest number occurrences:
SELECT ball, COUNT(*) FROM mytable GROUP BY ball ORDER BY ball ASC limit 1;
Get the highest number occurrences:
SELECT ball, COUNT(*) FROM mytable GROUP BY ball ORDER BY ball DESC limit 1;
If you want them all in one result you can use a union/union all:
(SELECT ball, COUNT(*) FROM mytable GROUP BY ball ORDER BY ball ASC limit 1)
UNION ALL
(SELECT ball, COUNT(*) FROM mytable GROUP BY ball ORDER BY ball DESC limit 1);
you can simply use
SELECT column, COUNT(*)
FROM mytable
GROUP BY column_name
this will count all of the existing entries for the specific column_name
you should use column before COUNT(*) so you know what has that many values.
i hope this helps!

Eliminate duplicate rows in mysql select statement

So hoping someone can help me on this. I've read so many questions on the same issue but none answer my question. I have the following SQL SELECT statement in my PHP code:
$sql = "
SELECT DISTINCT
pics.piclocation,
pics.picid,
rating
FROM
pics,
ratings
WHERE
pics.milfid = ratings.picid
ORDER BY
ratings.rating DESC
";
However as many know the DISTINCT keyword does not eliminate duplicate rows all the time. The problem is that the same picture is getting output often more than once i.e. pics.piclocation. The idea is each pic is rated from 1 to 5 which is then inserted into the ratings table.
Any ideas on how I can produce an output and eliminate duplication piclocation rows? I want the pictures to be listed based on which picture has the most 5 ratings if that helps any.
Thanks!
So if you only want images that have a rating of 5 then go ahead and select on that, then you can count the number of 5's that image has gotten and order by that. You will also then need to group by the unique identifer for an image which looks like its pics.milfid and ratings.picid.
SELECT
pics.piclocation,
pics.picid,
COUNT(ratings.rating) as nb_ratings
FROM
pics,
ratings
WHERE
pics.milfid = ratings.picid
AND ratings.rating = 5
GROUP BY
ratings.picid
ORDER BY
nb_ratings DESC
If instead you just wanted the highest rated pic, then you could SUM the ratings:
SELECT
pics.piclocation,
pics.picid,
SUM(rating) as total_rating
FROM
pics,
ratings
WHERE
pics.milfid = ratings.picid
GROUP BY
ratings.picid
ORDER BY
total_rating DESC

Performing arithmetic in SQL query

So I'm working on a script that awards "trophies" to the top 4 performers of a game. The table logs each "grab" attempt, the user that performed it, and whether it was successful. I'd like to create a script that is able to pull the top four off of percentage of successful grabs (attempts / successes)
Is something like this possible within the query itself using mysqli?
I have successfully accomplished the code already by just looping through each table entry, but with thousands of attempts per month it just seems like a clunky way to go about it.
Here is an example of a row in the table, I am attempting to grab the top four based off of monthlyTries/monthlySuccessful
id userId PetId PalId tries successfulGrabs monthlyTries MonthlySuccessful
5 44550 84564 3967 825 268 120 37
Assuming you have a success column that's either 1 or 0 you can sum the success and divide that by count(*) which is the total # of attempts
select user_id, sum(success)/count(*) percentage
from attempts a
group by user_id
order by percentage desc
limit 4
If the success column is not a 1/0 value you can use conditional aggregation
select user_id, sum(case when success = 'yes' then 1 else 0 end)/count(*) as percentage
from attempts a
group by user_id
order by percentage desc
limit 4
In MySQL, you can simplify the logic. If success takes on the values 0 and 1:
select a.user_id, avg(success) as percentage
from attempts a
group by a.user_id
order by percentage desc
limit 4;
Otherwise:
select a.user_id, avg(success = 'yes') as percentage
from attempts a
group by a.user_id
order by percentage desc
limit 4;

MySQL query to join 2 tables based on votes

I've a web application where I use 2 tables, one for storing product information and other for storing votes of each product.
Now I'd like to display the products based on the number of votes the products had got. Below is table structure
Products:
PRODUCT_ID TITLE
1 product1
2 product2
3 product3
4 product4
Votes:
PRODUCT_ID USER_ID
1 1
1 1
2 2
3 2
And I am expecting a result to display the products in descending order of the votes
PRODUCT_ID TITLE VOTES
1 product1 2
2 product2 1
3 product3 1
Currently I am using a query like this
SELECT p.product_id, p.title, count(*) AS total FROM products p
INNER JOIN votes v ON v.product_id = p.product_id GROUP BY p.product_id
ORDER BY count(*) DESC LIMIT 110
Products table has around 30,000 records and votes tables has around 90,000 records.
Now the problem is it takes a lot of time(randomly between 18 to 30 seconds). Since the number of records in the tables aren't that high, I wonder why it takes such a huge amount of time.
One thing noticed is when I run the query for the second time it fetches the results in few milli seconds which I think is the ideal time for a not so complex query like this.
Again I am pretty new to database side of programming.
I am not sure if there's anything wrong in the query or is it the table structure which isn't efficient (at least to fetch the records quickly).
First, your query is fine, although I would be inclined to format it differently:
SELECT p.product_id, p.title, count(*) AS total
FROM products p INNER JOIN
votes v
ON v.product_id = p.product_id
GROUP BY p.product_id
ORDER BY count(*) DESC
LIMIT 110;
As mentioned in another answer, an index on votes(product_id) would definitely help the query, if you don't have one already. Even with the improvement in the join performance, you still have the overhead of an aggregation. And, in MySQL that can be a lot of overhead.
If you are expecting lots and lots more votes -- getting into the millions -- then you may have to take another approach. One approach is to add a trigger to some table (perhaps the products table that keeps track of votes as they come in. Then the query would fly. Another approach would be to periodically summarize the data, similar to using a trigger but using a job instead.

Get multiple GROUP BY results per group, or use separate concatenated table

I am working on an auction web application. Now i have a table with bids, and from this table i want to select the last 10 bids per auction.
Now I know I can get the last bid by using something like:
SELECT bids.id FROM bids WHERE * GROUP BY bids.id ORDER BY bids.created
Now I have read that setting an amount for the GROUP BY results is not an easy thing to do, actually I have found no easy solution, if there is i would like to hear that.
But i have come up with some solutions to tackle this problem, but I am not sure if i am doing this well.
Alternative
The first thing is creating a new table, calling this bids_history. In this table i store a string of the last items.
example:
bids_history
================================================================
auction_id bid_id bidders times
1 20,25,40 user1,user2,user1 time1,time2,time3
I have to store the names and the times too, because I have found no easy way of taking the string used in bid_id(20,25,40), and just using this in a join.
This way i can just just join on auction id, and i have the latest result.
Now when there is placed a new bid, these are the steps:
insert bid into bids get the lastinserteid
get the bids_history string for this
auction product
explode the string
insert new values
check if there are more than 3
implode the array, and insert the string again
This all seems to me not a very well solution.
I really don't know which way to go. Please keep in mind this is a website with a lot of bidding's, they can g up to 15.000 bidding's per auction item. Maybe because of this amount is GROUPING and ORDERING not a good way to go. Please correct me if I am wrong.
After the auction is over i do clean up the bids table, removing all the bids, and store them in a separate table.
Can someone please help me tackle this problem!
And if you have been, thanks for reading..
EDIT
The tables i use are:
bids
======================
id (prim_key)
aid (auction id)
uid (user id)
cbid (current bid)
created (time created)
======================
auction_products
====================
id (prim_key)
pid (product id)
closetime (time the auction closses)
What i want as the result of the query:
result
===============================================
auction_products.id bids.uid bids.created
2 6 time1
2 8 time2
2 10 time3
5 3 time1
5 4 time2
5 9 time3
7 3 time1
7 2 time2
7 1 time3
So that is per auction the latest bids, to choose by number, 3 or 10
Using user variable, and control flow, i end up with that (just replace the <=3 with <=10 if you want the ten auctions) :
SELECT a.*
FROM
(SELECT aid, uid, created FROM bids ORDER BY aid, created DESC) a,
(SELECT #prev:=-1, #count:=1) b
WHERE
CASE WHEN #prev<>a.aid THEN
CASE WHEN #prev:=a.aid THEN
#count:=1
END
ELSE
#count:=#count+1
END <= 3
Why do this in one query?
$sql = "SELECT id FROM auctions ORDER BY created DESC LIMIT 10";
$auctions = array();
while($row = mysql_fetch_assoc(mysql_query($sql)))
$auctions[] = $row['id'];
$auctions = implode(', ', $auctions);
$sql = "SELECT id FROM bids WHERE auction_id IN ($auctions) ORDER BY created LIMIT 10";
// ...
You should obviously handle the case where, e.g. $auctions is empty, but I think this should work.
EDIT: This is wrong :-)
You will need to use a subquery:
SELECT bids1.id
FROM ( SELECT *
FROM bids AS bids1 LEFT JOIN
bids AS bids2 ON bids1.created < bids2.created
AND bids1.AuctionId = bids2.AuctionId
WHERE bid2.id IS NULL)
ORDER BY bids.created DESC
LIMIT 10
So the subquery performs a left join from bids to itself, pairing each record with all records that have the same auctionId and and a created date that is after its own created date. For the most recent record, there will be no other record with a greater created date, and so that record would not be included in the join, but since we use a Left join, it will be included, with all the bids2 fields being null, hence the WHERE bid2.id IS NULL statement.
So the sub query has one row per auction, contianing the data from the most recent bid. Then simply select off the top ten using orderby and limit.
If your database engine doesn't support subqueries, you can use a view just as well.
Ok, this one should work:
SELECT bids1.id
FROM bids AS bids1 LEFT JOIN
bids AS bids2 ON bids1.created < bids2.created
AND bids1.AuctionId = bids2.AuctionId
GROUP BY bids1.auctionId, bids1.created
HAVING COUNT(bids2.created) < 9
So, like before, left join bids with itself so we can compare each bid with all the others. Then, group it first by auction (we want the last ten bids per auction) and then by created. Because the left join pairs each bid with all previous bids, we can then count the number of bids2.created per group, which will give us the number of bids occurring before that bid. If this count is < 9 (because the first will have count == 0, it is zero indexed) it is one of the ten most recent bids, and we want to select it.
To select last 10 bids for a given auction, just create a normalized bids table (1 record per bid) and issue this query:
SELECT bids.id
FROM bids
WHERE auction = ?
ORDER BY
bids.created DESC
LIMIT 10
To select last 10 bids per multiple auctions, use this:
SELECT bo.*
FROM (
SELECT a.id,
COALESCE(
(
SELECT bi.created
FROM bids bi
WHERE bi.auction = a.id
ORDER BY
bi.auction DESC, bi.created DESC, bi.id DESC
LIMIT 1 OFFSET 9
), '01.01.1900'
) AS mcreated
COALESCE(
(
SELECT bi.id
FROM bids bi
WHERE bi.auction = a.id
ORDER BY
bi.auction DESC, bi.created DESC, bi.id DESC
LIMIT 1 OFFSET 9
), 0)
AS mid
FROM auctions a
) q
JOIN bids bo
ON bo.auction >= q.auction
AND bo.auction <= q.auction
AND (bo.created, bo.id) >= (q.mcreated, q.mid)
Create a composite index on bids (auction, created, id) for this to work fast.

Categories