SQL getting values and then total count of those values - mysql

I am working on an auction script with a table that looks like the attached image.
Within an auction two users can place the same bid amount, for example two players could place a bid of '1' which would give that bid amount a total count of two.
I need a query which gets all of a users single bid amount along with the total count of that bid amount within the scope of the auction id.
As it stands I first get the users bids:
SELECT bid_amount FROM all_bids WHERE auction_id = '129' AND user_id = '9'
And then I am looping through each amount in PHP and doing the following
SELECT COUNT(*) FROM all_bids WHERE auction_id = '129' AND bid_amount = 'xxx'
But this is of course very power hungry and I am sure it can be done all in one query.
I have got as far as
SELECT bid_amount,COUNT(*) FROM (SELECT bid_amount FROM all_bids WHERE auction_id = '129' AND user_id ='9') as foo GROUP BY bid_amount
Which returned the correct bid amounts but the counts were all 1 which is wrong as I guess my incorrect query is only counting the values within the subquery and not outside of the bids that just that user placed

SELECT a.*, b.cnt from all_bids a
join (select bid_amount,COUNT(*) cnt from all_bids group by bid_amount) b
on a.bid_amount=b.bid_amount
WHERE a.auction_id = '123' AND a.player_id = '456'

I think you want group by:
select bid_amount, count(*)
from all_bids
where auction_id = 123 and player_id = 456
group by bid_amount;
Note that you do not need single quotes for a numeric constant.
Hmmm. You might want this phrased as:
select ab.bid_amount, count(*)
from all_bids ab
where ab.auction_id = 123 and
ab.bid_amount in (select ab2.bid_amount
from all_bids ab2
where ab2.auction_id = ab.auction_id and ab2.player_id = 456
)
group by ab.bid_amount;

Related

Avoid using a subquery in a table join

In a MySQL 5.7 database, I have the following User table:
Name
Id
David
1
Frank
2
And the following Order table:
Id
Price
UserId
1
55
1
2
68
1
3
50
1
4
10
2
For every user, I want to select the price of the order with the biggest ID.
I can use the following query which adds additional complexity due to the nested subquery :
SELECT
User.Name,
last_user_order.Price
FROM User
LEFT JOIN (
SELECT Price, UserId FROM Order
ORDER BY Id DESC LIMIT 1
) AS last_user_order ON last_user_order.UserId = User.Id
There exist many questions here where the column to be selected is the same than the one being ordered. Hence, it is possible to use MAX in the first SELECT statement to avoid a subquery. Is it possible to avoid a subquery in my case?
For every user, I want to select the price of the order with the biggest ID.
That looks like:
SELECT
u.*,
o.Price,
FROM
User u
INNER JOIN Order o ON u.ID = o.UserID
INNER JOIN
(
SELECT MAX(ID) as OrderID FROM Order GROUP BY UserId
) maxO ON o.Id = maxO.OrderId
SELECT User.Name,
( SELECT Order.Price
FROM Order
WHERE Order.UserId = User.Id
ORDER BY Order.Id DESC LIMIT 1 ) LastPrice
FROM User;

SQL: counting the number of rows and returning all rows with max count: invalid use of group function

I have these tables:
tutors:
tutorid firstname, lastname...
courses:
url tutorid
reviews:
review courseid
I need to select all tutors that have the most reviews. 1 tutor = 1 course.
I first tried to just select courses with the most reviews:
select y.courseid, num from (select courseid,COUNT(reviews.rating) as num
from reviews group by (reviews.courseid)) y;
This selects all urls and the number of reviews for each.
this
select y.courseid, MAX(num) from (select courseid,COUNT(reviews.rating) as num
from reviews group by (reviews.courseid)) y;
would display the single course with most reviews - even if there are other courses with the same (maximum) number of reviews - they won't get displayed.
I'm trying to combat that. I tried
select y.courseid, num from (select courseid,COUNT(reviews.rating) as num
from reviews group by (reviews.courseid)) y
where num = MAX(num);
but get invalid use of group function error.
EDIT: the courseid - is the course's url. As in the course's url is the foreign key in the reviews table.
I would try this one:
select distinct t.tutorid t.firstname, t.lastname
from (select courseid, count(reviews.rating) total
from reviews
group by courseid) r
left join courses c
on r.courseid = c.courseid
left join tutors t
on c.tutorid = t.tutorid
where r.total=(select max(total)
from (select courseid,
count(reviews.rating) total
from reviews
group by courseid) r
)
You can create a column that rank the review in desc order and select those tutor with rank = 1
It would look like this:
Select * from(Select *, rank() over(order by num desc) as rank from table) where rank = 1
You can also use dense_rank base on your need.

How can I update the value of one field to the most often used value of another field?

