Arithmetic with columns in different tables in mysql [optmization] - mysql

In the following query:
SELECT
(SELECT nick FROM nicks n WHERE n.pid=p.id LIMIT 1 ORDER BY id DESC) as nick
, (
(
( SELECT COUNT(*) FROM kills k WHERE k.pid = p.id )
+
( SELECT COUNT(*) FROM votos v WHERE v.pid = p.id )
)
- (SELECT COUNT(*) FROM deaths d WHERE d.pid = p.id )
) as score
, (SELECT COUNT(*) FROM kills k WHERE k.pid = p.id ) as kills
, (SELECT COUNT(*) FROM deaths d WHERE d.pid = p.id ) as deaths
, (SELECT COUNT(*) FROM headshots h WHERE h.pid = p.id ) as headshots
, (SELECT COUNT(*) FROM votos v WHERE v.pid = p.id ) as reputation
FROM players p
WHERE p.uuid='STEAM_x:x:xxxxxx'
GROUP BY kills
This query works fine... but i think there exists a better way to do this.
Can anyone help me optimize this query?

Here is a somewhat better way to write the query:
SELECT p.*, (kills + reputation - deaths) as score
FROM (SELECT (SELECT nick FROM nicks n WHERE n.pid = p.id ORDER BY id DESC LIMIT 1
) as nick,
(SELECT COUNT(*) FROM kills k WHERE k.pid = p.id ) as kills,
(SELECT COUNT(*) FROM deaths d WHERE d.pid = p.id ) as deaths,
(SELECT COUNT(*) FROM headshots h WHERE h.pid = p.id ) as headshots,
(SELECT COUNT(*) FROM votos v WHERE v.pid = p.id ) as reputation
FROM players p
WHERE p.uuid = 'STEAM_x:x:xxxxxx'
) p
GROUP BY kills;
Note: I don't understand what the GROUP BY is doing. You are only aggregating by one column, so the rest of the columns have indeterminate values. Perhaps you intend ORDER BY.
I am guessing that the overhead for materializing the subquery before the group by is slightly less than the additional subqueries. But your version may have very comparable performance.
For either version, you want the following indexes:
players(uuid)
kills(pid)
deaths(pid)
headshots(pid)
votos(pid)

Related

Sum of grouped COUNT IN MySQL

I wrote this query
SELECT
country,
COUNT(DISTINCT tmp_tbl.user_guid) AS number_of_customers
FROM complete_tests c INNER JOIN
( SELECT DISTINCT d.dog_guid,
u.user_guid,
u.country
FROM dogs d INNER JOIN users u ON d.user_guid = u.user_guid
WHERE (u.exclude = 0 OR u.exclude IS NULL)
AND (d.exclude = 0 OR d.exclude IS NULL)
)
AS tmp_tbl ON c.dog_guid = tmp_tbl.dog_guid
GROUP BY country
ORDER BY number_of_customers DESC
And I need to add another variable that calculates the percentage of total
when I add
number_of_customers/SUM(number_of_customers)
or SUM(COUNT(DISTINCT tmp_tbl.user_guid)) / COUNT(DISTINCT tmp_tbl.user_guid)
it gives me error
Analytic functions come in handy here. Assuming you are using MySQL 8+:
SELECT country,
COUNT(DISTINCT tmp_tbl.user_guid) AS number_of_customers,
100.0 * COUNT(DISTINCT tmp_tbl.user_guid) /
SUM(COUNT(DISTINCT tmp_tbl.user_guid)) OVER () AS pct_customers
FROM complete_tests c
INNER JOIN
(
SELECT DISTINCT d.dog_guid, u.user_guid, u.country
FROM dogs d
INNER JOIN users u ON d.user_guid = u.user_guid
WHERE (u.exclude = 0 OR u.exclude IS NULL) AND
(d.exclude = 0 OR d.exclude IS NULL)
) AS tmp_tbl
ON c.dog_guid = tmp_tbl.dog_guid
GROUP BY
country
ORDER BY
number_of_customers DESC;

