Thanks for looking, I'm trying to get 20 entries from the database randomly and unique, so the same one doesn't appear twice. But I also have a questionGroup field, which should also not appear twice. I want to make that field distinct, but then get the ID of the field selected.
Below is my NOT WORKING script, because it does the ID as distinct too which
SELECT DISTINCT `questionGroup`,`id`
FROM `questions`
WHERE `area`='1'
ORDER BY rand() LIMIT 20
Any advise is greatly appreciated!
Thanks
Try doing the group by/distinct first in a subquery:
select *
from (select distinct `questionGroup`,`id`
from `questions`
where `area`='1'
) qc
order by rand()
limit 20
I see . . . What you want is to select a random row from each group, and then limit it to 20 groups. This is a harder problem. I'm not sure if you can do this accurately with a single query in mysql, not using variables or outside tables.
Here is an approximation:
select *
from (select `questionGroup`
coalesce(max(case when rand()*num < 1 then id end), min(id)) as id
from `questions` q join
(select questionGroup, count(*) as num
from questions
group by questionGroup
) qg
on qg.questionGroup = q.questionGroup
where `area`='1'
group by questionGroup
) qc
order by rand()
limit 20
This uses rand() to select an id, taking, on average two per grouping (but it is random, so sometimes 0, 1, 2, etc.). It chooses the max() of these. If none appear, then it takes the minimum.
This will be slightly biased away from the maximum id (or minimum, if you switch the min's and max's in the equation). For most applications, I'm not sure that this bias would make a big difference. In other databases that support ranking functions, you can solve the problem directly.
Something like this
SELECT DISTINCT *
FROM (
SELECT `questionGroup`,`id`
FROM `questions`
WHERE `area`='1'
ORDER BY rand()
) As q
LIMIT 20
Related
The query
(SELECT timest, `char`, exp_cnt AS XP FROM `charac` WHERE DATE(timest)='2017-01-30')
UNION
(SELECT timest, `char`, -exp_cnt AS XP FROM `charac` WHERE DATE(timest)=ADDDATE('2017-01-30',-1))
ORDER BY `char` ASC
LIMIT 50
gives me this result:
Where the date is 01-29, the values should not be 0, usually.
I try to debug it by copying-pasting and running the 2ndSELECT:
SELECT timest, `char`, -exp_cnt AS XP FROM `charac` WHERE DATE(timest)=ADDDATE('2017-01-30',-1)
ORDER BY `char` ASC
And I get correct results!
Why? I am confused. UNION ALL does not help.
First, you should use union all rather than union unless you want to incur the overhead of removing duplicates.
Second, you have a limit on the query. So, your results are probably being generated, they are just further down the list and cut-off.
If you care about what happens on 2017-01-29, then sort the list by date and do the comparison that way:
(SELECT timest, `char`, exp_cnt AS XP
FROM `charac`
WHERE DATE(timest) = '2017-01-30'
) UNION ALL
(SELECT timest, `char`, -exp_cnt AS XP
FROM `charac`
WHERE DATE(timest) = ADDDATE('2017-01-30', -1)
)
ORDER BY timest ASC
LIMIT 50;
This may not be the final result you want, but you will at least see that all rows are accounted for.
If you don't want 0 values on the 29th, then explicitly put that into the query:
(SELECT timest, `char`, exp_cnt AS XP
FROM `charac`
WHERE DATE(timest) = '2017-01-30'
) UNION ALL
(SELECT timest, `char`, -exp_cnt AS XP
FROM `charac`
WHERE DATE(timest) = ADDDATE('2017-01-30', -1) AND
exp_cnt <> 0
)
ORDER BY timest ASC
LIMIT 50;
How many rows for each day? I'll bet there are fewer than 50 for the 30th. I'll bet the UNION got all the rows from the 30th, then some from the 29th. That would explain it.
To further test my theory, run each SELECT in the UNION separately.
Another test: Swap the two SELECTs in the UNION. Now it may lose some of the rows from the 30th.
If you want the first 50 of both, use this pattern:
( SELECT ... ORDER BY timest LIMIT 50 )
UNION ALL
( SELECT ... ORDER BY timest LIMIT 50 )
ORDER BY timest LIMIT 50
If you need an OFFSET, too, it gets messier, but is possible:
For, say, the 3rd page:
Inside, do LIMIT 150 -- to be sure to get at least 150 from each (if available)
Outside, do LIMIT 100, 50 -- to get just the 3rd page after the ORDER BY.
But... There is still another issue. Since timest is not unique, the 50 picked is not really deterministic. So, you might want to change to ORDER BY timest, id or ORDER BY timest, char.
I need to show ordered 20 records on my grid but I can't use LIMIT because of my generator(Scriptcase) using LIMIT to show lines per page. It's generator's bug but I need to solve it for my project. So is it possible to show 20 ordered record from my table with a query?
As from comments,if you can't use limit then you can rank your results on basis of some order and in parent select filter limit the results by rank number
select * from (
select *
,#r:=#r + 1 as row_num
from your_table_name
cross join (select #r:=0)t
order by some_column asc /* or desc*/
) t1
where row_num <= 20
Demo with rank no.
Another hackish way would be using group_concat() with order by to get the list of ids ordered on asc/desc and substring_index to pick the desired ids like you need 20 records then join with same table using find_in_set ,But this solution will be very expensive in terms of performance and group_concat limitations if you need more than 20 records
select t.*
from your_table_name t
join (
select
substring_index(group_concat(id order by some_column asc),',',20) ids_list
from your_table_name
) t1 on (find_in_set(t.id , t1.ids_list) > 0)
Demo without rank
What about SELECT in SELECT:
SELECT *
FROM (
-- there put your query
-- with LIMIT 20
) q
So outer SELECT is without LIMIT and your generator can add own.
In a Scriptcase Grid, you CAN use Limit. This is a valid SQL query that selects only the first 20 records from a table. The grid is set to show only 10 records per page, so it will show 20 results split in a total of 2 pages:
SELECT
ProductID,
ProductName
FROM
Products
LIMIT 20
Also the embraced query works out well:
SELECT
ProductID,
ProductName
FROM
(SELECT
ProductID,
ProductName
FROM Products LIMIT 20) tmp
I am asking for a query that selects the newest 100 articles, then returns 30 articles of that result by top rated ordering. Something is wrong in my example.
SELECT *
FROM
(
SELECT articleid,articletitle,articleicon,timesviewed
FROM articles
WHERE articlestatus = 1 AND (articletype = 1 || articletype = 6)
ORDER BY articleid DESC LIMIT 100
)
ORDER BY (totalvotepoints/totalvotes) DESC LIMIT 30
Your inner SELECT statement does not include the columns totalvotepoints and totalvotes. Therefore the outer SELECT cannot reference them. Try
SELECT * FROM (
SELECT articleid,articletitle,articleicon,timesviewed,totalvotepoints,totalvotes
FROM articles
WHERE articlestatus = 1 AND articletype IN (1,6)
ORDER BY articleid DESC
LIMIT 100
)
ORDER BY (totalvotepoints/totalvotes)
DESC LIMIT 30
I see two problems here:
The inner SELECT query needs to include the totalvotepoints and totalvotes columns (or even just the result of the division operation).
The inner query needs a name, even though the name is never used. I'm more of a sql server guy, so maybe mysql lets you get away with this, but I'd expect the query to fail without a name supplied after the sub query
Put it all together:
SELECT *
FROM
(
SELECT articleid,articletitle,articleicon,timesviewed,totalvotepoints/totalvotes As rating
FROM articles
WHERE articlestatus = 1 AND articletype IN (1,6)
ORDER BY articleid DESC LIMIT 100
) t
ORDER BY rating DESC LIMIT 30
Also as a Sql Server guy I was surprised to find that the result of dividing two integers is a floating-point type, instead of an integer division. Most systems will do integer division here unless you specifically cast one side to a floating point type, if for no other reason than that sometimes you need integer division. It seems that in MySql, the only way to do integer division is with the DIV operator, which is a non-standard extension to ansi sql.
I have a MYSQL table of sports team results. Each event or match is stored with a for and against goals scored value. What I would like to do is retrieve an ordered list matches order by goals conceeded (ascending).
It seems simple enough until the team in question is the away team:
in which case we are looking at goals at for.
When the team in question is the home team we are looking at goals 'against'.
I can written the following query:
(SELECT * FROM `matches`,`teams`,`outcomes`
WHERE `home_team_id`=11 AND `matches`.away_team_id=`teams`.team_id
AND `matches`.score_id=`outcomes`.outcome_id
ORDER BY `against`,`date` DESC LIMIT 0,20)
UNION
(SELECT * FROM `matches`,`teams`,`outcomes`
WHERE `away_team_id`=11 AND `matches`.home_team_id=`teams`.team_id
AND `matches`.score_id=`outcomes`.outcome_id
ORDER BY `for`,`date` DESC LIMIT 0,20)
It works but the result set is in two halves, I want to combine the results and order by conceeded whether the team is home or away. Do I need an alias to do this?
Thank you.
Try to make a UNION query with the fields you need, renaming the for or against fields so that they have the same name. Then select everything from this table union and order by the renamed field:
select * from
((SELECT matches.*, teams.*, outcomes.against as goals
FROM matches,teams,outcomes
WHERE
matches.home_team_id=11
AND matches.away_team_id=teams.team_id
AND matches.score_id=outcomes.outcome_id
)
UNION
(SELECT matches.*, teams.*, outcomes.for as goals
FROM matches,teams,outcomes
WHERE matches.away_team_id=11
AND matches.home_team_id=teams.team_id
AND matches.score_id=outcomes.outcome_id
)) as union_table
order by goals, date desc limit 0,20;
This query executes perfectly in MySQL database.
since you already have the two halves you can just select everything from your query and sort accordingly:
SELECT * FROM
(
(SELECT * FROM `matches`,`teams`,`outcomes`
WHERE `home_team_id`=11 AND `matches`.away_team_id=`teams`.team_id
AND `matches`.score_id=`outcomes`.outcome_id
ORDER BY `against`,`date` DESC LIMIT 0,20)
UNION
(SELECT * FROM `matches`,`teams`,`outcomes`
WHERE `away_team_id`=11 AND `matches`.home_team_id=`teams`.team_id
AND `matches`.score_id=`outcomes`.outcome_id
ORDER BY `for`,`date` DESC LIMIT 0,20)
) as results order by results.conceeded asc
I need to display 10 related videos on a video page that come from the same category as that video. The problem is that there could possibly be hundreds of thousands of rows for each category so running RAND() is out of the question and I would prefer not to create a myisam table that matches my innodb table and then full text search for related.
I am not sure if my idea is possible, but I would like to select 100 of the latest rows for that category ordered by date, and then select only 10 from that set randomly.
Is this possible and could you point me in the right direction please?
I'm assuming you have a simple table with an identity named ID, and you can do something like:
SELECT *
FROM (
SELECT ID, Name, VideoFile
FROM VideoTable
ORDER BY ID DESC
LIMIT 100
) Derived
ORDER BY RAND()
LIMIT 10
select * from
(select * from table ORDER BY DESC LIMIT 100)
ORDER BY rand()
LIMIT 10