I have a table similar to the following:
ID PAYEE CATEGORY
001 Costco Grocery
002 See's Candy
003 Costco Repair
005 Costco Grocery
006 Costco
007 Costco
008 See's
Using MySQL withOUT the aid of a programming language, is there a query (nested or not) that would set the category of the three new rows to the most often used category for those payees?
For example, one of the Costco records (ID 003) has Repair as its category, whereas the other two Costco rows (ID 001 and ID 005) have Grocery as their category. Thus the desired result would be that the new Costco rows (ID 006 and ID 007) would be set to Grocery since that's the most often used category for that payee.
sure.. just change 'your_table' to the name of your table
UPDATE your_table
LEFT JOIN (SELECT payee, category
FROM
(SELECT payee, category FROM your_table WHERE category != '' AND category IS NOT NULL GROUP BY payee, category ORDER BY count(*) DESC) AS tbl2
GROUP BY payee
) AS tbl2 USING (payee)
SET your_table.category = tbl2.category;
this will change the costco that is categorized as repair to 'grocery' as well.. if you dont want this, add:
WHERE your_table.category IS NULL OR your_table.category = ''
to the very end of the query
This would work
UPDATE test t,
(SELECT category,
payee,
count(*)
FROM test ORDER BY count(*) desc LIMIT 1) t1
SET t.category = t1.category
WHERE t.payee = t1.payee
AND (t.category = ''
OR t.category IS NULL)
Sqlfiddle at http://www.sqlfiddle.com/#!2/ed5b0/1/0
I just couldn't figure out a way without repeating the creation of a derived table:
UPDATE t JOIN (
SELECT s1.payee, s1.category FROM (
SELECT payee, category, count(*) cat_count FROM t
WHERE category IS NOT NULL
GROUP BY payee, category
) s1
LEFT JOIN (
SELECT payee, category, count(*) cat_count FROM t
WHERE category IS NOT NULL
GROUP BY payee, category
) s2
ON s1.payee = s2.payee AND s1.cat_count < s2.cat_count
WHERE s2.cat_count IS NULL
) s
ON t.payee = s.payee
SET t.category = s.category
WHERE t.category IS NULL;
Fiddle here
Use mysql's multi-table update:
UPDATE mytable t
JOIN (SELECT payee, category
FROM (SELECT payee, category
FROM mytable
GROUP BY 1, 2
ORDER BY count(*) desc) x
GROUP BY 1) y
ON y.payee = t.payee
SET t.category = y.category
WHERE ifnull(t.category, '') = ''
There's a little bit of kung fu tucked away in there that makes it work: the outer group by returns the first row encountered for the group, which due to the ordering for the inner-most query will be the category with the highest count.

Group by grouped column?

I have a table of node likes, which looks roughly like this:
lid nid uid type
1 23 3 like
2 23 1 like
3 49 3 dislike
4 11 6 like
lid = unique ID for this table, nid = "node" (content) ID, uid = user ID and type is self explanatory.
With this query:
SELECT nid, COUNT(lid) AS score, type
FROM node_likes
INNER JOIN users ON node_likes.uid = users.uid
GROUP BY nid, type
I can get each node with its like and dislike scores. The inner join is irrelevant; some (dis)likes are from users that no longer exist, and the join is to eliminate them.
The result looks like this:
nid score type
307 4 like
307 1 dislike
404 24 like
How can I then sub-group this query by type, and return the top-scoring node ID for each "like" type (like/dislike)?
Ie.
nid score type
404 24 like
307 1 dislike
SELECT
SUBSTRING_INDEX(GROUP_CONCAT(nid ORDER BY likes DESC),',',1) as most_likes_nid,
MAX(likes) as most_likes,
SUBSTRING_INDEX(GROUP_CONCAT(nid ORDER BY dislikes DESC),',',1) as most_dislikes_nid,
MAX(dislikes) as most_dislikes
FROM (
SELECT
nid,
COUNT(IF(type = 'like', 1, null)) as likes,
COUNT(IF(type = 'dislike', 1 ,null)) as dislikes
FROM node_likes
GROUP BY nid
) as t
SELECT nid, COUNT(lid) AS score, type
FROM node_likes
INNER JOIN users ON node_likes.uid = users.uid
GROUP BY nid, type
ORDER BY type DESC, score DESC;
may do the trick.
Try this:
SELECT
nid, max(score) as score, type
FROM (
SELECT nid, COUNT(lid) AS score, type
FROM node_likes
INNER JOIN users ON node_likes.uid = users.uid
GROUP BY nid, type
) results
GROUP BY type
ORDER BY type DESC, score DESC

Organizing data output with MySQL

I have the following table with data:
t1 (results): card_id group_id project_id user_id
The tables that contain actual labels are:
t2 (groups): id project_id label
t3 (cards): id project_id label
There could be multiple entries by different users.
I need help with writing a query to display the results in a table format with totals counts corresponding card/group. Here's my start but I'm not sure that I'm on the right track...
SELECT COUNT(card_id) AS cTotal, COUNT(group_id) AS gTotal
WHERE project_id = $projID
Unless I'm mistaken, it seems that all you need to do is group by card_id and group_id for the given project_id and pull out the count for each group
SELECT card_id, group_id, COUNT(user_id) FROM mytable
WHERE project_id = 001
GROUP BY (card_id, group_id);
EDIT:
Taking into account the card and group tables involves some joins, but the query is fundamentally the same. Still grouping by card and group, and constraining by project id
SELECT c.label, g.label, COUNT(t1.user_id) FROM mytable t1
JOIN groups g ON t1.group_id=g.id
JOIN cards c ON t1.card_is=c.id
WHERE t1.project_id = 001
GROUP BY (c.card_id, g.group_id)
ORDER BY (c.card_id, g.group_id);
I don't think you can get a table as you want with just SQL. You'll have to render the table in code by iterating over the results. How you do that depends on what language/platform you are using.
if you know for a fixed fact that there are nine groups, then just include those groups in subqueries - similar to this:
select cTotal, g1.gTotal as Group1, g2.gTotal as Group2... etc
from
( SELECT COUNT(card_id) AS cTotal
, COUNT(group_id) AS gTotal
WHERE project_id = $projID
AND group_id = 1 ) g1
, ( SELECT COUNT(card_id) AS cTotal
, COUNT(group_id) AS gTotal
WHERE project_id = $projID
AND group_id = 2 ) g2
etc.