Attempting to delete data from table1 into table2 - php

I have a table of records, table 1.
I want to do 2 things, delete all records in table 1 from a specific user and add those deleted records to table2, as a back up.
I'm using mysqli prepared statements.
I also want to do it with one statement and not multiple statements, if possible.
My research led me to this
$stmt2 = $c1->prepare("DELETE FROM `scroll` OUTPUT.* INTO `deletedscroll` WHERE user= ? ");
But I keep getting syntax errors. This code may not be for mysqli is my guess, but the mysqli documentation doesn't go into this.
Does anyone know the correct syntax? I also see stuff on using:
DELETE FROM [source]
OUTPUT [deleted].<column_list>
INTO [destination] (<column_list>)
But I can not get that to work at all.

I have gotten this to work, which uses 2 calls, but it gets the job done without errors.
$stmt2 = $c1->prepare("INSERT INTO `deletedscroll` (user, text, flag, date ) SELECT user, text, flag, date FROM `scroll` WHERE user= ? ");
$stmt2->bind_param('s', $user);
$stmt2->execute();
$stmt2->close();
//move deleted users records from scrolls to deleted table
$stmt3 = $c1->prepare("DELETE FROM `scroll` WHERE user= ? ");
$stmt3->bind_param('s', $user);
$stmt3->execute();
$stmt3->close();

Related

How to increment a field in mysqli

I want to insert many lines, but in one column I would like it to increment. In my example I am setting a variable to 5000 and I would like it to increment by one, every time a line is added to the database.
I will say now this has nothing to do with auto increment. There is a reason I need an incrementing field.
Here is my insert query
$incrementField=5000;
$query="INSERT INTO `table` (`incrementField`,`this`,`morestuff`)
SELECT ?,`this`,`morestuff` FROM `someTable`";
$stmt = $db->prepare($query);
$stmt->bind_param('i',$incrementField);
$stmt->execute();
$stmt->close();
Where the ? is I would like it to increment.
I have tried ?+1 ?.+1 and all kinds of permutations, but nothing seems to work.
You definitely should use SQL AUTO_INCREMENT logic. I guess that columns supposed to be a unique identifier which is also known as PRIMARY KEY.
I now have an answer to this that works properly
$incrementField=5000;
$query="INSERT INTO `table` (`incrementField`,`this`,`morestuff`)
SELECT #position := ifnull(#position, ?) + 1),`this`,`morestuff` FROM `someTable`";
$stmt = $db->prepare($query);
$stmt->bind_param('i',$incrementField);
$stmt->execute();
$stmt->close();
So it can be done after all.
I think the more people that tell me it can't be done only encourages me to find a solution even more. So to all those who said it can't be done, it can.

Cleanest way to check if a record exists

I am using the following code to check if a row exists in my database:
$sql = "SELECT COUNT(1) FROM myTable WHERE user_id = :id_var";
$stmt = $conn->prepare($sql);
$stmt->bindParam(':id_var', $id_var);
$stmt->execute();
if ($stmt->fetch()[0]>0)
{
//... many lines of code
}
All of the code works and the doubts I have are concerning if the previous code is clean and efficient or if there is room for improvement.
Currently there are two questions bugging me with my previous code:
Should I have a LIMIT 1 at the end of my SQL statement? Does COUNT(1) already limit the amount of rows found by 1 or does the server keep searching for more records even after finding the first one?
The if ($stmt->fetch()[0]>0). Would this be the cleanest way to fetch the information from the SQL Query and execute the "if conditional"?
Of course if anyone spots anything else that can improve my code, I would love your feedback.
Q: Should I have a LIMIT 1 at the end of my SQL statement? Does COUNT(1) already limit the amount of rows found by 1 or does the server keep searching for more records even after finding the first one?
Your SELECT COUNT() FROM query will return one row, if the execution is successful, because there is no GROUP BY clause. There's no need to add a LIMIT 1 clause, it wouldn't have any affect.
The database will search for all rows that satisfy the conditions in the WHERE clause. If the user_id column is UNIQUE, and there is an index with that as the leading column, or, if that column is the PRIMARY KEY of the table... then the search for all matching rows will be efficient, using the index. If there isn't an index, then MySQL will need to search all the rows in the table.
It's the index that buys you good performance. You could write the query differently, to get a usable result. But what you have is fine.
Q: Is this the cleanest...
if ($stmt->fetch()[0]>0)
My personal preference would be to avoid that construct, and break that up into two or more statements. The normal pattern...separate statement to fetch the row, and then do a test.
Personally, I would tend to avoid the COUNT() and just get a row, and test whether there was a row to fetch...
$sql = "SELECT 1 AS `row_exists` FROM myTable WHERE user_id = :id_var";
$stmt = $conn->prepare($sql);
$stmt->bindParam(':id_var', $id_var);
$stmt->execute();
if($stmt->fetch()) {
// row found
} else {
// row not found
}
$stmt->closeCursor();

