How to optimise this temporary table query? - php

I have written a stored procedure that takes comma separated value as input and another value. I traverse values from comma separated value and then run a query for each value. now I need to return result so I thought to store results in temporary table and then selected values from that temporary table.But it is taking too long.
for query with 163 comma separated values it is taking 7 seconds.
for query with 295 comma separated values it is taking 12 seconds.
Here is procedure:-
DELIMITER $
create procedure check_fbid_exists(IN myArrayOfValue TEXT, IN leaderID INT(11) )
BEGIN
DECLARE status TINYINT(1);
DECLARE value INT(11);
DECLARE pos int(11);
CREATE TEMPORARY TABLE fbid_exists_result (userID int(11), status tinyint(1));
WHILE (CHAR_LENGTH(myArrayOfValue) > 0)
DO
SET pos=LOCATE( ',', myArrayOfValue);
IF pos>0 THEN
SET value = LEFT( myArrayOfValue,pos-1 );
SET myArrayOfValue= SUBSTRING( myArrayOfValue,pos+1 );
ELSE
SET value = myArrayOfValue;
SET myArrayOfValue='';
END IF;
SELECT EXISTS(SELECT 1 FROM users_followings WHERE UserID=value and LeaderUserID=leaderID LIMIT 1) INTO status;
insert into fbid_exists_result VALUES(value,status);
END WHILE;
SELECT * FROM fbid_exists_result ;
DROP TEMPORARY TABLE IF EXISTS fbid_exists_result ;
END$

Related

Creating stored procedure for multiple insert and alter table queries