How can I addition of 3 counts fields in MySQL one query?

I have 3 counts count(like), COUNT(comment), COUNT(views), what I want is to addition of this 3 in that query and get the total addition of this 3 counts.
Note:- I am using 3 joins for get this 3 count and group by for group by posts.
e.g. -
COUNT(like) = 5
COUNT(comment) = 3
COUNT(views) = 12
so i need a key total_count = 20
Is this possible?
SELECT up.id, COUNT(upl.id) as likes_count, COUNT(upc.id) as collected_count, COUNT(upvb.id) as viewed_by_count
FROM posts as up
LEFT JOIN post_likes as upl ON upl.post_id = up.id
LEFT JOIN post_viewd_by as upvb ON upvb.post_id = up.id
LEFT JOIN post_collected as upc ON upc.post_id = up.id WHERE up.status = 'Active' GROUP BY up.id
ORDER BY likes_count DESC, up.insertdate DESC
try this
SELECT up.id, COUNT(upl.id) as likes_count, COUNT(upc.id) as collected_count, COUNT(upvb.id) as viewed_by_count , COUNT(upl.id)+COUNT(upc.id)+COUNT(upvb.id) AS Total
FROM posts as up
LEFT JOIN post_likes as upl ON upl.post_id = up.id
LEFT JOIN post_viewd_by as upvb ON upvb.post_id = up.id
LEFT JOIN post_collected as upc ON upc.post_id = up.id
WHERE up.status = 'Active'
GROUP BY up.id
ORDER BY likes_count DESC, up.insertdate DESC
If you want correct counts, then one method is correlated subqueries:
SELECT up.id,
(likes_count + viewed_by_count + collected_count) as total
FROM (SELECT up.*,
(SELECT COUNT(*)
FROM post_likes upl
WHERE upl.post_id = up.id
) as likes_count,
(SELECT COUNT(*)
FROM post_viewd_by upvb
WHERE upvb.post_id = up.id
) as viewed_by_count,
(SELECT COUNT(*)
FROM post_collected pc
WHERE pc.post_id = up.id
) as collected_count
FROM posts up
WHERE up.status = 'Active'
) up
ORDER BY likes_count DESC, up.insertdate DESC;
Assuming you have an index on the post_id in all the subsidiary tables, this should also be the fastest method.

MYSQL order by + group by... fastest way?