mysql query in php with more conditions from two tables

First of all, I am absolute beginner with PHP and SQL.
I have two tables: users(userID, fullname, username, email, pass, userlevel) and games(gameID, userID, club, result, created_time).
In table games I have userID, same as in table users, but it's not foreign key. When I do this query in MySQL it works fine:
DELETE FROM games
WHERE EXISTS
(SELECT * FROM users
WHERE userlevel=2
AND users.userID=games.userID)
It removes anything that users.userID matches with games.userID and if that user is userlevel 2.
I need this in PHP, but only difference would be that userID will match user's ID that is logged,and user will be able to delete only the data that is input with its userID.
Also, how to allow everyone else, with userlevel 1 to be able to delete everything in table games no matter who entered on the same submit?
I have this, but its not working....it keeps givin' me the same error:
Fatal error: Call to a member function bind_param() on a non-object in
C(...)
require ('db_con.php');
session_start();
$userID=$_SESSION["UserID"];
if (isset($_POST['delete'])){
$stmt=$con->prepare("DELETE FROM games WHERE EXISTS (SELECT FROM users AS u WHERE u.userID = ? AND u.userlevel = 2 "));
$stmt->bind_param("s",$_POST['userID']));
$stmt->execute();
}
Even if I put $userlevel=2 and replace in query u.userlevel = '$userlevel', it gives the same error...
Any suggestions would be greatly appreciated.
thanx!
userID is an integer value. Change bind_param line to
$stmt->bind_param('i', $user_id);
One reason prepare() can fail is -
if the sql statement sent to it is not valid in the current DB.
prepare() will then return false.
Eg - if the table name is not correct or one or more field in the query does not exist.
You got a bracket mismatch:
$stmt=$con->prepare("DELETE FROM games WHERE EXISTS (SELECT FROM users AS u WHERE u.userID = ? AND u.userlevel = 2 "));
should really be
$stmt=$con->prepare("DELETE FROM games WHERE EXISTS (SELECT * FROM users AS u WHERE u.userID = ? AND u.userlevel = 2 )");
Anyway, the error you received means that $con->prepare did not return an object. Most likely it returned false. I assume you are using PDO, so according to the docs:
If the database server cannot successfully prepare the statement, PDO::prepare() returns FALSE or emits PDOException (depending on error handling).
So that's the case here. Your MySQL-server was unable to bind the statement. In order to debug this better (e.g. see more helpful error messages) set PDO to throw exceptions on errors.
You could probably do it like this:
$con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Just add that line right after you connect to the database.
i found the error, here is the wright code:
$stmt=$con->prepare("DELETE FROM games WHERE EXISTS (SELECT * FROM users WHERE users.userID = ? AND users.userlevel = 2 )");
but its not working what i wanted, it deleted all from my games table :D
i guess, SELECT * FROM has to be specific!

SELECT and lock a row and then UPDATE

