I want to select a bunch of rows from a mysql database and update the viewed attribute of those once selected (this is a kind of 'I have read these' flag).
Initially I did something like this:
update (
select a, b, c
from mytable
where viewed = '0'
)
set viewed = '1';
This selects the rows nicely and updates their viewed attribute as required. But it does not return the selected rows from the subquery.
Is there a clause I can add, or perhaps I need to store the subquery, etc...? I did consider a transaction but I ended up with the same problem. I have not tried a stored procedure...
Please can someone advise / point me in the right direction on how to do what I do above but in addition return the selected tables from the subquery?
Thanks in advance.
Update:
As pointed out by #Barmar, #a_horse_with_no_name, #fancyPants and #George Garchagudashvil...
In MySQL you have to use two statements to select and update, and not a nested statement as in my initial post, if you want to return the selected rows.
e.g.
begin;
select a, b, c
from mytable
where viewed = '0';
update mytable
set viewed = '1'
where viewed = '0';
commit;
thanks guys.
I would create a simple function:
DELIMITER $$
DROP FUNCTION IF EXISTS `mydb`.`updateMytable`$$
CREATE
/*[DEFINER = { user | CURRENT_USER }]*/
FUNCTION `mydb`.`updateMytable`() RETURNS TEXT
BEGIN
SET #updated := '';
UPDATE mytable
SET viewed = 1
WHERE viewed = 0
AND (
SELECT #updated := CONCAT_WS(',', #updated, id)
) != ''
;
RETURN TRIM(LEADING ',' FROM #updated);
END$$
DELIMITER ;
which updates tables and returns concatenated ids.
From php you call this:
SELECT mydb.updateMytable()
and you get ids in a stirng: 1,2,7,54,132 etc...
Update:
my function is returning string containing comma separated ids:
'1,5,7,52,...' these ids are only which would have been updated during the function call,
better php-mysql example would be (you may and would use PDO):
$query = "SELECT mydb.updateMytable()";
$res = mysql_query($query);
$arr = mysql_fetch_array($res);
$ids = explode(',', $arr[0]);
// now you can do whatever you want to do with ids
foreach ($ids as $id)
{
echo "Hoorah: updated $id\n";
}
also remember to change mydb and mytable according to your database names
Final
because you need more complex functionality, simply run two query:
First run:
SELECT a, b, c
FROM mytable
WHERE viewed = 0
Next run:
UPDATE mytable
SET viewed = 1
WHERE viewed = 0
Related
I have a table like this
tbl_user
id
user_id
amount
first i want to update a row based on id
$amount = 123; // dyanamic value
$sql = "UPDATE tbl_user SET amount=amount-'$amount' WHERE id='$id' LIMIT 1 ";
now i want to get updated value of amount column i have applied this sql
$sql = "SELECT amount FROM tbl_user WHERE id='$id' LIMIT 1 ";
my question is can i combine both of above sql or any single query to achieve above task?
The best you could imitate is to use two lines of queries, probably using a variable like:
UPDATE tbl_user SET
amount = #amount := amount-'$amount'
WHERE id='$id' LIMIT 1;
SELECT #amount;
The best you could do then is to create a Stored Procedure like:
DELIMITER //
CREATE PROCEDURE `return_amount` ()
BEGIN
UPDATE tbl_user SET
amount = #amount := amount-'$amount'
WHERE id='$id' LIMIT 1;
SELECT #amount;
END //
And then call Stored Procedure in your PHP.
Note: PostgreSQL has this kind of option using RETURNING statement that would look like this:
UPDATE tbl_user SET amount=amount-'$amount'
WHERE id='$id' LIMIT 1
RETURNING amount
See here
A function can do this easily. It sounds like you want to limit how many times your code connects to the database. With a stored function or procedure, you are only making one connection. Yes, the stored function has two queries inside it (update then select), but these are executed on the server side without stopping to do round trips to the client.
http://sqlfiddle.com/#!2/0e6a09/1/0
Here's my skeleton of your table:
CREATE TABLE tbl_user (
id VARCHAR(100) PRIMARY KEY,
user_id VARCHAR(100),
amount DECIMAL(17,4) );
INSERT INTO tbl_user VALUES ('1', 'John', '100.00');
And the proposed function:
CREATE FUNCTION incrementAmount
(p_id VARCHAR(100), p_amount DECIMAL(17,4))
RETURNS DECIMAL(17,4)
BEGIN
UPDATE tbl_user
SET amount = amount + p_amount
WHERE id = p_id;
RETURN (SELECT amount FROM tbl_user WHERE id = p_id);
END
//
Then you just run one query, a SELECT on the function you just created:
SELECT incrementAmount('1', 5.00)
The query result is:
105
It is not possible with a single query, but you can combine multiple commands into a script and execute them with a single request to the database server.
Run this script:
"UPDATE tbl_user SET amount=amount-'$amount' WHERE id='".$id."';SELECT amount FROM tbl_user WHERE id='".$id."'; "
Also, you might want to check whether $id is a number, as I do not see a protection against SQL injection inside your code. SQL injection is a serious threat, you would do better to prepare and protect yourself against it.
We can also use:
UPDATE tbl_user SET id = LAST_INSERT_ID(id), amount = 2.4,user_id=4 WHERE id = 123;
// SELECT
$id =SELECT LAST_INSERT_ID();
SELECT amount,user_id FROM tbl_user WHERE id = $id LIMIT 1
Here would be the procedure
CREATE PROCEDURE UpdateAndSelect
(
#amount MONEY,
#id INT
)
AS
BEGIN
UPDATE tbl_user
SET amount = #amount
WHERE id = #id
LIMIT 1
SELECT amount
FROM tbl_user
WHERE id = #id
LIMIT 1
END
GO
You would call this stored procedure by setting your variables (#amoutn and #id) and then calling:
exec UpdateAndSelect
Hope this helps solve your problem
For the life of me I cannot work out why this MySQL code doesn't work.
The aim is to check the credentials and if they exist, return the ID and if they don't then insert them and return the ID;
SET #Found = 0;
SELECT Id INTO #Found FROM Users WHERE Users.Username = "testAccount" AND Users.Email = "test#email.com";
IF #Found > 0 THEN
SELECT #Found;
ELSE
INSERT INTO Users VALUES (DEFAULT, "testAccount", "test#email.com");
SELECT LAST_INSERT_ID();
END IF;
Thank you for the help.
The MySQL if statement only works in stored programs -- stored procedures, functions, and triggers.
That is what the first line of the documentation means:
The IF statement for stored programs implements a basic conditional construct.
So, your statement is syntactically invalid.
In addition, you have a syntax error in your select:
SELECT Id INFO #Found
FROM Users
WHERE Users.Username = "testAccount" AND Users.Email = "test#email.com";
What is being selected? Should you have commas? Or do you mean:
SELECT #Found := id
. . .
I have this trigger. If the incoming log agrees with input filter, than is not saved into database. But, I want to keep number of "hits" of each Primitive_filter. I have a column named hit_rate, which is int(30). Is there some way how to do that? Maybe specific error? Or sth else? Thx for help.
UPDATE Primitive_filters SET hit_rate = hit_rate + 1 where Primitive_filters.id = ???;
trigger
delimiter //
CREATE TRIGGER inputFilter
before insert
on Logs
for each row
begin
declare msg varchar(255);
IF (SELECT COUNT(*) FROM Primitive_filters, Primitive_in_filter, Filters WHERE
Filters.name = "input" AND Filters.id = Primitive_in_filter.id_filter AND Primitive_in_filter.id_primitive = Primitive_filters.id AND
(Primitive_filters.id_host LIKE CONCAT('%',(SELECT host FROM Hosts WHERE id = new.id_host),'%') OR Primitive_filters.id_host IS NULL) AND
(Primitive_filters.facility LIKE CONCAT('%',new.facility,'%') OR Primitive_filters.facility IS NULL) AND
(Primitive_filters.priority LIKE CONCAT('%',new.priority,'%') OR Primitive_filters.priority IS NULL) AND
(Primitive_filters.program LIKE CONCAT('%',new.program,'%') OR Primitive_filters.program IS NULL) AND
(new.msg REGEXP Primitive_filters.msg OR Primitive_filters.msg IS NULL)) > 0 THEN CALL raise_error; END IF;
END //
delimiter ;
This is NOT the answer to your question.
It's only a hint how to fix a potentially serious performance problem in your code.
Don't use this:
IF (SELECT COUNT(*) FROM ... giant query ...) > 0
THEN CALL raise_error;
END IF;
Use this instead:
IF EXISTS (SELECT 1 FROM ... giant query ...)
THEN CALL raise_error;
END IF;
The former condition calculates a count ... it must read all rows returned by the query
If the query returns billion rows, it must reads them all --> because you asked give me a count of rows.
Then, after the query return the count, there is a check: if the query returns at least one row, then do something.
The latter condition stops executing the query when the query returns first row, saving time and resources.
I have a field named 'dealBusinessLocations' (in a table 'dp_deals') which contain some ids of another table(dp_business_locations) in comma separated format.
dealBusinessLocations
----------------------
0,20,21,22,23,24,25,26
I need to use this values within an in() function of a query.
like
select * from dp_deals as d left join dp_business_locations as b on(b.businessLocID IN (d.dealBusinessLocations) ;
Sine mysql doesn't support any string explode function, I have created a stored function
delimiter //
DROP FUNCTION IF EXISTS BusinessList;
create function BusinessList(BusinessIds text) returns text deterministic
BEGIN
declare i int default 0;
declare TmpBid text;
declare result text default '';
set TmpBid = BusinessIds;
WHILE LENGTH(TmpBid) > 0 DO
SET i = LOCATE(',', TmpBid);
IF (i = 0)
THEN SET i = LENGTH(TmpBid) + 1;
END IF;
set result = CONCAT(result,CONCAT('\'',SUBSTRING(TmpBid, 1, i - 1),'\'\,'));
SET TmpBid = SUBSTRING(TmpBid, i + 1, LENGTH(TmpBid));
END WHILE;
IF(LENGTH(result) > 0)
THEN SET result = SUBSTRING(result,1,LENGTH(result)-1);
END IF;
return result;
END//
delimiter ;
The function is working perfectly.
mysql> BusinessList( '21,22' )
BusinessList( '21,22' )
-----------------------
'21','22'
But the query using the function does not worked either. here is the query.
select * from dp_deals as d left join dp_business_locations as b on(b.businessLocID IN (BusinessList(d.dealBusinessLocations)));
I have also tried using static value for function argumet, But no use
select * from dp_deals as d left join dp_business_locations as b on(b.businessLocID IN (BusinessList('21,22')));
It seems that there is some problem with using value returned from the function.
First, read this:
Is storing a comma separated list in a database column really that bad?
Yes, it is
Then, go and normalize your tables.
Now, if you really can't do otherwise, or until you normalize, use the FIND_IN_SET() function:
select *
from dp_deals as d
left join dp_business_locations as b
on FIND_IN_SET(b.businessLocID, d.dealBusinessLocations)
Then, read that article again. If the query is slow or if you have other problems with this table, then you'll know why:
Is storing a comma separated list in a database column really that bad?
Yes, it is
Simple, use find_in_set() instead.
SELECT *
FROM dp_deals as d
LEFT JOIN dp_business_locations as b
ON (FIND_IN_SET(b.businessLocID,d.dealBusinessLocations) > 0);
See: http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_find-in-set
Note that if you drop CSV and get out off hell you can use a simple join like:
SELECT d.*, GROUP_CONCAT(b.dealBusinessLocation) as locations
FROM dp_deals as d
LEFT JOIN dp_business_location as b
ON (d.dealBusinessLocation = b.businessLocID);
Which will be much much faster and normalized as a bonus.
I think your problem is that IN() doesn't expect to get one string with lots of fields in it, but lots of fields.
With your function you are sending it this:
WHERE something IN ('\'21\',\'22\''); /* i.e. a single text containing "'21','22'" */
And not the expected
WHERE something IN ('21','22');
I do have a table with more than 100000 data elements, but there are almost 350 blank rows within. How do I delete this blank rows using phpmyadmin? Manually deleting is a tedious task.
The general answer is:
DELETE FROM table_name WHERE some_column = '';
or
DELETE FROM table_name WHERE some_column IS NULL;
See: http://dev.mysql.com/doc/refman/5.0/en/delete.html
More info when you post your tables!~
Also, be sure to do:
SELECT * FROM table_name WHERE some_column = '';
before you delete, so you can see which rows you are deleting! I think in phpMyAdmin you can even just do the select and then "select all" and delete, but I'm not sure. This would be pretty fast, and very safe.
I am doing the mysql operation in command prompt in windows. And the basic queries:
delete * from table_name where column=''
and
delete * from table_name where column='NULL'
doesn't work. I don't know whether it works in phpmyadmin sqlcommand builder. Anyway:
delete * from table_name where column is NULL
works fine.
I have a PHP script that automatically removes empty rows based on column data types.
That allows me to define "emptiness" differently for different column types.
e.g.
table
first_name (varchar) | last_name (varchar) | some_qty ( int ) | other_qty (decimal)
DELETE FROM `table` WHERE
(`first_name` IS NULL OR `first_name` = '')
AND
(`last_name` IS NULL OR `last_name` = '')
AND
(`some_qty` IS NULL OR `some_qty` = 0)
AND
(`other_qty` IS NULL OR `other_qty` = 0)
Since "0" values are meaningless in my system, I count them as empty. But I found out that if you do (first_name = 0) then you will always get true, because strings always == 0 in MySQL. So I tailor the definition of "empty" to the data type.
This procedure will delete any row for all columns that are null ignoring the primary column that may be set as an ID. I hope it helps you.
DELIMITER //
CREATE PROCEDURE DeleteRowsAllColNull(IN tbl VARCHAR(64))
BEGIN
SET #tbl = tbl;
SET SESSION group_concat_max_len = 1000000;
SELECT CONCAT('DELETE FROM `',#tbl,'` WHERE ',(REPLACE(group_concat(concat('`',COLUMN_NAME, '` is NULL')),',',' AND ')),';') FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = #tbl AND COLUMN_KEY NOT LIKE 'PRI' into #delete_all;
PREPARE delete_all FROM #delete_all;
EXECUTE delete_all;
DEALLOCATE PREPARE delete_all;
END //
DELIMITER ;
Execute the procedure like this.
CALL DeleteRowsAllColNull('your table');
I know this has already been answered and has got a tick, but I wrote a small function for doing this, and thought it might be useful to other people.
I call my function with an array so that I can use the same function for different tables.
$tableArray=array("Address", "Email", "Phone"); //This is the column names
$this->deleteBlankLines("tableName",$tableArray);
and here is the function which takes the array and builds the delete string
private function deleteBlankLines($tablename,$columnArray){
$Where="";
foreach($columnArray as $line):
$Where.="(`".$line."`=''||`".$line."` IS NULL) && ";
endforeach;
$Where = rtrim($Where, '&& ');
$query="DELETE FROM `{$tablename}` WHERE ".$Where;
$stmt = $this->db->prepare($query);
$stmt->execute();
}
You can use this function for multiple tables. You just need to send in a different table name and array and it will work.
My function will check for a whole row of empty columns or NULL columns at the same time. If you don't need it to check for NULL then you can remove that part.