I have two tables:
exam_outline_items:
jml_quiz_pool:
Of all the things I've tried, this got me the closest:
select t1.sequence, t1.title, t2.q_cat, t2.q_count
from student_pl.exam_outline_items t1
cross join pe_joomla.jml_quiz_pool t2
where t1.exam_outline_id = 5 and t1.chapter_num > 0
and t2.q_id = 1109 and t2.q_count > 0
group by title
Which produces this result:
I just need those q_cat values to be different, like they are in the 2nd query.
Thanks in advance for your help.
You have to have something to connect them with. If you don't have such a column, you can simulate one by creating a rownumber with variables.
select sequence, title, q_cat, q_count from (
select t1.sequence, t1.title, #r1 := #r1 + 1 as rownumber
from student_pl.exam_outline_items t1
, (select #r1 := 0) var_init
where t1.exam_outline_id = 5 and t1.chapter_num > 0
order by t1.sequence
) a
inner join
(
select t2.q_cat, t2.q_count, #r2 := #r2 + 1 as rownumber
from pe_joomla.jml_quiz_pool t2
, (select #r2 := 0) var_init
where t2.q_id = 1109 and t2.q_count > 0
order by t2.q_cat
) b on a.rownumber = b.rownumber;
Also note, that I used order by in those queries. In a database you have no sort order unless you explicitly set it with order by.
Related
I am using a modified version of a query similiar to another question here:Convert SQL Server query to MySQL
Select *
from
(
SELECT tbl.*, #counter := #counter +1 counter
FROM (select #counter:=0) initvar, tbl
Where client_id = 55
ORDER BY ordcolumn
) X
where counter >= (80/100 * #counter);
ORDER BY ordcolumn
tbl.* contains the field 'client_id' and I am attempting to get the top 20% of the records for each client_id in a single statement. Right now if I feed it a single client_id in the where statement it gives me the correct results, however if I feed it multiple client_id's it simply takes the top 20% of the combined recordset instead of doing each client_id individually.
I'm aware of how to do this in most databases, but the logic in MySQL is eluding me. I get the feeling it involves some ranking and partitioning.
Sample data is pretty straight forward.
Client_id rate
1 1
1 2
1 3
(etc to rate = 100)
2 1
2 2
2 3
(etc to rate = 100)
Actual values aren't that clean, but it works.
As an added bonus...there is also a date field associated to these records and 1 to 100 exists for this client for multiple dates. I need to grab the top 20% of records for each client_id, year(date),month(date)
You need to do the enumeration for each client:
SELECT *
FROM (SELECT tbl.*, #counter := #counter +1 counter
(#rn := if(#c = client_id, #rn + 1,
if(#c := client_id, 1, 1)
)
)
FROM (select #c := -1, #rn := 0) initvar CROSS JOIN tbl
ORDER BY client_id, ordcolumn
) t cross join
(SELECT client_id, COUNT(*) as cnt
FROM tbl
GROUP BY client_id
) tt
where rn >= (80/100 * tt.cnt);
ORDER BY ordcolumn;
Using Gordon's answer as a starting point, I think this might be closer to what you need.
SELECT t.*
, (#counter := #counter+1) AS overallRow
, (#clientRow := if(#prevClient = t.client_id, #clientRow + 1,
if(#prevClient := t.client_id, 1, 1) -- This just updates #prevClient without creating an extra field, though it makes it a little harder to read
)
) AS clientRow
-- Alteratively (for everything done in clientRow)
, #clientRow := if(#prevClient = t.client_id, #clientRow + 1, 1) AS clientRow
, #prevClient := t.client_id AS extraField
-- This may be more reliable as well; I not sure if the order
-- of evaluation of IF(,,) is reliable enough to guarantee
-- no side effects in the non-"alternatively" clientRow calculation.
FROM tbl AS t
INNER JOIN (
SELECT client_id, COUNT(*) AS c
FROM tbl
GROUP BY client_id
) AS cc ON tbl.client_id = cc.client_id
INNER JOIN (select #prevClient := -1, #clientRow := 0) AS initvar ON 1 = 1
WHERE t.client_id = 55
HAVING clientRow * 5 < cc.c -- You can use a HAVING without a GROUP BY in MySQL
-- (note that clientRow is derived, so you cannot use it in the `WHERE`)
ORDER BY t.client_id, t.ordcolumn
;
Imagine a table just filled with ID's and created timestamps, how would I convert this over to MySQL?:
SELECT created AS col_a , LAG (created) OVER ( ORDER by created ) AS col_b
FROM tester
You can use a correlated subquery:
SELECT t1.created AS col_a,
(SELECT created
FROM tester AS t2
WHERE t2.created < t1.created
ORDER BY created DESC LIMIT 1) AS col_b
FROM tester AS t1
or, use variables:
SELECT t1.created AS col_a, t2.created AS col_b
FROM (
SELECT created, #rn1 := #rn1 + 1 AS rn
FROM tester
CROSS JOIN (SELECT #rn1 := 0) AS var
ORDER BY created) AS t1
LEFT JOIN (
SELECT created, #rn2 := #rn2 + 1 AS rn
FROM tester
CROSS JOIN (SELECT #rn2 := 0) AS var
ORDER BY created
) AS t2 ON t1.rn = t2.rn + 1
I'm trying to produce an incremental counter (column 'rownum' in the query below) in an SQL select clause. the counter should start over each time a new user switches.
SELECT * FROM (
SELECT CONCAT(' ',g.node1,' ',g.node2),
#r:= CASE WHEN #g = g.`user` THEN #r +1 ELSE 1 END rownum,
#g:= g.`user` user_group
FROM sn.sn_graph_reduced g
CROSS JOIN (SELECT #g:=0,#r:=0) t2
ORDER BY `user` , RAND()
) t
WHERE rownum <= 100
However, the above code snippet returns the row number, and since the records are RANDOMLY sampled, the row numbers are not incremental.
What I need is a simple counter (1,2,3....) for each row returned.
thanks
Try putting the variable outside of the inline view:
SELECT t.*,
#r:= CASE WHEN #t = t.`user` THEN #r +1 ELSE 1 END rownum,
#t:= t.`user` user_group
FROM (
SELECT CONCAT(' ',g.node1,' ',g.node2), g.`user`
FROM sn.sn_graph_reduced g
ORDER BY `user` , RAND()
) t
CROSS JOIN (SELECT #t:=0,#r:=0) t2
where rownum <= 100
the method for incrementing row number doesn't cope with RAND() directly, so use values from rand() as a column. Also initiate #g as '' not zero and then you need a final ORDER BY.
SELECT
*
FROM (
SELECT
CONCAT(' ', g.node1, ' ', g.node2) AS node_concat
, #r:= IF(#g = g.`user`, #r + 1, 1) AS rownum
, #g:= g.`user` AS user_group
FROM (
SELECT *, rand() AS R FROM sn_graph_reduced
) g
CROSS JOIN ( SELECT #g:= '' ,#r:= 1 ) t2
ORDER BY
`user`
, R
) t
WHERE rownum <= 100
ORDER BY
user_group
, rownum
;
see: This SQLfiddle
I have a Mysql Database like below:
id , name , col1
and i want to find all rows that: value of col1 of the row is greater than avrage of maximom 5 rows past
for example if I have 50 rows , and if the row #20 has gotten , the avrage of value of col1 of rows #20,#19,#18,#17,#16 should be less than the value of col1 of row #20 , and so on...
Thank you in advance.
What you seem to want here is running average of past M records starting from current record and we need to select the current record if current record's column value is greater than the running average.
Here is my attempt to it:
SET #M := 2;
SELECT * FROM
(
SELECT (#rownumber:= #rownumber + 1) AS rn, yt.*
FROM your_table yt,(SELECT #rownumber:= 0) nums
ORDER BY name, id
) a
WHERE a.var1 >
(
SELECT avg(b.var1)
FROM
(
SELECT (#rownumber:= #rownumber + 1) AS rn, yt.*
FROM your_table yt,(SELECT #rownumber:= 0) nums
ORDER BY name, id
) b
WHERE b.rn > a.rn - #M AND b.rn <= a.rn
)
#M is count of past records to be considered for finding running average.
Here is the code at SQL Fiddle
[EDIT]:
Here is another solution which according to me should be more efficient than correlated query.
SET #M := 2;
SELECT a.* FROM
(
SELECT (#rownumber:= #rownumber + 1) AS rn, yt.*
FROM your_table yt,(SELECT #rownumber:= 0) nums
ORDER BY name, id
) a
JOIN
(
SELECT b.name, b.rn, AVG(c.var1) AS av
FROM
(
SELECT (#rownumber1:= #rownumber1 + 1) AS rn, yt.*
FROM your_table yt,(SELECT #rownumber1:= 0) nums
ORDER BY name, id
) b
JOIN
(
SELECT (#rownumber2:= #rownumber2 + 1) AS rn, yt.*
FROM your_table yt,(SELECT #rownumber2:= 0) nums
ORDER BY name, id
) c
ON b.name = c.name
AND c.rn > (b.rn - #M) AND c.rn <= b.rn
GROUP BY b.name,b.rn
) runningavg
ON a.name = runningavg.name
AND a.rn = runningavg.rn
AND a.var1 > runningavg.av
Here I have used simple inner join to calculate running average and again with inner join have selected rows which have column value greater than average.
Here is the code at SQL Fiddle
Let me know did it prove to be efficient.
I want to use the "order by rand alternative" query (bottom) to get a random set of results but I want to get them from within the results a query such as:
SELECT t2.id FROM index_table t1 JOIN data_table t2 ON t1.id= t2.index_id
And I need to limit the number of random results I'd get back.
I can't quite get my head around the syntax I'd need to use, any help greatly appreciated.
thanks
"order by rand alternative" query:
How can i optimize MySQL's ORDER BY RAND() function?
SELECT *
FROM (
SELECT #cnt := COUNT(*) + 1,
#lim := 10
FROM t_random
) vars
STRAIGHT_JOIN
(
SELECT r.*,
#lim := #lim - 1
FROM t_random r
WHERE (#cnt := #cnt - 1)
AND RAND(20090301) < #lim / #cnt
) i
SELECT
t2.id
FROM
index_table t1
JOIN data_table t2
ON t1.id= t2.index_id
ORDER BY
RAND()
LIMIT 5
or whatever the maximum number of entries you want via the limit
Would something like this work?
SELECT *
FROM (
SELECT #cnt := COUNT(*) + 1,
#lim := 10
FROM FROM index_table t1 JOIN data_table t2 ON t1.id= t2.index_id
) vars
STRAIGHT_JOIN
(
SELECT t2.id,
#lim := #lim - 1
FROM index_table t1 JOIN data_table t2 ON t1.id= t2.index_id
WHERE (#cnt := #cnt - 1)
AND RAND() < #lim / #cnt
) i
I removed the parameter from RAND otherwise my output was always the same.