dependent subquery to self join - mysql

How do I change the following dependent subquery to self join?
SELECT d.name, d.created,
(SELECT SUM( q1.payout ) FROM client AS q1 WHERE q1.uid = d.uid) AS payout,
(SELECT COUNT( q2.uid ) FROM client AS q2 WHERE q2.uid = d.uid AND q2.winning =1) AS cnt
FROM client AS d group by d.name, d.created ORDER BY cnt DESC LIMIT 0 , 10;

SELECT d.name, d.created, SUM(q1.payout) AS psum, COUNT(q2.uid) AS cnt
FROM client d
LEFT JOIN
client q1
ON q1.uid = d.uid
LEFT JOIN
client q2
ON q2.uid = d.uid
AND q2.winning =1
GROUP BY
d.name, d.created
ORDER BY
cnt DESC
LIMIT 0, 10
If uid is a PRIMARY KEY, you can rewrite it like this:
SELECT d.name, d.created, SUM(payout) AS psum, COUNT(IF(winning = 1, uid, NULL)) AS cnt
FROM client d
GROUP BY
d.name, d.created
ORDER BY
cnt DESC
LIMIT 0, 10

SELECT d.name, d.created, SUM(d.payout) AS allpayout, COUNT(alt.uid) as cnt
FROM client AS d
LEFT JOIN client AS alt
ON alt.uid = d.uid AND alt.winning = 1
GROUP BY d.name, d.created
ORDER BY cnt DESC
LIMIT 0, 10

SELECT d.name, d.created, SUM( d.payout ) AS payout, SUM( IF( d.winning = 1, 1, 0 ) ) AS cnt
FROM client AS d
GROUP BY
d.name, d.created
ORDER BY cnt DESC LIMIT 0 , 10;

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;

Grouping Data In Sql

I'm moving a project from MySQL to MSSQL. And since I have a non-standard use of grouping on the Mssql side, I can't bring up the correct data. I need a suggestion. The stok_adet field displays the wrong data. How can we overcome this? Thank you in advance for your help.
My current screenshots are as follows.
Mssql Query
SELECT sm.*,
(SELECT COUNT(*)
FROM stok_durum d
WHERE sm.stok_durum_id = sm.stok_durum_id
) as stok_adet
FROM (SELECT s.*, m.*,
ROW_NUMBER() OVER (PARTITION BY s.bundle_no, s.boy, s.yukseklik, s.hatali
ORDER BY (SELECT NULL)
) as seqnum
FROM stok s
CROSS JOIN mermer_cins m
WHERE m.mermer_cins_id = '5' AND s.blok_no = 'M6320'
) sm
WHERE seqnum = 1
ORDER BY sm.blok_no ASC, sm.bundle_no ASC, sm.stok_tarih DESC
MySql side
SELECT Count(*) AS stok_adet, s.*, m.*,d.*
FROM stok AS s
CROSS JOIN mermer_cins AS m
JOIN stok_durum AS d ON s.stok_durum_id = d.stok_durum_id
WHERE m.mermer_cins_id = '5' AND s.blok_no = 'M6320'
GROUP BY s.bundle_no, s.boy, s.yukseklik, s.hatali
ORDER BY s.blok_no ASC, s.bundle_no ASC, s.stok_tarih DESC
MsSql side
MySql side
When calculating stok_adet you have a comparison between the same column:
WHERE sm.stok_durum_id = sm.stok_durum_id
but you maybee want:
WHERE d.stok_durum_id = sm.stok_durum_id
Try:
SELECT sm.*,
(SELECT COUNT(*)
FROM stok_durum d
WHERE d.stok_durum_id = sm.stok_durum_id
) as stok_adet
FROM (SELECT s.*, m.*,
ROW_NUMBER() OVER (PARTITION BY s.bundle_no, s.boy, s.yukseklik, s.hatali
ORDER BY (SELECT NULL)
) as seqnum
FROM stok s
CROSS JOIN mermer_cins m
WHERE m.mermer_cins_id = '5' AND s.blok_no = 'M6320'
) sm
WHERE seqnum = 1
ORDER BY sm.blok_no ASC, sm.bundle_no ASC, sm.stok_tarih DESC