$link->query("DROP TABLE IF EXISTS table2");
$link->query("CREATE TABLE table2 (newcol BIGINT UNSIGNED PRIMARY KEY)");
$result=$link->query("select col1 from table1");
while($data=$result->fetch_array(MYSQL_ASSOC))
{
$link->query("insert into table2 (newcol) values($data['col1']);
$link->query(""ALTER TABLE table2 ADD `".$data['col1']."` BIGINT DEFAULT 0"");
}
What Iam trying to do is
Create a table "table2" with one column "newcol".
select all the values of "col1" from "table1" And
for each value of col1 from table1
-insert the value into "newcol" of table2 And
-add a column named (value from col1 of table 1) into "table2"
The above code looks very neat and efficient in php , but the problem is it takes some amount of time .So I think its better to convert these into MySQL Stored procedure .Since I'm new to stored procedures , very much confused .Please help me guys.
Of course, I couldn't test it, but it is compiling fine on on my computer.
DELIMITER //
CREATE PROCEDURE `myProcedure` ()
BEGIN
DECLARE _done BOOLEAN DEFAULT FALSE;
DECLARE _myField BIGINT UNSIGNED DEFAULT 0;
/* the cursor here is like your PDOStatement
* it is used to fetch data */
DEClARE _myReader CURSOR FOR
SELECT `col1` FROM `table1`;
/* it is not very elegant, but we need to throw an exception
* to break the loop */
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET _done = TRUE;
DROP TABLE IF EXISTS `table2`;
CREATE TABLE `table2` (
`newcol` BIGINT UNSIGNED PRIMARY KEY
);
/* you open your PDOStatement */
OPEN _myReader;
/* myLoop is like a GOTO*/
myLoop: LOOP
/* $result->fetch_array(MYSQL_ASSOC)*/
FETCH _myReader INTO _myField;
/* if the no data exception had been thrown,
* goto the end of the loop */
IF _done = 1 THEN
LEAVE myLoop;
END IF;
INSERT INTO `table2` (newcol) VALUES (_myField);
ALTER TABLE `table2` ADD `_myField` BIGINT DEFAULT 0;
END LOOP myLoop;
/* close your PDO object */
CLOSE _myReader;
END //
Jonathan Parent Lévesque helped me a lot in figuring out how the looping inside stored procedures work and to get the overall structure for the stored procedure equivalent to the php code described in the question above.
Thanks Jonathan Parent Lévesque
But in his code Adding column name using a variable didn't work as expected.
Finally I figured it out
BEGIN
DECLARE _done BOOLEAN DEFAULT FALSE;
DECLARE _myField BIGINT DEFAULT 0;
DEClARE _myReader CURSOR FOR
SELECT id FROM `tags`;
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET _done = TRUE;
DROP TABLE IF EXISTS `tag_similarity`;
CREATE TABLE `tag_similarity` (
`tag` BIGINT UNSIGNED PRIMARY KEY
);
OPEN _myReader;
myLoop: LOOP
FETCH _myReader INTO _myField;
IF _done = 1 THEN
LEAVE myLoop;
END IF;
INSERT INTO `tag_similarity` (tag) VALUES (_myField);
SET #sql = CONCAT('ALTER TABLE tag_similarity ADD `',_myfield,'` BIGINT DEFAULT 0');
PREPARE stmt FROM #sql;
EXECUTE stmt ;
DEALLOCATE PREPARE stmt;
END LOOP myLoop;
CLOSE _myReader;
END

Filter comma separated values from same table

Can anybody help me to get the sub_cat(csv) values which are not in id column from same table.
(sub_cat) comma separated values are the ids of same table, i need to get the values which are not in id column. Like 2,3,7 are present in id column whereas 20,24 are not. I need to get 20,24 only.
As I have elaborated in this post, I recommend not storing data in CSV format.
This gives trouble accessing and updating it.
I am not sure about this, but you might be able to simply use:
SELECT sub_cat FROM table_name WHERE id NOT IN (
SELECT sub_cat FROM table_name
)
However, I always prefer to store only one ID per row.
To do this purely in MySQL required writing a stored procedure; you'll need to change the database name test to the actual name of your database. It would have been a whole lot easier to do it with PHP - but where's the fun in that?
DELIMITER //
CREATE PROCEDURE test.check_subcats(
IN s_delimiter VARCHAR(30)
)
DETERMINISTIC
READS SQL DATA
BEGIN
DECLARE s_csv TEXT;
DECLARE i_subcat_index INT(10) unsigned DEFAULT 1;
DECLARE i_subcat_count INT(10) unsigned;
DECLARE l_category_done INT(10) DEFAULT FALSE;
DECLARE c_category CURSOR FOR SELECT category.sub_cat FROM category;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET l_category_done = TRUE;
-- create a temporary table to hold every csv value
CREATE TEMPORARY TABLE IF NOT EXISTS tmp_csv( cvalue VARCHAR(10) NOT NULL );
OPEN c_category;
l_category: LOOP
FETCH c_category INTO s_csv;
IF l_category_done THEN
LEAVE l_category;
ELSE
-- determine the number of sub-categories
SELECT (LENGTH(s_csv) - LENGTH(REPLACE(s_csv, s_delimiter, ''))) + 1 INTO i_subcat_count;
-- loop to store all csv values
WHILE i_subcat_index <= i_subcat_count DO
INSERT INTO tmp_csv ( cvalue ) (
SELECT REPLACE(SUBSTRING(
SUBSTRING_INDEX(s_csv, s_delimiter, i_subcat_index),
LENGTH(SUBSTRING_INDEX(s_csv, s_delimiter, i_subcat_index - 1)) + 1
), s_delimiter, '')
);
SET i_subcat_index = i_subcat_index + 1;
END WHILE;
END IF;
SET i_subcat_index = 1;
END LOOP;
CLOSE c_category;
SELECT DISTINCT tmp_csv.cvalue FROM tmp_csv WHERE tmp_csv.cvalue NOT IN ( SELECT category.id FROM category );
DROP TEMPORARY TABLE IF EXISTS tmp_csv;
END //
DELIMITER ;
I'm not 100% certain as to how robust it is but it was working with your data on my dev box.
You specify the delimiter for your CSV data when you call the procedure thus:
CALL `check_subcats`(',');
Essentially this loops through the category table to read sub_cat. It then splits the sub_cat value into chunks using the delimiter provided (much like PHPs explode() function) and writes every one of those values to a temporary table.
This then gives you a temporary table holding all your CSV data in individual bits and it's then a simple matter of selecting everything from that data that's NOT IN the category.id list.

Getting the MySQL WHERE IN delimited string

I've been recently stuck on an issue I've been having involving sending a string of comma separated values into a stored procedure. My issue is that when I execute my stored procedure in PHP it uploads the values with quotes around it like so;
CALL `rankingInformation`('145', '5', '', '37,38,39,40,41')
Failing to add the quotes would make MySQL interpret them as extra parameters.
However it's mean't to be like in the WHERE IN on the query side it's meant to be formatted like so
'37', '38', '39', '40', '41'
Here is the query below, can anyone spot anything I can do? Here is what I've got up to now.
CREATE DEFINER = `root`#` localhost` PROCEDURE` rankingInformation`(IN` surveyId` INT, IN` filterCounting` INT, IN` survey_filter_id` INT, IN` question_limit` TEXT)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT 'Gathers all the ranking information for a given ID'
BEGIN
DECLARE sfi int(2);
DECLARE ql TEXT;
IF(survey_filter_id = '') THEN
SET sfi = (SELECT sf2.survey_filter_id FROM survey_filters AS sf2 WHERE sf2.survey_id = 145 AND sf2.survey_filter_id IS NOT NULL LIMIT 1);
ELSE
SET sfi = survey_filter_id;
END IF;
SELECT
COUNT( * ) AS total, CONCAT(su.first_name, ' ', su.last_name) as full_name, sf.survey_filter_id, sf.survey_filter_name, qa.question_id, su.temp_group_1 AS department
FROM questions_answers AS qa
INNER JOIN survey_users AS su ON su.survey_users_id = qa.survey_users_id_answer
INNER JOIN survey_filters AS sf ON sf.survey_id = surveyId
WHERE qa.survey_id = surveyId
AND qa.question_id IN (splitAndTranslate(question_limit, ','))
AND sf.survey_filter_id = sfi
GROUP BY qa.survey_users_id_answer
HAVING total > filterCounting
ORDER BY total DESC;
END
splitAndTranslate
Here is a function I found which is mean't to do the job, I am not sure I am far away.
CREATE DEFINER=`root`#`localhost` FUNCTION `splitAndTranslate`(`str` TEXT, `delim` VARCHAR(1))
RETURNS text CHARSET utf8
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT 'Fixes all Where IN issues'
BEGIN
DECLARE i INT DEFAULT 0; -- total number of delimiters
DECLARE ctr INT DEFAULT 0; -- counter for the loop
DECLARE str_len INT; -- string length,self explanatory
DECLARE out_str text DEFAULT ''; -- return string holder
DECLARE temp_str text DEFAULT ''; -- temporary string holder
DECLARE temp_val VARCHAR(255) DEFAULT ''; -- temporary string holder for query
-- get length
SET str_len=LENGTH(str);
SET i = (LENGTH(str)-LENGTH(REPLACE(str, delim, '')))/LENGTH(delim) + 1;
-- get total number delimeters and add 1
-- add 1 since total separated values are 1 more than the number of delimiters
-- start of while loop
WHILE(ctr<i) DO
-- add 1 to the counter, which will also be used to get the value of the string
SET ctr=ctr+1;
-- get value separated by delimiter using ctr as the index
SET temp_str = REPLACE(SUBSTRING(SUBSTRING_INDEX(str, delim, ctr), LENGTH(SUBSTRING_INDEX(str, delim,ctr - 1)) + 1), delim, '');
-- query real value and insert into temporary value holder, temp_str contains the exploded ID
#SELECT ImageFileName INTO temp_val FROM custombu_roomassets_images WHERE ImageID=temp_str;
-- concat real value into output string separated by delimiter
SET out_str=CONCAT(out_str, temp_val, ',');
END WHILE;
-- end of while loop
-- trim delimiter from end of string
SET out_str=TRIM(TRAILING delim FROM out_str);
RETURN(out_str); -- return
END
What did you do with FIND_IN_SET? Building on spencer7593's answer, it should work if you replace:
AND qa.question_id IN (splitAndTranslate(question_limit, ','))
with
AND FIND_IN_SET(qa.question_id, question_limit)>0
The commas within a string value are not interpreted as SQL text in the context of a SQL IN comparison. Your query is essentially of the form:
AND qa.question_id IN ('some,long,string,value')
And any comma characters within the string are just data; just characters that are part of the string. This is effectively the same as an equals comparison.
The MySQL FIND_IN_SET function might be a way for you to perform the comparison you want.
http://dev.mysql.com/doc/refman/5.5/en/string-functions.html#function_find-in-set
AND qa.question_id IN (splitAndTranslate(question_limit, ','))
Put these lines instead of the above line
AND (qa.question_id = question_limit
OR qa.question_id LIKE CONCAT(question_limit,',%')
OR qa.question_id LIKE CONCAT('%,',question_limit,',%')
OR qa.question_id LIKE CONCAT('%,',question_limit))
Then you don't need the splitAndTranslate Function anymore . .

How to Parse PHP Searilize data in Mysql Stored Procedure & Looping it

I have below data in Mysql Column (storing all data in serialize form - with comma separated into column) and i want to get/fetch this column data in Mysql stored procedure and want to loop for each data and insert into another trans table.
So if my data like below then i want to insert 7 record in trans table.
{"FormBuilderId":"5","vAnswer":"Develeop"},
{"FormBuilderId":"15","vAnswer":"Search Engine"},
{"FormBuilderId":"13","vAnswer":"10-15"},
{"FormBuilderId":"6","vAnswer":"Tester entered"},
{"FormBuilderId":"1","vAnswer":"Female"},
{"FormBuilderId":"14","vAnswer":"Moon.jpg"},
{"FormBuilderId":"12","vAnswer":"TV,dancing and modeling"}
My table structure & data is like below in table:
CREATE TABLE IF NOT EXISTS `idea_history` (
`iIdeaHistoryId` int(11) NOT NULL AUTO_INCREMENT,
`iApplicationId` tinyint(11) unsigned DEFAULT '0',
`tAnswerData` text COMMENT 'all answer data store here in json format',
`dAddedDate` datetime NOT NULL,
PRIMARY KEY (`iIdeaHistoryId`),
KEY `iApplicationId` (`iApplicationId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='history comes here' AUTO_INCREMENT=57 ;
--
-- Dumping data for table `idea_history`
--
INSERT INTO idea_history (
iIdeaHistoryId,
iApplicationId,
tAnswerData,
dAddedDate
)
VALUES
(
53,
2,
'{"FormBuilderId" : "2","vAnswer":"Environmental Group"},{"FormBuilderId" : "11","vAnswer":"Satelite"},{"FormBuilderId" : "3","vAnswer":"HB"},{"FormBuilderId" : "4","vAnswer":"Dev"},{"FormBuilderId" : "7","vAnswer":"HB"},{"FormBuilderId" : "8","vAnswer":"Balaji Satellite"},{"FormBuilderId" : "10","vAnswer":""}',
'2014-07-05 19:20:56'
),
(
54,
1,
'{"FormBuilderId":"5","vAnswer":"Hello krishna|kanth double"},{"FormBuilderId":"15","vAnswer":"Website"},{"FormBuilderId":"6","vAnswer":"need to check"},{"FormBuilderId":"13","vAnswer":"20-25"}',
'2014-07-05 19:20:56'
),
(
55,
2,
'{"FormBuilderId":"11","vAnswer":"comapnay"},{"FormBuilderId":"8","vAnswer":"here am|chw "},{"FormBuilderId" : "10","vAnswer":""},{"FormBuilderId":"9","vAnswer":"Business"}',
'2014-07-05 19:20:56'
) ;
I will pass iIdeaHistoryId in stored procedure and it will fetch value of tAnswerData field and part this value and insert into another trans table.
Could you please guide what i have to change in stored procedure ?
you can do it like this
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(your_variable,'{"',-1),'":"',1) INTO arg_1;
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(your_variable,'":"',-2),'","',1) INTO arg_2;
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(your_variable,'","',-1),'":"',1) INTO arg_3;
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(your_variable,'":"',-1),'"}',1) INTO arg_4;
INSERT INTO trans VALUES (arg_1,arg_2,arg_3,arg_4);
or if you want to do it in store procedure add your data here
DELIMITER $$
DROP PROCEDURE IF EXISTS trans$$
CREATE PROCEDURE trans()
BEGIN
DECLARE arg_1,arg_2,arg_3,arg_4 VARCHAR(100);
DECLARE finished INTEGER DEFAULT 0;
DECLARE cursor_name CURSOR FOR YOUR_SELECT_statement;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
OPEN cursor_name;
my_loop: LOOP
FETCH cursor_name INTO your_variable;
IF finished = 1 THEN
LEAVE my_loop;
END IF;
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(your_variable,'{"',-1),'":"',1)
INTO arg_1 FROM your_table;
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(your_variable,'":"',-2),'","',1)
INTO arg_2 FROM your_table;
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(your_variable,'","',-1),'":"',1)
INTO arg_3 FROM your_table;
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(your_variable,'":"',-1),'"}',1)
INTO arg_4 FROM your_table;
INSERT INTO trans VALUES (arg_1,arg_2,arg_3,arg_4);
END LOOP my_loop;
CLOSE cursor_name;
END$$
DELIMITER ;
for one column you use this
DELIMITER $$
DROP PROCEDURE IF EXISTS trans$$
CREATE PROCEDURE trans(IN in_iIdeaHistoryId INT)
BEGIN
DECLARE arg_1 VARCHAR(100);
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(REPLACE(`tAnswerData`,' ',''),'{"',-1),'":"',1)
INTO arg_1 FROM `idea_history` WHERE iIdeaHistoryId=in_iIdeaHistoryId;
INSERT INTO trans VALUES (arg_1);
END$$
DELIMITER ;
and call it like this
CALL `trans`(53);
This looks like JSON. Use php function json_decode() to store the json data into an array and then you can use foreach loop on that array. Inside the loop you can place condition and when its met, you could use insert query to store it in your database.
Does this help you?
If you can explain a bit further what you're trying to do exactly and what are your condition, then i Can probably give you a better answer.

Show item of the day

I am looking to create a function that gets me a random item from a mySQL table, but let's me keep the returned as the "item of the day". In other words, the item that was "the item of the day" yesterday should not be shown again until all other items have been shown as item of the day.
Any suggestions on how to do this in an elegant way?
Thanks
Add a bool column "UsedAsItemOfTheDay" set to false (0). Update to true when item is picked. Exclude already used items from the picking process.
SELECT * FROM `table`
WHERE UsedAsItemOfTheDay = 0
ORDER BY RAND() LIMIT 1;
(Note: this is not the fastest way to return a random row in MySql; it will be slow on huge tables)
See also: quick selection of a random row from a large table in mysql
SELECT <fields> FROM <table> WHERE <some logic to exclude already used> ORDER BY RAND() LIMIT 1 will get you a random row from the table.
Add a column to store whether the item has been used:
ALTER TABLE your_table ADD COLUMN isused BOOL DEFAULT 0;
Get a random item of the day:
SELECT t.*
FROM your_table t
WHERE t.isused = 0
ORDER BY RAND()
LIMIT 1
Now update that record so it can't be used in the future:
UPDATE your_table
SET isused = 1
WHERE id = id_from_select_random_statement
People who "know" SQL will look for declarative solutions and will shun procedural code. Flagging rows is a "smell" for procedural code.
Is the set of Items static (never changes) or stable (rarely changes)? If yes, it would be easier to do a one-off exercise of generating a lookup table of values from now until the end of time, rather than scheduling a proc to running daily to look for unused flags and update the flag for today and clear all flags if all have been used etc.
Create a table of sequential dates between today and a far future date representing the lifetime of your application (you could consider omitting non-business days, of course). Add a column(s) referencing the key in you Items table (ensure you opt for ON DELETE NO ACTION referential action just in case those Items prove not to be static!) Then randomly assign the whole set of Items one per day until each has been used once. Repeat again for the whole set of Items until the table is full. You could easily generate this data using a spreadsheet and import it (or pure SQL if you are hardcore ;)
Quick example using Standard SQL:
Say there are only five Items in the set:
CREATE TABLE Items
(
item_ID INTEGER NOT NULL UNIQUE
);
INSERT INTO Items (item_ID)
VALUES (1),
(2),
(3),
(4),
(5);
You lookup table would be as simple as this:
CREATE TABLE ItemsOfTheDay
(
cal_date DATE NOT NULL UNIQUE,
item_ID INTEGER NOT NULL
REFERENCES Items (item_ID)
ON DELETE NO ACTION
ON UPDATE CASCADE
);
Starting with today, add the whole set of Items in random order:
INSERT INTO Items (item_ID)
VALUES ('2010-07-13', 2),
('2010-07-14', 4),
('2010-07-15', 5),
('2010-07-16', 1),
('2010-07-17', 3);
Then, starting with the most recent unfilled date, add the whole set of Items in (hopefully a different) random order:
INSERT INTO Items (item_ID)
VALUES ('2010-07-18', 1),
('2010-07-19', 3),
('2010-07-20', 4),
('2010-07-21', 5),
('2010-07-22', 2);
...and again...
INSERT INTO Items (item_ID)
VALUES ('2010-07-23', 2),
('2010-07-24', 3),
('2010-07-25', 5),
('2010-07-26', 1),
('2010-07-27', 4);
..and so on until the table is full.
Then it would then simply be a case of looking up today's date in the lookup table as and when required.
If the set of Items changes then the lookup table would obviously need to be regenerated, so you need to balance out the simplicity of design against the need for manual maintenance.
If you have fixed items you can add column
ALTER TABLE your_table ADD COLUMN item_day INT DEFAULT 0;
then selecting item use
WHERE item_day = DATE_FORMAT('%j')
If you get empty result then you can format new list of day items:
<?php
$qry = " UPDATE your_table SET item_day = 0";
$db->execute($qry);
// You only need 355 item to set as item of the day
for($i = 0; $i < 355; $i++) {
$qry = "UPDATE your_table SET item_day = ".($i+1)." WHERE item_day = 0 ORDER BY RAND() LIMIT 1";
$rs = $db->execute($qry);
// If no items left stop update
if (!$rs) { break; }
}
?>
Here's a stored procedure which selects a random row without using ORDER BY RAND(), and which resets the used flag once all items have been used:
DELIMITER //
DROP PROCEDURE IF EXISTS random_iotd//
CREATE PROCEDURE random_iotd()
BEGIN
# Reset used flag if all the rows have been used.
SELECT COUNT(*) INTO #used FROM iotd WHERE used = 1;
SELECT COUNT(*) INTO #rows FROM iotd;
IF (#used = #rows) THEN
UPDATE iotd SET used = 0;
END IF;
# Select a random number between 1 and the number of unused rows.
SELECT FLOOR(RAND() * (#rows - #used)) INTO #rand;
# Select the id of the row at position #rand.
PREPARE stmt FROM 'SELECT id INTO #id FROM iotd WHERE used = 0 LIMIT ?,1';
EXECUTE stmt USING #rand;
# Select the row where id = #id.
PREPARE stmt FROM 'SELECT id, item FROM iotd WHERE id = ?';
EXECUTE stmt USING #id;
# Update the row where id = #id.
PREPARE stmt FROM 'UPDATE iotd SET used = 1 WHERE id = ?';
EXECUTE stmt USING #id;
DEALLOCATE PREPARE stmt;
END;
//
DELIMITER ;
To use:
CALL random_iotd();
The procedure assumes a table structure like this:
CREATE TABLE `iotd` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`item` varchar(255) NOT NULL,
`used` BOOLEAN NOT NULL DEFAULT 0,
INDEX `used` (`used`),
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
Here's one way to get the result from PHP (to keep things simple, error checking has been removed):
$mysqli = new mysqli('localhost', 'root', 'password', 'database');
$stmt = $mysqli->prepare('CALL random_iotd()');
$stmt->execute();
$stmt->bind_result($id, $item);
$stmt->fetch();
echo "$id, $item\n";
// 4, Item 4
UPADATE
This version should return the same result repeatedly on a given date. I've not really had time to test this, so be sure to do some testing of your own...
DELIMITER //
DROP PROCEDURE IF EXISTS random_iotd//
CREATE PROCEDURE random_iotd()
BEGIN
# Get today's item.
SET #id := NULL;
SELECT id INTO #id FROM iotd WHERE ts = CURRENT_DATE();
IF ISNULL(#id) THEN
# Reset used flag if all the rows have been used.
SELECT COUNT(*) INTO #used FROM iotd WHERE used = 1;
SELECT COUNT(*) INTO #rows FROM iotd;
IF (#used = #rows) THEN
UPDATE iotd SET used = 0;
END IF;
# Select a random number between 1 and the number of unused rows.
SELECT FLOOR(RAND() * (#rows - #used)) INTO #rand;
# Select the id of the row at position #rand.
PREPARE stmt FROM 'SELECT id INTO #id FROM iotd WHERE used = 0 LIMIT ?,1';
EXECUTE stmt USING #rand;
# Update the row where id = #id.
PREPARE stmt FROM 'UPDATE iotd SET used = 1, ts = CURRENT_DATE() WHERE id = ?';
EXECUTE stmt USING #id;
END IF;
# Select the row where id = #id.
PREPARE stmt FROM 'SELECT id, item FROM iotd WHERE id = ?';
EXECUTE stmt USING #id;
DEALLOCATE PREPARE stmt;
END;
//
DELIMITER ;
And the table structure:
CREATE TABLE `iotd` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`item` varchar(255) NOT NULL,
`used` BOOLEAN NOT NULL DEFAULT 0,
`ts` DATE DEFAULT 0,
INDEX `used` (`used`),
INDEX `ts` (`ts`),
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
Why don't you use sequence?
Sequence serves your purpose easily...

Categories