I have a script that selects a row from MySQL database.
Then updates this row. Like this:
$statement = $db->prepare("SELECT id, link from persons WHERE processing = 0");
$statement->execute();
$row = $statement->fetch();
$statement = $db->prepare("UPDATE persons SET processing = 1 WHERE id = :id");
$success = $statement->execute(array(':id' => $row['id']));
The script calls this php code multiple times simultaneously. And sometimes it SELECTS the row eventhough it should be "processing = 1" because the other script call it at the exact time.
How can I avoid this?
What you need to do is add some kind of lock here to prevent race conditions like the one you've created:
UPDATE persons SET processing=1 WHERE id=:id AND processing=0
That will avoid double-locking it.
To improve this even more, create a lock column you can use for claiming:
UPDATE persons
SET processing=:processing_uuid
WHERE processing IS NULL
LIMIT 1
This requires a VARCHAR, indexed processing column used for claiming that has a default of NULL. If you get a row modified in the results, you've claimed a record and can go and work with it by using:
SELECT * FROM persons WHERE processing=:processing_uuid
Each time you try and claim, generate a new claim UUID key.
Try using transactions for your queries. Read about them at the mysql dev site
You can wrap your code with:
$dbh->beginTransaction();
// ... your transactions here
$dbh->commit();
You'll find the documentation here.
Use SELECT FOR UPDATE
http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html
eg
SELECT counter_field FROM child_codes FOR UPDATE;
UPDATE child_codes SET counter_field = counter_field + 1;
Wrap this in a transaction and the locks will be released when the transaction ends.

repatation of inserting in mysql database

Hello all,
I am working on a site were a member can send message to all of his/her friends ..
so all of their friends are stored in an array like:
$selectfrnds=mysql_query("select sender_id,receiver_id from fk_friends where (sender_id='$id' or receiver_id='$id') and friendtofriend='freq' ");
$friendis=array();
I have imploded the query into a variable and now all their friends are represented:
$frndsall='4','8','2','12','13','14','15','16','18','19','21','23','24','27','35','36','40','43','29','45','44','38','46','22','1'
so I just want to ask: How do I insert query that will send a message to all these ids?
I want something like:
insert into tablename (message,id// id of friend ) values ('$message//this is same','$xyz// which is stored into varialbe $frndsall one by one it should insert with all the values')
any help please...
Use Prepared Statements in a foreach loop:
$stmt = $pdo->prepare(
"INSERT INTO tablename (message, id) VALUES (:message, :id)");
$stmt->bindParam(':message', $message);
$stmt->bindParam(':id', $id);
foreach ($frndsall as $frndId) {
$id = $frndId;
$stmt->execute();
}
check out php foreach statements (http://www.php.net/manual/en/control-structures.foreach.php) - that should be what you're looking for.
It's possible to do bulk insert by first preparing a select statement that produces the desired result set to be inserted, then prefixing INSERT into .
Example
INSERT INTO tablename (message, id)
SELECT '$message', receiver_id
FROM fk_friends
WHERE (sender_id='$id' or receiver_id='$id') AND friendtofriend='freq'
If your database abstraction layer supports prepared statements, something like this is probably the best way to do this query.
$db->query($db->prepare('
INSERT INTO tablename (message, id)
SELECT ?, receiver_id
FROM fk_friends
WHERE (sender_id=? or receiver_id=?) AND friendtofriend=?
'), array($message, $id, $id, 'freq')));
Otherwise, if not using prepared statements, be sure to properly escape all data you insert into the query before querying it from the database, using the relevant escaping function for your method of accessing the database.
Hope that helps.
You can do Insert in foreach, but this solution will bad on real project, where user will have more than 3k friends.
Here is a question of database architecture in my mind.
You say:
member can send message to all of his/her friends
If you want to add possibility to send a message only to all, will be better to use separate table like
`mass_messages`
id (int 11)
sender_id (int 11)
message (text)
send_date (timestamp)
Also you can create another table to store friends who already read your message.
But this way not useful if you try to send message to all except for a few friends.

Categories