I have a database with 95 tables. A users tables exists for the system users. Many other tables (45 out of the 95) have a "created_by" that refers to the user who created/added the row, through users.id.
Now. If I wanted to delete a user, I just cannot go and do $user->delete(), I need to keep the user around (soft-delete it) in case this user has created rows on other tables. But what if this user didn't add any content, then I should just go ahead and $user->forceDelete() it.
My question is: is there a good way to go about doing this? To check whether a user should be deleted or Force-deleted when we have this big number of tables.
I figured I could just loop though the tables and check if the id of the user (to be deleted) exists, if found then it's ->delete(), else it's ->forceDelete(). Here is the code:
// Get all tables
$allTables = \DB::connection()->getDoctrineSchemaManager()->listTableNames();
$tablesWithCreatedBy = [];
foreach($allTables as $tableName){
$tableColumns = \DB::getSchemaBuilder()->getColumnListing($tableName);
if(in_array('created_by', $tableColumns)){
$tablesWithCreatedBy[] = $tableName;
}
}
foreach($tablesWithCreatedBy as $tableName){
$result = \DB::select(" SELECT created_by FROM `$tableName`
WHERE `created_by` = {$this->user->id} LIMIT 0, 1 ");
if(isset($result[0])){
$this->user->delete();
break;
}
}
// If wasn't trashed from the code above, then force delete the user!
if(!$this->user->trashed()){
$this->user->forceDelete();
}
I feel there must be a better way to do it! Is there?
You've to add records_count to users table that will be incremented every time when user adds content to other tables, so after that change solution will be simple as:
$result = ($this->user->records_count > 0)
? $this->user->delete()
: $this->user->forceDelete();
Or write Laravel Console Command and run it at background that will walk through db and do cleanup operations.
Related
I've the following table layout in my database with some data.
I'm taking input by check-boxes so user can select all applicable accident road conditions and it to database. I would say it is okay if you're adding a new record you just loop through the checkboexs checked and insert them in DB
The first information is now saved in the database, now user decided to change the road conditions for any reasons user came back and change it the to the following.
Now my question, how should I update my table. The first thing that came into my mind was to delete the record that were already there and insert the new one's.
My real issue here is, assume user have choose the 3 items before but changed it two or one then how would i delete the those are not checked you know what I'm saying. Below is some code snippets that I've been trying.
$accidentRoadConditions = AccidentRoadConditions::findAccidentRoadConditions($acc_det_id);
$wc_array = [];
while ($roadConditions = $accidentRoadConditions ->fetch(PDO::FETCH_OBJ)) {
$wc_array[] = $roadConditions ->rc_id;
}
Above I'm selecting all the road conditions that is already stored in the database.
if (isset($_POST['rta_ad_rc'])) {
foreach ($_POST['rta_ad_rc'] as $rc_id) {
//AccidentRoadConditions::save(array(null, $ad_lsid, $rc_id));
// $tmprory = AccidentRoadConditions::findByADAndRCIds($acc_det_id, $rc_id);
// if(!$tmprory){
// AccidentRoadConditions::save(array(null, $acc_det_id, $rc_id));
// }
if(in_array($rc_id, $wc_array)){
$errors[] = "in array <br />";
unset($wc_array[0]);
}
}
}
So my question is how to update values in database according to what was checked by user and deleting those which were unchecked which were checked before. Getting bit complicated so simply how to update database according to above mention scenario.
Any Idea?
I think you need to do the following
Store the selected checks in an array
Check in the database if any of those are already saved or not
if yes, skipped them otherwise add them into an array
$old_rc_array = [];
$new_rc_array = [];
while ($roadConditions = $accidentRoadConditions->fetch(PDO::FETCH_OBJ)) {
$old_rc_array[] = $roadConditions->rc_id;
}
if (isset($_POST['rta_ad_rc'])) {
foreach ($_POST['rta_ad_rc'] as $rc_id) {
if(in_array($rc_id, $old_rc_array)){
unset($old_rc_array[array_search($rc_id, $old_rc_array)]);
}else{
$new_rc_array[] = $rc_id;
}
}
}
foreach ($old_rc_array as $rc_to_delete) {
AccidentRoadConditions::deleteByADIdAndRCId($hidden_acc_det_id, $rc_to_delete);
}
foreach ($new_rc_array as $rc_to_insert) {
AccidentRoadConditions::save(array(null, $hidden_acc_det_id, $rc_to_insert));
}
I think this is what you should do.
Create composite unique constraint on ad_id and rc_id
Delete all the rows not in the selected checkbox ids.
Try to insert all the rows but user INSERT IGNORE. This will insert the record if it does not exist or it will just ignore it. As you are using some framework see how you can do that.
If you can not then just wrap it using try/catch and ignore if the error is related to constraint violation.
This way You don't need to check if the values exist and also there will not be any unnecessary inserts.
Reason: I was assigned to run some script that advances a website,it's a fantasy football site and there are several instants of the site located into different domains. Some has more than 80k users and each users supposed to have a team that consists of 15 players. Hence some tables have No.users x No.players rows.
However Sometimes the script fails and the result gets corrupted, therefore I must backup 10 tables in question before i execute the script. Nevertheless, I still need to backup the tables to keep historical record of users action. Because football matches may last for 50+ game weeks.
Task: To duplicate db tables using php script. When i started i used to backup the tables using sqlyog. it's works but it's time consuming since I have to wait for each table to be duplicated. Besides, for large tables the sqlyog application crashes during the duplicating of large tables which may be very annoying.
Current solution: I have created a simple application with interface that does the job and it works great. It consist of three files, one for db connection, 2nd for db manipulation, 3rd for user interface and to use the 2nd file's code.
The thing is, sometimes it get stuck at the middle of duplicating tables process.
Objective: To create an application to be used by admin to facilitate database backing up using mysql+php.
My Question: How to ensure that the duplicating script will definitely backup the table completely without hanging the server or interrupting the script.
Down here I will include my code for duplicating function, but basically these are the two crucial lines that i think the problem is located in them:
//duplicate tables structure
$query = "CREATE TABLE $this->dbName.`$newTableName` LIKE $this->dbName.`$oldTable`";
//duplicate tables data
$query = "INSERT INTO $this->dbName.`$newTableName` SELECT * FROM $this->dbName.`$oldTable`";
The rest of the code is solely for validation in case error occur. If you wish to take a look at the whole code, be my guest. Here's the function:
private function duplicateTable($oldTable, $newTableName) {
if ($this->isExistingTable($oldTable))
{
$this->printLogger("Original Table is valid -table exists- : $oldTable ");
}
else
{
$this->printrR("Original Table is invalid -table does not exist- : $oldTable ");
return false;
}
if (!$this->isExistingTable($newTableName))// make sure new table does not exist alrady
{
$this->printLogger("Distination Table name is valid -no table with this name- : $newTableName");
$query = "CREATE TABLE $this->dbName.`$newTableName` LIKE $this->dbName.`$oldTable`";
$result = mysql_query($query) or $this->printrR("Error in query. Query:\n $query\n Error: " . mysql_error());
}
else
{
$this->printrR("Distination Table is invalid. -table already exists- $newTableName");
$this->printr("Now checking if tables actually match,: $oldTable => $newTableName \n");
$varifyStatus = $this->varifyDuplicatedTables($oldTable, $newTableName);
if ($varifyStatus >= 0)
{
$this->printrG("Tables match, it seems they were duplicated before $oldTable => $newTableName");
}
else
{
$this->printrR("The duplicate table exists, yet, doesn't match the original! $oldTable => $newTableName");
}
return false;
}
if ($result)
{
$this->printLogger("Query executed 1/2");
}
else
{
$this->printrR("Something went wrong duplicateTable\nQuery: $query\n\n\nMySql_Error: " . mysql_error());
return false;
}
if (!$this->isExistingTable($newTableName))//validate table has been created
{
$this->printrR("Attemp to duplicate table structure failed $newTableName table was not found after creating!");
return false;
}
else
{
$this->printLogger("Table created successfully: $newTableName");
//Now checking table structure
$this->printLogger("Now comparing indexes ... ");
$autoInc = $this->checkAutoInc($oldTable, $newTableName);
if ($autoInc == 1)
{
$this->printLogger("Auto inc seems ok");
}
elseif ($autoInc == 0)
{
$this->printLogger("No inc key for both tables. Continue anyways");
}
elseif ($autoInc == -1)
{
$this->printLogger("No match inc key!");
}
$time = $oldTable == 'team_details' ? 5 : 2;
$msg = $oldTable == 'team_details' ? "This may take a while for team_details. Please wait." : "Please wait.";
$this->printLogger("Sleep for $time ...\n");
sleep($time);
$this->printLogger("Preparing for copying data ...\n");
$query = "INSERT INTO $this->dbName.`$newTableName` SELECT * FROM $this->dbName.`$oldTable`";
$this->printLogger("Processing copyign data query.$msg...\n\n\n");
$result = mysql_query($query) or $this->printrR("Error in query. Query:\n $query\n Error: " . mysql_error());
// ERROR usually happens here if large tables
sleep($time); //to make db process current requeste.
$this->printLogger("Query executed 2/2");
sleep($time); //to make db process current requeste.
if ($result)
{
$this->printLogger("Table created ($newTableName) and data has been copied!");
$this->printLogger("Confirming number of rows ... ");
/////////////////////////////////
// start checking count
$numRows = $this->checkCountRows($oldTable, $newTableName);
if ($numRows)
{
$this->printLogger("Table duplicated successfully ");
return true;
}
else
{
$this->printLogger("Table duplicated, but, please check num rows $newTableName");
return -3;
}
// end of checking count
/////////////////////////////////
}//end of if(!$result) query 2/2
else
{
$this->printrR("Something went wrong duplicate Table\nINSERT INTO $oldTable -> $newTableName\n\n$query\n mysql_error() \n " . mysql_error());
return false;
}
}
}
AS you noticed the function is only to duplicate one table, that's why there is another function that that takes an array of tables from the user and pass the tables names array one by one to duplicateTable().
Any other function should be included for this question, please let me know.
One solution pops into my mind, would duplicating tables by part by part add any improvement, I'm not sure how Insert into works, but maybe if I could insert let's say 25% at a time it may help?
However Sometimes the script fails and the result gets corrupted,
therefore I must backup 10 tables in question before i execute the
script.
Probably you need to use another solution here: transactions. You need to wrap up all queries you are using in failing script into transaction. If transaction fails all data will be the same as in the beginning of the operation. If queries got executed correctly - you are OK.
why are you every time duplicating the table..
CLUSTERS are good option which can make duplicate copies of your table in distributed manner and is much more reliable and secure.
I wrote a function which makes a random id makeid(); Just to ensure the id is unique I have a SQL statement which checks if the id already exists.
$does_id_exist = mysql_query("SELECT COUNT(*) AS count FROM signups WHERE affid='$affid'");
if(mysql_num_rows($does_id_exist) == 1)
{
#loop function and perform query again
}
else
{
#insert record
}
So I'm having trouble with looping the function. How do I loop my function makeid() and perform the $does_id_exist check to ensure that each ID is unique.
--UPDATE-- Just to clarify- My code makes an id like YES#281E But before I INSERT this id into the users record. I just need to verify IF any other user already has this id. IF another user has this id that event must trigger my function to create a new id e.g. WOW!29E3 and again check the sql/query to ensure no other user has that id. Continue to loop if fails or end and INSERT if the id is available.
You can either just use a primary key on your database table, or something like this:
<?php
// the id to insert
$newId = null;
// populate with results from a SELECT `aff_id` FROM `table`
$currentIds = array();
// prepopulate
for( $i=0; $i<100000; $i++ )
{
$currentIds[] = "STRING_" + rand();
}
// generate at least one id
do
{
$newId = "STRING_" + rand();
}
// while the id is taken (cached in $currentIds)
while( in_array($newId, $currentIds) );
// when we get here, we have an id that's not taken.
echo $newId;
?>
Output:
STRING_905649971 (run time 95ms);
I'd definitely not recommend running the query repeatedly. Perhaps a final check before you insert, if your traffic volume is high enough.
Do not do COUNT(*), because you do not need to know how many rows is there (it should be 0 or 1 as you need Id unique), so even DB finds your row it will still be checking for the whole table to count. You really care if you got 1 row, so just select for row with that ID and this sufficient. You should also avoid using rand() - this does not help as you see and you cannot predict how many loops you can do before you find "free slot". use something predictable, like date prefix, or prefix incremented each day. anything that would help you narrow the data set. But for now (pseudocode!):
$id = null;
while( $id == null ) {
$newId = 'prefix' . rand();
mysql_query("SELECT `affid` FROM `signups` WHERE `affid`='${newId}'");
if( mysql_num_rows() == 0) {
$id = newId;
break;
}
}
Ensure you got DB indexed, to speed things up.
EDIT: I do agree that any cache would be useful to speed things up (you can add it easily yourself based on #Josh example), still, I think this is fixing at wrong place. If possible rethink the way you generate your ID. It does not really need to be auto increment, but something more predictable than rand() would help you. If your ID does not need to be easily memorable and it is not any security concern to have them sequential, maybe use numbers with other base than 10 (i.e. using 26 would use all digits + letters so you'd end with PREFIX-AX3TK, so string as you want, and at the same time you would easily be able to quickly generate next Id
if ($email != $CURUSER["email"]) {
if (!validemail($email))
bark( _("Klaida! Panašu, kad klaidingai įvedei email adresą!") );
$r = do_mysql_query("SELECT id FROM users WHERE email=" . sqlesc($email)) or sqlerr();
if (mysql_num_rows($r) > 0)
bark( sprintf( _("Toks emailas adresas %s jau naudojamas!"), $email) );
####
$date = date("Y-m-d");
$modcomment_email = "{$date} - E-mail pakeistas, pakeitė: {$CURUSER['username']}";
$modcomment_email = mysql_real_escape_string($modcomment_email);
do_mysql_query("UPDATE users SET modcomment = '$modcomment_email' where id = ".$CURUSER['id']) or sqlerr(__FILE__, __LINE__);
#####
$changedemail = 1;
}
After this script executed row updated, but old data deleted. How update row, but keep old data ?
You're using an UPDATE command. This does, as you've observed, modify the row(s) that match your WHERE clause.
If you'd like to keep the list of previous $modcomment_emails then you'll need to use an INSERT into some table and then make sure you're selecting the most recent record out when you do your SELECT to output the data. You could add a column to keep the date of the insert as something to ORDER BY.
If you need to keep the old data, you would have to move a copy into an archive table, by issuing the "update" command it overwrites anything that was there. Example
if I have a table where username is "Bugfinder" and now, I change the username to "Bug" there will be no reference to Bugfinder because it was overwritten. If I wanted to keep that, I would need to have copied the line into an archive table maybe a long with the date/time of change, so I could look back historically for changes.
Yes - if you want to insert new row use INSERT if want to update use UPDATE. Also solution is to create history table when you would drop needed old date with the time when change happend
I am trying to implement a check in my PHP code, that checks if there is a duplicate uid in the database, and if so, to assign a new uid, and check again, but I am having trouble nailing the logic, here is what I have thus far,
function check($uid){
$sql = mysql_query("SELECT * FROM table WHERE uid='$uid'");
$pre = mysql_num_rows($sql);
if($pre >= 1){
return false;
}else{
return true;
}
}
And then using that function I thought of using a while loop to continue looping through until it evaluates to true
$pre_check = check($uid);
while($pre_check == false){
//having trouble figuring out what should go here
}
So basically, once I have a usable uid, write everything to the database, else keep generating new ones and checking them till it finds one that is not already in use.
It is probably really simple, but for some reason I am having trouble with it.
Thanx in advance!
$uid = 100; // pick some other value you want to start with or have stored based on the last successful insert.
while($pre_check == false){
$pre_check = check(++$uid);
}
Of course ths is exactly what 'auto incrementing' primary keys are useful for. Are you aware of 'auto incrementing' primary keys in mysql?
EDIT
In light of your comment regarding maintaining someone else's code that uses the random function like that (ewwwww)... I would use the method I suggest above and store the last inserted id somewhere you can read it again for the next user. This will allow you to "fill-in-the-blanks" for the uids that are missing. So, if for example you have uids 1, 2, 5, 9, 40, 100... you can start with $uid = 1; Your while loop will return once you get to 3. Now you store the 3 and create the new record. Next time, you start with $uid = 3; and so on. Eventually you will have all numbers filled in.
It is also important to realize that you will need to do the inserts by either locking the tables for WRITES. You don't want to get into a race condition where two different users are given the same uid because they are both searching for an available uid at the same time.
Indeed the best is to use autoincrement ids, but if you don't have the choice, you can do a reccursive function like that:
function find_uid() {
$new_uid = rand(1000000000, 9999999999);
$sql = mysql_query("SELECT COUNT(*) AS 'nb' WHERE uid=".$new_uid.";");
$row = mysql_fetch_assoc();
$pre = $row['nb'];
return ($pre >= 1 ? find_uid() : $new_uid);
}
COUNT(*) should be more performant because the count is made by MySQL and not php.
By the way, if you need a new uid shouldn't the condition be ($pre > 0) instead of ($pre > 1) ?