Mysql - dynamic UNION with multiple recieve ids

how to convert this multiple UNION to simple query?
number of artist_id is dynamic.
45,122,95
or
100,20
or
89,9449
or
22,495,700,98
....
(SELECT b.`id`, b.`id`, b.`date`, b.`artist`, b.`title`, b.`photo`
FROM `tags_mp3s` a
INNER JOIN `mp3s` b ON b.`id` = a.`mp3_id` AND a.`artist_id` = 45
ORDER BY b.`date` DESC
LIMIT 5)
UNION ALL
(SELECT b.`id`, b.`id`, b.`date`, b.`artist`, b.`title`, b.`photo`
FROM `tags_mp3s` a
INNER JOIN `mp3s` b ON b.`id` = a.`mp3_id` AND a.`artist_id` = 122
ORDER BY b.`date` DESC
LIMIT 5)
UNION ALL
(SELECT b.`id`, b.`id`, b.`date`, b.`artist`, b.`title`, b.`photo`
FROM `tags_mp3s` a
INNER JOIN `mp3s` b ON b.`id` = a.`mp3_id` AND a.`artist_id` = 95
ORDER BY b.`date` DESC
LIMIT 5)
.....
thanks
In MySQL, you can use variables:
SELECT ta.*
FROM (SELECT m.*,
(#rn := if(#a = t.artist_id, #rn + 1,
if(#a := t.artist_id, 1, 1)
)
) as rn
FROM tags_mp3s t INNER JOIN
mp3s m
ON m.id = t.mp3_id CROSS JOIN
(SELECT #rn := 0, #a := -1) params
WHERE t.artist_id IN (. . . )
ORDER BY t.artist_id, m.date DESC
) ta
WHERE rn <= 5;

MySQL: To exclude some results based on specified conditions

I am stuck at some problem. The problem has two tables, namely hackers and challenges.
Here is the schema:
Hackers(hacker_id: int, name string)
Challenges(challenge_id: int, hacker_id int)
I am trying to write a query to print the hacker_id, name, and the total number of challenges created by each student. Sort your results by the total number of challenges in descending order. If more than one student created the same number of challenges, then sort the result by hacker_id. If more than one student created the same number of challenges and the count is less than the maximum number of challenges created, then exclude those students from the result.
Here is my query:
select hackers.hacker_id ,
hackers.name ,
count(challenges.challenge_id) as challenges_created
from
hackers left join challenges
on
hackers.hacker_id = challenges.hacker_id
having
count(challenges_created) >= max(challenges_created)
order by challenges_created desc, hackers.hacker_id asc
I am getting wrong output!
Please tell me where I am getting wrong!
Sample Input :
Hackers Table:image
Challengers Table :image
Sample Output:
21283 Angela 6
88255 Patrick 5
96196 Lisa 1
It's a crazy requirement to solve in mysql only. With CTE (not supported by mysql) you could reuse your query:
with tmp as (
select h.hacker_id, h.name, count(1) as challenges_created
from hackers h
left join challenges c on c.hacker_id = h.hacker_id
group by h.hacker_id, h.name
order by challenges_created desc, h.hacker_id asc
), max_challenges_created as (
select max(challenges_created) as challenges_created
from tmp
), count_per_challenges_created as (
select challenges_created, count(1) as c
from tmp
group by challenges_created
)
select *
from tmp
cross join max_challenges_created m
left join count_per_challenges_created c on c.challenges_created = tmp.challenges_created
where tmp.challenges_created = m.challenges_created
or c.c = 1
http://rextester.com/XGYQ11641
In MySQL you can't even use a temporary table, because you couldn't use it multiple times in one query. So you have to copy&paste the same query three times (and hope that mysql will execute it only once using cache):
select *
from (
select h.*, count(1) as challenges_created
from hackers h
left join challenges c on c.hacker_id = h.hacker_id
group by h.hacker_id
order by challenges_created desc, h.hacker_id
) tmp
cross join (
select max(challenges_created) as challenges_created
from (
select h.*, count(1) as challenges_created
from hackers h
left join challenges c on c.hacker_id = h.hacker_id
group by h.hacker_id
order by challenges_created desc, h.hacker_id asc
) tmp
) m
left join (
select challenges_created, count(1) as c
from (
select h.*, count(1) as challenges_created
from hackers h
left join challenges c on c.hacker_id = h.hacker_id
group by h.hacker_id
order by challenges_created desc, h.hacker_id asc
) tmp
group by challenges_created
) c on c.challenges_created = tmp.challenges_created
where tmp.challenges_created = m.challenges_created
or c.c = 1
http://rextester.com/IDUMRB49795
Try start solving smallest part of the query and keep joining them.
You can try my code which is in SQL(Oracle)
SELECT h.hacker_id,
h.name,
COUNT(c.challenge_id) AS challenge_count
FROM Hackers h
JOIN Challenges c ON c.hacker_id = h.hacker_id
GROUP BY h.hacker_id, h.name
HAVING challenge_count =
(SELECT COUNT(challenge_id) AS count_max
FROM Challenges
GROUP BY hacker_id
ORDER BY count_max DESC limit 1)
OR challenge_count IN
(SELECT DISTINCT c_compare AS c_unique
FROM (SELECT h2.hacker_id,
h2.name,
COUNT(challenge_id) AS c_compare
FROM Hackers h2
JOIN Challenges c ON c.hacker_id = h2.hacker_id
GROUP BY h2.hacker_id, h2.name) counts
GROUP BY c_compare
HAVING COUNT(c_compare) = 1)
ORDER BY challenge_count DESC, h.hacker_id;
try this it's very simple....
select
h.hacker_id,
h.name
from hackers h, difficulty d, challenges c, submissions s
where
h.hacker_id=s.hacker_id
and s.challenge_id=c.challenge_id
and d.difficulty_level=c.difficulty_level
and d.score=s.score
group by h.hacker_id, h.name
having count(*)>1
ORDER BY COUNT(*) DESC, h.hacker_id;
This is not the real answer, but the start point for you:
http://sqlfiddle.com/#!9/6910c/5
SELECT h.hacker_id ,
h.name ,
COUNT(challenges.challenge_id) as challenges_created
FROM hackers h
LEFT JOIN challenges
ON h.hacker_id = challenges.hacker_id
GROUP BY h.hacker_id
#HAVING challenges_created >= max(challenges_created)
ORDER BY challenges_created desc, h.hacker_id asc
I've commented out your HAVING clause, because can not understand what you are trying to achieve.
Explain please why do you expect only 3 rows in result? Why Rose and Frank should be excluded from the result set?
The solution is easy when you use multiple joins on the necessary tables. I like to group results as derived tabled for clarity but it can be done without those derived tables too.
select hckid, nme
from
(select h.name as nme, h.hacker_id as hckid, cnt
from (select s.hacker_id as hackerid, count(d.score) as cnt
from submissions s
join challenges c
on c.challenge_id = s.challenge_id
join difficulty d
on c.difficulty_level = d.difficulty_level
where d.score = s.score
group by s.hacker_id
having count(d.score) > 1) ft
join hackers h
on ft.hackerid = h.hacker_id) t
order by t.cnt desc, t.hckid asc
select hacker_id, name, tmp.challenges_created
from (
select h.*, count(1) as challenges_created
from hackers h
left join challenges c on c.hacker_id = h.hacker_id
group by h.hacker_id, h.name
order by challenges_created desc, h.hacker_id
) tmp
cross join (
select max(challenges_created) as challenges_created
from (
select h.*, count(1) as challenges_created
from hackers h
left join challenges c on c.hacker_id = h.hacker_id
group by h.hacker_id, h.name
order by challenges_created desc, h.hacker_id asc
) tmp
) m
left join (
select challenges_created, count(1) as c
from (
select h.*, count(1) as challenges_created
from hackers h
left join challenges c on c.hacker_id = h.hacker_id
group by h.hacker_id, h.name
order by challenges_created desc, h.hacker_id asc
) tmp
group by challenges_created
) c on c.challenges_created = tmp.challenges_created
where tmp.challenges_created = m.challenges_created
or c.c = 1
SELECT a.hacker_id, a.name, b.id
FROM Hackers a
INNER JOIN (SELECT hacker_id, id
FROM (SELECT hacker_id, count(challenge_id) AS id
FROM Challenges
GROUP BY hacker_id) x
WHERE id = 50
UNION ALL
SELECT hacker_id,id
FROM (SELECT hacker_id, count(challenge_id) AS id
FROM Challenges
GROUP BY hacker_id) y
WHERE id IN (SELECT id
FROM (SELECT hacker_id, count(challenge_id) AS id
FROM Challenges
GROUP BY hacker_id) x
GROUP BY id
HAVING count(*) = 1)
) b ON a.hacker_id = b.hacker_id
ORDER BY id DESC, hacker_id
This is a question in hackerranck.com you can check it here [link]
in this site you can pass answer by many sql DBMS (oracale,maysql,sqlserver, and DB2)
It's Work very well for SQL-SERVER
with t
as(
Select s.hacker_id ,
s.score,d.Difficulty_level as Difficulty_level,
c.Challenge_id
From submissions s
inner join Challenges c on c.Challenge_id = s.Challenge_id
inner join Difficulty d on d.Difficulty_level = c.Difficulty_level and d.score = s.score
)
Select t.hacker_id, h.name
From t inner join hackers h ON t.hacker_id=h.hacker_id
Where t.score in (Select score from Difficulty)
Group by t.hacker_id,h.name
Having count(t.Challenge_id) > 1
Order by count(t.Challenge_id) desc,t.hacker_id asc
I have done in SQL Server
I have joined 2 different selects , the first one uses rank to determine the ones with rank 1 and the second finds duplicates , so in the join result I filtered the result to have either 1st rank or no duplicates (cnt = 1)
select s.hacker_id, s.name, s.challenge_created from (
select * ,rank() over (order by challenge_created desc) rn from (
select h.hacker_id, name, count(challenge_id) challenge_created from hackers h
inner join challenges c on h.hacker_id = c.hacker_id
group by h.hacker_id, name) as T) as S
inner join (select A.challenge_created , count(1) cnt from (select h.hacker_id, name, count(challenge_id) challenge_created from hackers h
inner join challenges c on h.hacker_id = c.hacker_id
group by h.hacker_id, name) as A group by A.challenge_created) as O
on s.challenge_created = O.challenge_created
where rn = 1 or O.cnt = 1
order by s.challenge_created desc , hacker_id
The below query works fine in mysql
For having clause condition
I have included the record in which the count is either equal to
maximum number of challenges created OR
the number of challenges created is unique
select c.hacker_id,h.name,count(c.challenge_id) as cnt from
challenges c join hackers h
on c.hacker_id = h.hacker_id
group by c.hacker_id,h.name
having cnt =
(select max(counts) from (select count(challenge_id) as counts from challenges group by hacker_id) as countstable1) or
cnt in (select counts from (select count(challenge_id) as counts from challenges group by hacker_id) as countstable2 group by counts having count(*)=1)
order by cnt desc,c.hacker_id

Arithmetic with columns in different tables in mysql [optmization]

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)