Trying to use ID in MySQL SubSubQuery - mysql

So I'll show you what I'm trying to do and explain my problem, there may be an answer different to the approach I'm trying to take.
The query I'm trying to perform is as follows:
SELECT *
FROM report_keywords rk
WHERE rk.report_id = 231
AND (
SELECT SUM(t.conv) FROM (
SELECT conv FROM report_keywords t2 WHERE t2.campaign_id = rk.campaign_id ORDER BY conv DESC LIMIT 10
) t
) >= 30
GROUP BY rk.campaign_id
The error I get is
Unknown column 'rk.campaign_id' in 'where clause'
Obviously this is saying that the table alias rk is not making it to the subsubquery. What I'm trying to do is get all of the campaigns where the sum of the top 10 conversions is greater than or equal to 30.
The relevant table structure is:
id INT,
report_id INT,
campaign_id INT,
conv INT
Any help would be greatly appreciated.
Update
Thanks to Kickstart I was able to do what I wanted. Here's my final query:
SELECT campaign_id, SUM(conv) as sum_conv
FROM (
SELECT campaign_id, conv, #Sequence := if(campaign_id = #campaign_id, #Sequence + 1, 1) AS aSequence, #campaign_id := campaign_id
FROM report_keywords
CROSS JOIN (SELECT #Sequence := 0, #campaign_id := 0) Sub1
WHERE report_id = 231
ORDER BY campaign_id, conv DESC
) t
WHERE aSequence <= 10
GROUP BY campaign_id
HAVING sum_conv >= 30

Possibly use a user variable to add a sequence number to get the latest 10 records for each one, then use SUM to get the count of those.
Something like this:-
SELECT rk.*
FROM report_keywords rk
INNER JOIN
(
SELECT campaign_id, SUM(conv) AS SumConv
FROM
(
SELECT campaign_id, conv, #Sequence := if(campaign_id = #campaign_id, #Sequence + 1, 1) AS aSequence, #campaign_id := campaign_id
FROM report_keywords
CROSS JOIN (SELECT #Sequence := 0, #campaign_id := "") Sub1
ORDER BY campaign_id, conv
) Sub2
WHERE aSequence <= 10
GROUP BY campaign_id
) Sub3
ON rk.campaign_id = Sub3.campaign_id AND Sub3.SumConv >= 30
WHERE rk.report_id = 231

Related

user defined variable to store ranking gives wrong values if order by

mysql table: work
|id|user_id|created_at|realization|
I have been working on a sql query which calculates performance (realisation today / realization on the first day of the month) and sortes records based on performance.
Expected result:
|ranking|performance|user|
|1|0.88|36|
|2|0.712444111|444|
|3|0.711|1|
|4|0.33333|9|
|5|0.1006|29|
returned result:
|ranking|performance|user|
|4|0.88|36|
|2|0.712444111|444|
|5|0.711|1|
|3|0.33333|9|
|1|0.1006|29|
Here is my query:
SET #ranking := 0;
SELECT
#ranking := #ranking + 1 as ranking,
w1.user_id,
IFNULL(ROUND(w2.realization / w1.realization), 4), 0) AS performance
FROM work w1
JOIN (
SELECT min(created_at) AS first_month, max(created_at) AS last_month, user_id
FROM work
WHERE (DATE_FOMAT(NOW(), '%Y-%m') = DATE_FORMAT(created_at, '%Y-%m')
GROUP BY user_id
ORDER BY user_id
) AS w ON w1.user_id = w.user_id AND w1.created_at = w.first_month
JOIN work AS w2 ON w1.user_id = w2.user_id AND w2.created_at = w.last_month
ORDER BY performance DESC
UPDATE
Even if I try to wrap it this way, the rankings are not right
SET #ranking := 0;
SELECT #ranking := #ranking + 1 as ranking, a.user_id, a.performance
FROM (
SELECT
w1.user_id,
IFNULL(ROUND(w2.realization / w1.realization), 4), 0) AS performance
FROM work w1
JOIN (
SELECT min(created_at) AS first_month, max(created_at) AS last_month, user_id
FROM work
WHERE (DATE_FOMAT(NOW(), '%Y-%m') = DATE_FORMAT(created_at, '%Y- %m')
GROUP BY user_id
ORDER BY user_id
) AS w ON w1.user_id = w.user_id AND w1.created_at = w.first_month
JOIN work AS w2 ON w1.user_id = w2.user_id AND w2.created_at = w.last_month
ORDER BY performance DESC
) AS a

MySQL get n rows per key

Given a very simple table bits:
ID | bit_id | timestamp | percent | delta
where bitid is varchar(255) and timestamp is a bigint.
There are many, many rows with identical bit_id's. ID is primary and timestamp is not unique.
With the following SQL i get the rows for a specific set of bit_id's:
SELECT bits.bit_id, bits.timestamp, bits.percent
FROM bits
WHERE bits.bit_id IN ( '00e04c0353bc', '00e04c02c749' )
AND bits.timestamp>1480075040
ORDER BY bits.timestamp DESC
What i want is only the 5 latest rows, per bit_id that match the WHERE-statement. So for each given bit_id in the subset, i want the 5 newest rows.
So simply adding LIMIT n won't do.
How? My MySQL-version does not work with LIMIT in sub-selects.
If you have only two values, then the easiest way is union all:
(SELECT b.bit_id, b.timestamp, b.percent
FROM bits b
WHERE b.bit_id = '00e04c0353bc'
b.timestamp > 1480075040
ORDER BY b.timestamp DESC
LIMIT 5
) UNION ALL
(SELECT b.bit_id, b.timestamp, b.percent
FROM bits b
WHERE b.bit_id = '00e04c02c749'
b.timestamp > 1480075040
ORDER BY b.timestamp DESC
LIMIT 5
);
I'm not sure if the WHERE condition on timestamp is necessary.
If you wanted to do this for more bit_ids or all of them, then variables might be simpler:
select b.*
from (select b.*,
(#rn := if(#b = bit_id, #rn + 1,
if(#b = bit_id, 1, 1)
)
) as rn
from bits b cross join
(select #b := '', #rn := 0) params
order by b.bit_id, b.timestamp desc
) b
where rn <= 5;

How to get the maximum count, and ID from the table SQL

**castID**
nm0000116
nm0000116
nm0000116
nm0000116
nm0000116
nm0634240
nm0634240
nm0798899
This is my table (created as a view). Now I want to list the castID which has the most count (in this case which is nm0000116, and how many occurences/count it has in this table ( should be 5 times) and I'm not quite sure which query to use
try
Select CastId, count(*) countOfCastId
From table
Group By CastId
Having count(*)
= (Select Max(cnt)
From (Select count(*) cnt
From table
Group By CastId) z)
SELECT
MAX(E),
castId
FROM
(SELECT COUNT(castId) AS E,castId FROM [directors winning movies list] GROUP BY castId) AS t
You could just return the topmost count using LIMIT:
SELECT castID,
COUNT(*) AS Cnt
FROM atable
GROUP BY castID
ORDER BY Cnt DESC
LIMIT 1
;
However, if there can be ties, the above query would return only one row. If you want all the "winners", you could take the count from the above query as a scalar result and compare it against all the counts to return only those that match:
SELECT castID,
COUNT(*) AS Cnt
FROM atable
GROUP BY castID
HAVING COUNT(*) = (
SELECT COUNT(*)
FROM atable
GROUP BY castID
ORDER BY Cnt DESC
LIMIT 1
)
;
(Basically, same as Charles Bretana's approach, it just derives the top count differently.)
Alternatively, you could use a variable to rank all the counts and then return only those that have the ranking of 1:
SELECT castID,
Cnt
FROM (
SELECT castID,
COUNT(*) AS Cnt,
#r := IFNULL(#r, 0) + 1 AS r
FROM atable
GROUP BY castID
ORDER BY Cnt DESC
) AS s
WHERE r = 1
;
Please note that with the above method the variable must either not exist or be pre-initialised with a 0 or NULL prior to running the query. To be on the safe side, you could initialise the variable directly in your query:
SELECT s.castID,
s.Cnt
FROM (SELECT #r := 0) AS x
CROSS JOIN
(
SELECT castID,
COUNT(*) AS Cnt,
#r := #r + 1 AS r
FROM atable
GROUP BY castID
ORDER BY Cnt DESC
) AS s
WHERE s.r = 1
;

Select users with a difference larger than zero between the last two entries in a specific table with SQL?

Here's the SQLFiddle Link to my tables.
I basically want to select only Jack and Jill, as there is a non-zero difference between the last two nums entries of the table foo with the user being their respective names.
How is this possible?
Note: just to mention, in my foo table, I have around 100000 rows, so it would be good if there was a very quick and fast way of retrieving the data.
I prefer doing this using limit with the offset to get the two most recent values. Happily, your table has an id column for determining the order.
select user,
(select num from foo f2 where f2.user = f.user order by f2.id desc limit 1
) lastval,
(select num from foo f2 where f2.user = f.user order by f2.id desc limit 1, 2
) lastval2
from foo f
group by user
having lastval <> lastval2
Here's one way (although I think you'd be more likely to JOIN on a user's id rather than their name!?!...
SELECT u.*
FROM
( SELECT x.*, COUNT(*) rank FROM foo x JOIN foo y ON y.user = x.user AND y.id >= x.id GROUP BY x.id)a
LEFT
JOIN
( SELECT x.*, COUNT(*) rank FROM foo x JOIN foo y ON y.user = x.user AND y.id >= x.id GROUP BY x.id)b
ON b.user = a.user
AND b.num = a.num
AND b.rank = a.rank + 1
JOIN users u
ON u.user = a.user
WHERE b.id IS NULL
AND a.rank = 1;
I think this query can be rewritten as follows, which might be faster...
SELECT u.*
FROM
( SELECT id
, user
, num
, #prev_user := #curr_user
, #curr_user := user
, #rank := IF(#prev_user = #curr_user, #rank+1, #rank:=1) rank
FROM foo
JOIN (SELECT #curr_user := null, #prev_user := null, #rank := 0) sel1
ORDER
BY user
, id DESC
) a
LEFT
JOIN
( SELECT id
, user
, num
, #prev_user := #curr_user
, #curr_user := user
, #rank := IF(#prev_user = #curr_user, #rank+1, #rank:=1) rank
FROM foo
JOIN (SELECT #curr_user := null, #prev_user := null, #rank := 0) sel1
ORDER
BY user
, id DESC
) b
ON b.user = a.user
AND b.num = a.num
AND b.rank = a.rank + 1
JOIN users u
ON u.user = a.user
WHERE b.id IS NULL
AND a.rank = 1;
Based on Strawberrys 2nd solution I have tried this.
SELECT user, MIN(num) AS MinNum, MAX(num) AS MaxNum
FROM ( SELECT id
, user
, num
, #prev_user := #curr_user
, #curr_user := user
, #rank := IF(#prev_user = #curr_user, #rank+1, 1) AS rank
FROM foo
JOIN (SELECT #curr_user := null, #prev_user := null, #rank := 1) sel1
ORDER BY user, id DESC
) AS Sub
WHERE rank <= 2
GROUP BY user
HAVING MinNum != MaxNum
This is getting the details ranked as a subselect and rejecting where the rank is greater than 2 (unfortunately the user variables give strange results if you try and check this within the subselect). The results are then grouped on user and the min and max value of num are returned. If they are different then the row is returned (and as you only have 1 or 2 rows per user, the min and max will only be different if there are 2 rows returned AND they have different values).
Advantage of this is that it avoids joining 2 100000 sets against each other and also only needs to do the ranking once (although you would hope that MySQL would optimise this 2nd issue away anyway).

MySQL: JOIN syntax + selects within selects = Operand Error

I've been wrestling with this query for a while. Here it is:
select First10.mal, Last10.family_name,
(select * from gender2
JOIN
(select * from status) as Mix1
JOIN
(select * from age as Mix2 order by rand() limit 10) as Mix3
JOIN
(select incidentid from incidentid2 as Mix4)
as Mix5 where data='mal' and incidentid='6' and status IN ('inj','ali') and age IN ('NULL','0-17')
order by rand() limit 100)
from ( select fn.mal, #fns := #fns + 1 as Sequence
from ( select mal from fnames where mal IS NOT NULL order by rand() limit 100) fn,
(select #fns := 0 ) vars ) First10
JOIN
( select ln.family_name, #lns := #lns + 1 as Sequence
from ( select family_name from lastnames order by rand() limit 100 ) ln,
(select #lns := 0 ) vars ) Last10
ON First10.Sequence = Last10.Sequence;
My goal here is to insert into a table which would present a randomized first name, last name, gender, status, age, and incident id. I have tried many ways to rework this script, including seperating the select statements, but I always seem to end up with this error:
ERROR 1241 (21000): Operand should contain 1 column(s)
Please advise guys, this has been stressing me out for a while now... It's probably a very simple answer, but I am just confusing myself further. If you need any clarifications, just ask.
Well, it looks like after a good night's sleep and some help from a friend, I got this query working. For those that are looking for an answer to a similar question, here is how I got this to work:
select mal,family_name,data,age,status,incidentid
from ( select fn.mal, #fns := #fns + 1 as Sequence
from ( select mal from fnames where mal IS NOT NULL order by rand() limit 100) fn,
(select #fns := 0 ) vars ) as FN
INNER JOIN
(select ln.family_name, #lns := #lns + 1 as Sequence
from ( select family_name from lastnames order by rand() limit 100 ) ln,
(select #lns := 0 ) vars ) as LN
INNER JOIN
(select * from gender2) as Mix0
INNER JOIN
(select * from status) as Mix1
INNER JOIN
(select * from age as Mix2 order by rand() limit 3) as Mix3
INNER JOIN
(select incidentid from incidentid2 as Mix4)
as Mix5 where data='mal' and incidentid='6' and status IN ('inj','ali') and age IN ('NULL','0-17')
order by rand() limit 100;