I'm trying to fetch a random row from a table in MySQL. I'm using the following query:
SELECT *
FROM my_table
WHERE id IN (
SELECT ROUND((RAND() * (MAX(id)-MIN(id)))+MIN(id)) AS id
FROM my_table
);
I wish to understand why that query sometimes returns more than 1 row when the inner query always get 1 row?
Thanks
Just to give you a hint, this will always return 1 random row so refactor your query.
Note: Your query will be very expensive if you have many rows in DB.
SELECT * FROM table
WHERE column = 'data'
ORDER BY RAND()
LIMIT 1
use this instead
SELECT *
FROM my_table
WHERE id = (
SELECT ROUND((RAND() * (MAX(id)-MIN(id)))+MIN(id)) AS id
FROM my_table
);
I ended with the following stored procedure
BEGIN
SET #indx=0;
DROP TEMPORARY TABLE IF EXISTS temp;
CREATE TEMPORARY TABLE temp
SELECT (#indx := #indx+1) AS indx, mt.id
FROM my_table mt;
BEGIN
DECLARE v_random_index INTEGER;
DECLARE v_random_id INTEGER;
SELECT ROUND((RAND() * (MAX(indx)-MIN(indx)))+MIN(indx)) INTO v_random_index
FROM temp;
SELECT id INTO v_random_id
FROM temp
WHERE indx=v_random_index;
SELECT *
FROM my_table mt
WHERE id=v_random_id;
END;
END;
Related
I Have Fix 10 Rows Per Page , If 2records Comes From Query Then I Want To Show 8 Blank Rows. How To Fix It ?
This is a fairly generic example. It just counts the actual rows, calculates how many rows are required to round up to the nearest 10 and then UNIONs a query that generates blank rows.
SELECT *
FROM (
SELECT
RowN = ROW_NUMBER() OVER(ORDER BY myColumn), -- order by any column
*
FROM myTable
UNION ALL
SELECT
TOP (SELECT ExtraRows = (FLOOR((Count(*)+9)/10) * 10) - COUNT(*) FROM myTable) -- 10 here is rows per page
NewRowNumber = ROW_NUMBER() OVER (ORDER BY [object_id]) + (SELECT COUNT(*) FROM myTable)
,NULL, NULL, NULL -- one nulll value for each column in myTable
FROM sys.all_columns
) u
ORDER by u.RowN -- add any additional required sorting here
If your current query is not simple then dump the results of that into a temp table
SELECT *
INTO #t
FROM ...
myBigQuery
then change the references to myTable in the main query above to #t or whatever the temp table is called.
EDIT for using with SP
If using a Stored proc then you can dump the results of that into a temp table and do the same. For exmaple
CREATE TABLE #t (ColA int, ColB varchar(100)....)
INSERT INTO #t
EXEC myStoredProc
...
the main query from above
...
Just swap out all references to myTable with #t
How can I get row count values in MySQL as ##ROWCOUNT does in mssql?
For SELECTs you can use the FOUND_ROWS construct (documented here):
SELECT SQL_CALC_FOUND_ROWS something FROM your_table WHERE whatever;
SELECT FOUND_ROWS( ) ;
which will return the number of rows in the last SELECT query (or if the first query has a LIMIT clause, it returns the number of rows there would've been without the LIMIT).
For UPDATE/DELETE/INSERT, it's the ROW_COUNT construct
INSERT INTO your_table VALUES (1,2,3);
SELECT ROW_COUNT();
which will return the number of affected rows.
mysql> SELECT SQL_CALC_FOUND_ROWS * FROM tbl_name
-> WHERE id > 100 LIMIT 10;
mysql> SELECT FOUND_ROWS();
Read more about this here
The simplest way would be to use a variable:
mysql> SELECT #rowcount:=COUNT(*) FROM my_table;
mysql> SELECT #rowcount;
Or you can use FOUND_ROWS() construct after putting a SQL_CALC_FOUND_ROWS in a SELECT statement.
mysql> SELECT SQL_CALC_FOUND_ROWS * FROM my_table;
mysql> SELECT FOUND_ROWS();
There is another way:
CREATE TEMPORARY TABLE `results` AS ( *** Your query without LIMIT *** );
Get the row count
SELECT COUNT(*) FROM `results`;
Get your subset
SELECT * FROM `results` LIMIT 5,10;
The temporary table exists only in the current session. I would still clean-up afterwards
DROP TEMPORARY TABLE `results`;
Count the number of rows in a sub-query in the where clause. Then test if the total number of rows is greater than zero.
SELECT customerNumber,
customerName,
creditLimit
FROM customers
where (SELECT count(*) as tot
FROM customers) > 0;
I need to select multiple rows in MySQL. I have procedure called 'proc_table' which returns me now 3 fields - oid, objet, and 1 row from db.cars. Query is limited (by me).
I need to delete that limit and make query which ll return me first 5 rows. If they don't exist there will be no reference (field with null value). I make some query in 'pseudo code'.
Q: What function in MySQL I should use to select specific rows? How deal with situation if they don't exist?
CREATE procedure `proc_table`()
BEGIN
SELECT a.oid as 'oid',
a.objet as 'objet',
-- limited query
(select tab2_oid from db.cars where tab1_oid = a.oid LIMIT 1)
-- limited query
FROM db.tab1 as a;
END$$
pseudo code
-- limited query
(select tab2_oid as 'first_row' from db.cars where tab1_oid = a.oid where row=1)
(select tab2_oid as 'second_row' from db.cars where tab1_oid = a.oid where row=2)
...
-- limited query
how about using an union and a LIMIT statement?
select feld1,feld2 from
( select feld1,feld2 from table1 where... LIMIT 1,1
union all
select feld1,feld2 from table2 where....LIMIT 2,1
....
)a LIMIT 5
I'm going to ask a question that has been asked in very abstract terms, with (understandably) no concrete answers provided:
From the MySQL prompt, how do I create and populate a table, rand_numbers, with one column, number INT, and 1111 rows, where the number column holds a random number between 2222 and 5555?
Something like:
CREATE TABLE rand_numbers(number INT);
#run following line 1111 times
INSERT INTO rand_numbers (number) VALUES (2222 + CEIL( RAND() * 3333));
This question has been asked, but either relies on external languages for the loop or is far too general. I would like to know if it's possible to do something this simple from a typical Linux MySQL prompt.
To create the table use:
CREATE TABLE rand_numbers (
number INT NOT NULL
) ENGINE = MYISAM;
Then to populate it with random values, you can define a stored procedure (which supports looping):
DELIMITER $$
CREATE PROCEDURE InsertRand(IN NumRows INT, IN MinVal INT, IN MaxVal INT)
BEGIN
DECLARE i INT;
SET i = 1;
START TRANSACTION;
WHILE i <= NumRows DO
INSERT INTO rand_numbers VALUES (MinVal + CEIL(RAND() * (MaxVal - MinVal)));
SET i = i + 1;
END WHILE;
COMMIT;
END$$
DELIMITER ;
CALL InsertRand(1111, 2222, 5555);
Then you can reuse that procedure to insert more random values based on different parameters.. say 600 rows with random values between 1200 and 8500:
CALL InsertRand(600, 1200, 8500);
Without creating a stored procedure, one technique I've applied is to use the table itself to add the columns. First seed it with a value...
INSERT INTO rand_numbers ( number ) VALUES ( rand() * 3333 );
Then insert again, selecting from this table to double the rows each time...
INSERT INTO rand_numbers ( number ) SELECT number * rand() FROM rand_numbers;
You don't need to run the second query that many times to get quite a few random rows. Not as "neat" as using a stored procedure of course, just proposing an alternative.
As pointed out by mohamed23gharbi, you can run into duplicates if your test mass is too large. You can use INSERT IGNORE to skip duplicates if that is a problem.
The task can be done also this way:
-- scale from 0 to MAX
UPDATE `table` SET `column` = 1000 * RAND() WHERE 1;
-- scale from MIN to MAX
UPDATE `table` SET `column` = MIN + (MAX - MIN) * RAND() WHERE 1;
You can also use math function like FLOOR(), CEIL(), etc. in the expression..
I have always used this -
insert into rand_numbers ( number ) select rand() from (
select 0 as i
union select 1 union select 2 union select 3
union select 4 union select 5 union select 6
union select 7 union select 8 union select 9
) as t1, (
select 0 as i
union select 1 union select 2 union select 3
union select 4 union select 5 union select 6
union select 7 union select 8 union select 9
) as t2, (
select 0 as i
union select 1 union select 2 union select 3
union select 4 union select 5 union select 6
union select 7 union select 8 union select 9
) as t3;
Inserts 1000 random numbers. On-the-fly tables t1, t2, t3 are cross joined so we get 10x10x10 rows.
So, for like a million rows, just add 3 more of
(select 0 as i union select 1 ...) as statements. This seems convenient to me, since there's not much effort copy-pasting a few lines a bunch of times.
Hope this helps,
If you are lazy and you have the query for creating the table, try http://filldb.info//
I have the following query where I would like to return two result sets. One being the results as a table and the second just returning the number of potential results, or MaxResults.
The last line errors with Invalid object name ResultsTemp. It does not work until I comment out the second last line. It appears I can only use the ResultsTemp once.
DECLARE #StartRow int;
DECLARE #MaxRows int;
set #StartRow = 0;
set #MaxRows = 5;
WITH ResultsTemp AS
(
SELECT ROW_NUMBER() OVER (ORDER BY FTS.RANK DESC) AS RowId,
Id, Name FROM tNews
INNER JOIN CONTAINSTABLE(tNews, *, 'FORMSOF(INFLECTIONAL, hello)')
AS FTS ON tNews.Id = FTS.[KEY]
)
SELECT Id, Name, RowId FROM ResultsTemp
Group By Id, Name, RowId
Having RowId between #StartRow and (#StartRow + #MaxRows);
select COUNT(*) from ResultsTemp;
thank you
This can't be done (as far as I'm aware). There's three workarounds, one which allows you to keep as two distinct result sets, and the other two which require merging the results into the same result set (as either an extra row or an extra column).
Instead of a CTE, push the results into a temporary table then query from that.
Merge the count into the actual result set as another row: use a UNION ALL and give the count row suitable values for ID, Name and RowID so you can extract it from the rest of the data
Add the SELECT COUNT(*) into the primary result set as an extra column with a CROSS JOIN or similar.
For this latter, you can do it by changing your primary query to:
SELECT Id, Name, RowId, countTable.totalRows
FROM ResultsTemp
CROSS JOIN (SELECT COUNT(*) AS totalRows FROM ResultsTemp) AS countTable
GROUP BY Id, Name, RowId
HAVING RowId BETWEEN #StartRow and (#StartRow + #MaxRows);
I can't vouch for the performance - you'd need to profile.
Create table #temp
(
Id int,
Name Varchar(100),
RowId int
)
DECLARE #StartRow int;
DECLARE #MaxRows int;
set #StartRow = 0;
set #MaxRows = 5;
SELECT ROW_NUMBER() OVER (ORDER BY FTS.RANK DESC) AS RowId,
Id, Name into #temp FROM tNews
INNER JOIN CONTAINSTABLE(tNews, *, 'FORMSOF(INFLECTIONAL, hello)')
AS FTS ON tNews.Id = FTS.[KEY]
SELECT Id, Name, RowId FROM #temp
Group By Id, Name, RowId
Having RowId between #StartRow and (#StartRow + #MaxRows);
select COUNT(RowId) from #temp;