I am trying to optimize a SQL query and I would like some expert opinion on the best/fastest way to combine GROUP BY and ORDER BY
Basically I am trying to select the lowest price from a products table and group them by merchant name.
This was my original query:
select p.*, m.*, d.* from datafeeds as d, products as p left outer join meta as m on p.mykey = m.mykey where p.datafeed_id = d.id and (match(p.name) against ('+asics +"gel" -women*' in boolean mode)) and p.datafeed_id = '35' and p.is_custom = 0 group by d.merchant_name order by d.merchant_name limit 50
And the ORDER BY was not working, I was getting grouped products but not the ones with the lowest prices.
After reading other discussions i came up with an improved query:
SELECT p . * , m . * , d . *
FROM datafeeds AS d, products AS p
INNER JOIN (
SELECT MIN( display_price ) AS MinPrice
FROM products AS p
WHERE 1 =1
AND (
MATCH (
p.name
)
AGAINST (
'+asics +"gel" -women*'
IN BOOLEAN
MODE
)
)
AND p.datafeed_id = '35'
AND p.is_custom =0
GROUP BY merchant_name
) AS p2 ON p.display_price = p2.MinPrice
LEFT OUTER JOIN meta AS m ON p.mykey = m.mykey
WHERE p.datafeed_id = d.id
AND (
MATCH (
p.name
)
AGAINST (
'+asics +"gel" -women*'
IN BOOLEAN
MODE
)
)
AND p.datafeed_id = '35'
AND p.is_custom =0
GROUP BY d.merchant_name
ORDER BY d.merchant_name
LIMIT 50`
The query gets the correct results but it is quite slow.
Is there a better way to do it?
Thanks in advance
You could try this
SELECT p.*,
m.*,
d.*
FROM datafeeds AS d,
(
SELECT *
FROM products
WHERE 1 = 1
AND (
MATCH ( name ) against ( '+asics +"gel" -women*' IN boolean mode ) )
AND datafeed_id = '35'
AND is_custom =0
ORDER BY merchant_name,
display_price) AS p
LEFT OUTER JOIN meta AS m
ON p.mykey = m.mykey
WHERE p.datafeed_id = d.id
GROUP BY d.merchant_name
ORDER BY d.merchant_name
LIMIT 50

how to sort and group by by it's count

I have the following:
SELECT DISTINCT s.username, COUNT( v.id ) AS cnt
FROM `instagram_item_viewer` v
INNER JOIN `instagram_shop_picture` p ON v.item_id = p.id
INNER JOIN `instagram_shop` s ON p.shop_id = s.id
AND s.expirydate IS NULL
AND s.isLocked =0
AND v.created >= '2014-08-01'
GROUP BY (
s.id
)
ORDER BY cnt DESC
Basically I have an instagram_item_viewer with the following structure:
id viewer_id item_id created
It tracks when a user has viewed an item and what time. So basically I wanted to find shops that has the most items viewed. I tried the query above and it executed fine, however it doesn't seem to give the appropriate data, it should have more count than what it is. What am I doing wrong?
First, with a group by statement, you don't need the DISTINCT clause. The grouping takes care of making your records distinct.
You may want to reconsider the order of your tables. Since you are interested in the shops, start there.
Select s.username, count(v.id)
From instagram_shop s
INNER JOIN instagram_shop_picture p ON p.shop_id = s.shop_id
INNER JOIN instagram_item_viewer v ON v.item_id = p.id
AND v.created >= '2014-08-01'
WHERE s.expirydate IS NULL
AND s.isLocked = 0
GROUP BY s.username
Give thata shot.
As mentioned by #Lennart, if you have a sample data it would be helpful. Because otherwise there will be assumptions.
Try run this to debug (this is not the answer yet)
SELECT s.username, p.id, COUNT( v.id ) AS cnt
FROM `instagram_item_viewer` v
INNER JOIN `instagram_shop_picture` p ON v.item_id = p.id
INNER JOIN `instagram_shop` s ON p.shop_id = s.id
AND s.expirydate IS NULL
AND s.isLocked =0
AND v.created >= '2014-08-01'
GROUP BY (
s.username, p.id
)
ORDER BY cnt DESC
The problem here is the store and item viewer is too far apart (i.e. bridged via shop_picture). Thus shop_picture needs to be in the SELECT statement.
Your original query only gets the first shop_picture count for that store that is why it is less than expected
Ultimately if you still want to achieve your goal, you can expand my SQL above to
SELECT x.username, SUM(x.cnt) -- or COUNT(x.cnt) depending on what you want
FROM
(
SELECT s.username, p.id, COUNT( v.id ) AS cnt
FROM `instagram_item_viewer` v
INNER JOIN `instagram_shop_picture` p ON v.item_id = p.id
INNER JOIN `instagram_shop` s ON p.shop_id = s.id
AND s.expirydate IS NULL
AND s.isLocked =0
AND v.created >= '2014-08-01'
GROUP BY (
s.username, p.id
)
ORDER BY cnt DESC
) x
GROUP BY x.username

SQL Query with Joins Counting multiple Results Per Record Ordering By Count

I have a table called Request.
Other tables are linked to the Request table through a request id.
There is a TwitterTweet table and a FacebookPost table.
So a single request can have 50 TwitterTweets and/or 20 FacebookPosts or any amount of Tweets/Posts
We can add them together for a total count of 70.
I'm trying to create a query that could tell me what is the request with the highest total count.
I know this is wrong:
(I attempted to just order them by the counts within the TwitterTweet, but it would not let me do an OUTER JOIN which I thought
would bring back the Count.count column. It forced me to do a Left Join for it to compile. My Logic was to do a join so
that the results were calculated for each row by the requestid)
SELECT r1.`id` AS requestid, r1 . *
FROM `Request` AS r1
LEFT JOIN
(SELECT COUNT( * ) AS count, rid
FROM
((SELECT `TwitterTweet`.`id` AS `smid` , `TwitterTweet`.`requestid` AS rid
FROM `TwitterTweet`
WHERE `TwitterTweet`.`requestid` = requestid
AND `TwitterTweet`.`active` =1) AS talias
)) AS Count ON ( Count.rid = requestid )
ORDER BY Count.count
*When I tried to add in the Facebook side it would not compile any more
(The concept is that the results are added from TwitterTweet with the results from FacebookPost
that are attached to the specific requestid which would give us a count. The entire result
set should be ordered by that count)
SELECT r1.`id` AS requestid, r1 . *
FROM `Request` AS r1
LEFT JOIN
(SELECT COUNT( * ) AS count, rid
FROM
((SELECT `TwitterTweet`.`id` AS `smid` , `TwitterTweet`.`requestid` AS rid
FROM `TwitterTweet`
WHERE `TwitterTweet`.`requestid` = requestid
AND `TwitterTweet`.`active` =1 ) AS talias
UNION All
(SELECT `FacebookPost`.`id` AS `smid`, `FacebookPost`.`requestid` AS rid
FROM `FacebookPost`
WHERE `FacebookPost`.`requestid` = requestid
AND `FacebookPost`.`active` = 1) as falias
)) AS Count ON ( Count.rid = requestid )
ORDER BY Count.count
I updated the Query with an attempt to add an alias:
SELECT rid, SUM(count) total_count
FROM
(
(SELECT COUNT(*) AS count, r.rid
FROM request r
JOIN TwitterTweet tt
ON r.id = tt.requestid
WHERE tt.active = 1
GROUP BY r.rid) AS twitter
UNION ALL
(SELECT COUNT(*) AS count, r.rid
FROM request r
JOIN FacebookPost fp
ON r.id = fp.requestid
WHERE fp.active = 1
GROUP BY r.rid ) AS fbook
)
GROUP BY rid
ORDER BY SUM(count) DESC
I made another adjustment to give the middle subquery an alias, but now I only get one row returned with a zero in the rid column and 5686 in the total_count column...the 5686 might be all of the results.
SELECT counts.rid, SUM(count) total_count
FROM
(
SELECT COUNT(*) AS count, r.requestid AS rid
FROM request r
JOIN TwitterTweet tt
ON r.id = tt.requestid
WHERE tt.active = 1
GROUP BY r.requestid
UNION ALL
SELECT COUNT(*) AS count, r.requestid AS rid
FROM request r
JOIN FacebookPost fp
ON r.id = fp.requestid
WHERE fp.active = 1
GROUP BY r.requestid
) AS counts
GROUP BY counts.rid
ORDER BY SUM(count) DESC
Got it!!!
Thanks for your help guys, I had to remove those joins on the request:
SELECT counts.rid, SUM(count) total_count
FROM
(
SELECT COUNT(*) AS count, tt.requestid AS rid
FROM TwitterTweet tt
WHERE tt.active = 1
GROUP BY tt.requestid
UNION ALL
SELECT COUNT(*) AS count, fp.requestid AS rid
FROM FacebookPost fp
WHERE fp.active = 1
GROUP BY fp.requestid
) AS counts
GROUP BY counts.rid
ORDER BY SUM(count) DESC
SELECT id, SUM(count) total_count
FROM
(
SELECT COUNT(*) AS count, r.id
FROM request r
JOIN TwitterTweet tt
ON r.id = tt.requestid
WHERE tt.active = 1
GROUP BY r.id
UNION ALL
SELECT COUNT(*) AS count, r.id
FROM request r
JOIN FacebookPost fp
ON r.id = fp.requestid
WHERE fp.active = 1
GROUP BY r.id
) sub
GROUP BY id
ORDER BY SUM(count) DESC
;