Update selected check box data and remove unchecked data - php

Assume I have users database and base_u_group default will be 0 which are not under any groups.
1)base_users
|base_u_id|base_u_username|base_u_group|
------------------------------------------
| 1 | username 1 | 0 |
| 2 | username 2 | 2, 3, 4 |
| 3 | username 3 | 4 |
| 4 | username 4 | 3,5 |
List down all the users. $checkBox will be automatically checked when belong to that edit group.
echo "<tr>";
echo "<td>". $count .". ".$row_User['base_u_username']. "</td>";
echo "<td align=\"center\"><input type=\"checkbox\" class = \"group\" name=\"userList[]\" value=".$row_User['base_u_id']." ".$checkBox."/></td>";
echo "</tr>";
My problem is how do I insert selected check boxes data without duplication for example: 3, 3, 4, 5 --> 3, 4, 5
and when the check boxes are unchecked, it will delete that group in my base_u_group , for example unchecked the check box for username 2 of group 3
2, 3, 4 --> 2, 4
$gid indicates the selected edit group.
This is what I did so far:
<?php
$userGroup = $_POST['userList'];
foreach($userGroup as $a)
{
$selSQL = base_executeSQL("SELECT * FROM base_users WHERE base_u_id='".$a."'");
while($row_SQL = base_fetch_array($selSQL))
if($row_SQL['base_u_group'] != "0")
{
$data = explode(", ",$row_SQL['base_u_group']);
for($i=0; $i<count($data);$i++)
{
//insert to user group if base_u_group does not find the group ID
if($gid <> $data[$i])
base_executeSQL("UPDATE base_users SET base_u_group='".$gid. ", ". $row_SQL['base_u_group']."' WHERE base_u_id='".$a."'");
}
}
//if the user does not belong to any groups: base_u_group = 0
else
base_executeSQL("UPDATE base_users SET base_u_group='".$gid."' WHERE base_u_id='".$a."'");
}
?>
EDIT: deletion of groups
$data = explode(", ",$row_SQL['base_u_group']);
$ok = true;
for($i=0; $i<count($data);$i++)
{
//insert to user group if base_u_group does not find the group ID
if($gid == $data[$i])
{
$arr = array_merge(array_diff($data,array($gid)));
$newArray = implode(", ",$arr);
base_executeSQL("UPDATE base_users SET base_u_group='".$newArray."' WHERE base_u_id!='".$row_SQL['base_u_id']."' AND base_u_domain='local'");
$ok = false;
}
}
if (ok) base_executeSQL("UPDATE base_users SET base_u_group='".$gid. ", ". $row_SQL['base_u_group']."' WHERE base_u_id='".$a."'");

If you want to stick with your current database design, then your approach is actually the easiest you can do. Moving this logic to MySQL would be very hard, and would have no advantages over your solution. Seems like your approach already handles all the requirements you posted for the adding a group to the user, and deleting the group is pretty much the same logic.
However, I would highly suggest changing your database design. Remove base_u_group column completely, and create a new table user_groups with two columns user_id and group_id. Make a unique key consisting of both of them. Now to add a group, just INSERT into that table, and the unique key will prevent you from inserting the same group twice. Deleting from a group is now also as trivial as deleting from that table. To get all the groups for the user, just execute SELECT group_id FROM user_groups WHERE user_id=$user_id (well, with proper escaping, or via a prepared statement).
You might also make user_id be a foreign key to your current table, so that it disallows inserting invalid user_ids. Also, if you have a table for groups, adding a foreign key from user_groups to that table would help avoid invalid group ids as well.
EDIT: Apparently I misunderstood part of your question. I thought you already have a working solution, and were asking for a better one. I can immediately see an issue in your solution, that causes duplicates, it is easy to fix, just make the following changes to your code:
$data = explode(", ",$row_SQL['base_u_group']);
$ok = true;
for($i=0; $i<count($data);$i++)
{
//insert to user group if base_u_group does not find the group ID
if($gid == $data[$i]) $ok = false;
}
if (ok) base_executeSQL("UPDATE base_users SET base_u_group='".$gid. ", ". $row_SQL['base_u_group']."' WHERE base_u_id='".$a."'");
To see why your code is wrong, think what happens if $data = {1, 2} and $gid is 2 (in which case you obviously don't want to add it). You iterate over every element of $data, so on the first iteration your $data[i] is 1. Since 1 != 2, you update your table and add another 2 at the end
With my changes, I first iterate over all elements of data, and make sure none of them is equal to $gid, and only if that's the case I run a query once to append $gid at the end.
Unfortunately, from your code it is not clear what kind of request you issue for delete, but the logic will be similar. You would go over every element of data, and if any of them is equal to what you want to delete, then just just remove it from data and break from the loop. Then implode your data and store it into the database with an UPDATE query.

Related

Find a string in MySQL table through a loop in multiple arrays

I am using WordPress, and I need to create a function to check for typos in a custom table, by comparing the single values with a comparison table. The values to be checked are animal species names, and they stored as follows in table A
id | qualifying_species
----------------------
1 | Dugong dugon, Delphinus delphis
2 | Balaneoptera physalus, Tursiops truncatus, Stenella coeruleoalba
etc.
These values must be checked for typos by matching them with table B which contains a simple list of species name as a reference
id | species_name
----------------------
1 | Dugong dugon
2 | Delphinus delphis
3 | Balaneoptera physalus
4 | Tursiops truncatus
5 | Stenella coeruleoalba
Here's the code I prepared
function test(){
global $wpdb;
$query_species = $wpdb->get_results("SELECT qualifying_species FROM A", ARRAY_A);
foreach($query_species as $row_species)
{
$string = implode (";", $row_species);
$qualifying_species = explode(";", $string);
//echo '<pre>';
//print_r($qualifying_species);
//echo '</pre>';
foreach ($qualifying_species as $key => $value) {
//I get here the single species name
echo $value . '<br>';
//I compare the single name with the species list table
$wpdb->get_results("SELECT COUNT(species_name) as num_rows FROM B WHERE species_name = '$value'");
//if the species is written correctly, it will be counted as 1 with num_rows
//if the species is written wrongly, no match will be found and num_rows = 0
echo $wpdb->num_rows . '<br>';
}
}
}
The echo was to check the results of the function. The Mysql query works when I do it on PHPMyAdmin, but it seems that something is wrong with the PHP loop that I wrote. For each $value echoed I have a result of 1 echoed with $wpdb->num_rows even if $value presents typos or doesn't exist in table B
What am I doing wrong?
Possible solutoin for MySQL 5.7.
Create a procedure (must be performed only once):
CREATE PROCEDURE check_table_data ()
BEGIN
DECLARE sp_name VARCHAR(255);
DECLARE done INT DEFAULT FALSE;
DECLARE cur CURSOR FOR SELECT species_name FROM tableB;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done := TRUE;
OPEN cur;
CREATE TEMPORARY TABLE t_tableA SELECT * FROM tableA;
FETCH cur INTO sp_name;
REPEAT
UPDATE t_tableA SET qualifying_species = REPLACE(qualifying_species, sp_name, '');
FETCH cur INTO sp_name;
UNTIL done END REPEAT;
CLOSE cur;
SELECT id, qualifying_species wrong_species FROM t_tableA WHERE REPLACE(qualifying_species, ',', '') != '';
DROP TABLE t_tableA;
END
Now, when you need to check your data for unknown species and misprintings you simply execute one tiny query
CALL check_table_data;
which will return id for a row which have a species value not found in tableB, and this species itself.
fiddle
The code assumes that there is no species_name value which is a substring of another species_name value.
The procedure do the next: it makes the data copy then removes existent species from the values. If some species is wrong (is absent, contains misprint) it won't be removed. After all species processed the procedure selects all rows which are not empty (contain non-removed species).
You maybe could do this in the same query :
SELECT *
FROM table_a
INNER JOIN table_b ON FIND_IN_SET(species_name,replace(qualifying_species,';',','))
if you want to find non-existent values, use something like this:
SELECT *
FROM table_b
LEFT OUTER JOIN table_a ON FIND_IN_SET(species_name,replace(qualifying_species,';',','))
WHERE table_a.id IS null

How to Perform WHERE NOT IN for Database column containing multiple values in SQL

How can i perform a WHERE NOT IN Condition when the database column as well as the check condition have multiple values in it.
I want to set the status of user to 1, if he doesn't belong to the excludedUserGroups
User Status Table :- user_status
+---------+--------+
| user_id | status |
+=========+========+
| 1 | 1 |
+---------+--------+
User Table :- user
+---------+---------------+---------------------+
| user_id | user_group_id | secondary_group_ids |
+=========+===============+=====================+
| 1 | 4 | 2,8,9 |
+---------+---------------+---------------------+
Variables:-
$excludeUsergroups = "8,7,12"
$finalExcludeUsergroups = implode(",", $excludeUsergroups);
Query i want to perform is :-
Update user_status us
LEFT JOIN user u ON us.user = u.user
SET us.status = 1
WHERE (u.user_group_id NOT IN (". $finalExcludeUsergroups . ") AND u.secondary_group_ids NOT IN (" . $finalExcludeUsergroups . "))
Now the issue is that, both u.secondary_group_ids and $finalExcludedUsergroups have multiple values, its giving error on checks for that second NOT IN.
It performs checks for user_group_id as it contains only 1 value, but when it goes to secondary_groups_ids it fails
How can i perform the second NOT IN where it checks for each value of secondary_group_ids and checks it with $finalExcludedUsergroups
Update
I tested with FIND_IN_SET , however it doesn't work, as FIND_IN_SET checks for a string from the list of strings FIND_IN_SET(string, string_list)
But in my case i want to check a list of strings from the list of strings, which isn't possible with FIND_IN_LIST.
I'd use MySql NOT REGEXP Documentation and the regular expression /(^|\,)3($|\,)/
$sql = "UPDATE user_status us
LEFT JOIN user u ON us.user = u.user
SET us.status = 1
WHERE (u.user_group_id NOT REGEXP '(^|\,):id($|,)'
AND u.secondary_group_ids NOT REGEXP '(^|\,):id($|,)')";
set your :id parameter to $finalExcludeUsergroups,
I would show example of how but don't know if you're using mysqli, pdo, a framework, etc
Break down of the regular expression:
the group (^|\,) will match the start of the string or a comma
3 will match the id you're looking for (for the code I'd use a parameter here)
the group ($|\,) will match the end of a string or a comma
I was intrigued by your issue and wondered if a simple User Defined Function might help, so prepared the example below. If you create the function in your DB, you should be able to pass your two "comma separated value" strings to it inside your query to see if any groups are shared. As you'll notice, I've named it for the negative case (i.e. Disjoint) as I couldn't quickly think of a suitable short name to express the positive case (i.e. Sets_have_one_or_more_elements_in_common seems a bit long!).
Hopefully someone may find this useful at some point!
/*
MySQL Function to determine whether two comma-separated strings
supplied as parameters share any common elements.
Example Usage:
SELECT SETS_ARE_DISJOINT('8,7,12', '2,8,9'); --> False - shared entry = 8
SELECT SETS_ARE_DISJOINT('7,12', '2,8,9'); --> True - no shared entries
*/
CREATE FUNCTION SETS_ARE_DISJOINT(csv1 TEXT, csv2 TEXT)
RETURNS BOOLEAN
LANGUAGE SQL
BEGIN
DECLARE Occurrences INT;
DECLARE myValue TEXT;
SET Occurrences = LENGTH(TRIM(csv1)) - LENGTH(REPLACE(TRIM(csv1), ',', ''));
Loop1:
WHILE Occurrences > 0 DO
SET myValue = SUBSTRING_INDEX(csv1, ',', 1);
IF (myValue != '') THEN
IF FIND_IN_SET(myValue , csv2) > 0 THEN
RETURN FALSE;
END IF;
ELSE
LEAVE Loop1;
END IF;
SET Occurrences = LENGTH(TRIM(csv1)) - LENGTH(REPLACE(TRIM(csv1), ',', ''));
IF (Occurrences = 0) THEN
LEAVE Loop1;
END IF;
SET csv1 = SUBSTRING(csv1, LENGTH(SUBSTRING_INDEX(csv1, ',', 1)) + 2);
END WHILE;
RETURN TRUE;
END;
Ok, so i achieved what i wanted to do through a foreach loop, i looped through the $finalExcludeUsergroups and created an array of the FIND_IN_SET and than included it in the query .
foreach ($excludeUsergroups as $usergroup)
{
$statusJoiner[] = 'FIND_IN_SET(' . $db->quote($usergroup) .' , user.secondary_group_ids) = 0';
}
and than i joined them through AND condition
if (!empty($statusJoiner))
{
//Joiner for hit part
$Joiner = ' AND ';
$statusFinalJoiner = implode($Joiner,$statusJoiner);
}
and than the query goes like this :-
Update user_status us
LEFT JOIN user u ON us.user = u.user
SET us.status = 1
WHERE (u.user_group_id NOT IN (". $finalExcludeUsergroups . ") AND " . $statusFinalJoiner . ")
That's it, it worked out :)

PHP - Searching for multiple rows with numbers/commas

I've made a user group and a user table in my database, called test and user_test. Every user has a field called groups which countains at least one number, but could also contain multiple numbers, for example 1,2,3. The user group table exists of id and group_name.
What I've been trying to do for so long now is to get data from all groups that this user is assigned to. For example, if one user is assigned to groups 1,2,3 (as its shown in the database), it will print out the name of each group with those id. So perhaps it'd print out group 1 group 2 group 3.
$user_test = $this->mysqli->query("SELECT user_id,groups FROM user_test WHERE user_id = '1'");
while($user_test_fetch = $user_test->fetch_array()) {
$groups = $user_test_fetch["groups"];
}
if(strlen($groups) > 1) { // user has more than 1 group
// ???
} else { // user does not have more than 1 group
$search = "id = '".$groups . "'";
}
$group_data = $this->mysqli->query("SELECT * FROM test WHERE ".$search."");
while($group_data_fetch = $group_data->fetch_array()) {
echo $group_data_fetch["group_name"];
}
Or if you have any other way you'd do this task, please feel free to show me! I'm just simply trying to learn how to do this task (preferably, as efficient as possible).
Pretty simple.
If it is stored in the 1,2,3 format. And assuming a single one is called 1 and id is an INT or BIGINT (otherwise this query will slow down if it is a VARCHAR)
Change
if(strlen($groups) > 1) { // user has more than 1 group
// ???
} else { // user does not have more than 1 group
$search = "id = '".$groups . "'";
}
to
$search = "id IN (".$groups.");
this single line will work with a single group or a set of groups, as long as it is separated by a comma (Because SQL loves commas)

MYSQL: What happens to a result set after the table is changed?

Part of a script I am writing requires me to know exactly how a result set gets it's information from a MYSQL query.
I have a standard result from an SQL query, which I then make a row array using fetch_array.
Whilst looping through this content let's say I delete one of the rows I find from the table. If I reset the pointer of the result back to the first, will I find that row again even though it no longer exists, find an empty row, or miss that row entirely?
In other words is the result asking MYSQL for each row as it needs it, or does it get the whole array in one go so it will not pickup on any changes to the table afterwards?
Cheers in advance
EDIT
$result = $conn->query("SELECT ID, value FROM table");
while($row=$result->fetch_array()){
if(x){
$conn->query("DELETE FROM table WHERE ID=$row['ID']");
mysqli_data_seek($result,0);
}
}
The question is will that deleted row be repeated after the reset, get skipped or return something else such as NULL?
No, it will not delete that row inside initial fetched result set.
But of course it will delete the row in your present table.
If you try to reset the pointer, the initial result set with that row still resides. Not unless you overwrite it with another one.
Consider this example. Lets say you have this inside your table:
+----+--------+
| id | value |
+----+--------+
| 1 | test1 |
| 2 | test2 |
| 5 | test5 |
+----+--------+
If you make this kind of operation:
$result = $conn->query('SELECT id, value FROM mytable'); // sample query
while($row = $result->fetch_assoc()) { // fetch all results
if($row['id'] == 5) { // some condition
$conn->query('DELETE FROM mytable WHERE id = 5'); // delete one row in table
}
}
$result->data_seek(0); // reset the pointer
while($row = $result->fetch_assoc()) { // fetch the same result set
echo $row['id'] . '<br/>';
}
It will delete that particular row in your table but not the one in the initial result.
At initial load, it will show:
1
2
5
If you refresh it, now 5 will be gone since its a new request and another result set called.

MySQL insert multiple rows into a table if they don't already exist

If I have a table like this:
+-------------+-------------+
| Field | Type |
+-------------+-------------+
| document | varchar(35) |
| section | int(11) |
| selection | int(11) |
| status | varchar(35) |
+-------------+-------------+
And I have an array in PHP. Let's call it $choices[]. The column selection coresponds to an array index for the choices array. If I add an element or elements to the $choices array then I need to update the database. I'd be operating in a loop where $document and $section are also set. I don't want to assume that there are no missing rows in the middle of my numbering. What's the most efficient way to check for (document,section,selection) for each element in $choices, create any that does not exist, and set its status to "new"?
Do I have to do this as a loop, or is there a way to do this in one query?
This is completely untested, but this is basically how I'd do it if I didn't ask for help. I guessed someone might have a MUCH better solution than me.
foreach($documents as $document)
{
foreach($sections as $section)
{
$choices=$master_array[$document][$section];
$ch_count=count($choices);
$counter=0;
while($counter < $ch_count-1)
{
$query="SELECT status FROM mytable WHERE document='$document' AND section='$section' AND selection='$counter'";
$result = mysql_query($query);
if(mysql_num_rows($result)==0)
{
$query="INSERT INTO mytable VALUES('$document','$section','$counter','new')";
$result = mysql_query($query);
}
$counter++;
}
}
}
If everything is going to be updated with "new", then this can be achieved somewhat easily.
Examine the following code:
<?
$setStr = "";
foreach($choices as $column)
{
$setStr .= $column . "=" . "'new', ";
}
$sqlCommand = "UPDATE table SET ".substr($setStr,'',-2)." WHERE pick_a_row_here;";
echo $sqlCommand;
?>
For the ones that do not exist, try configuring it so their values are default to false when unspecified. Of course an INSERT statement can easily be adapted from my above UPDATE statement.
Let me know what you think. If you have questions or comments, I'll do my best to answer them.
I ended up using a loop to contatenate together a large query string that looked something like this, except much larger:
INSERT IGNORE INTO mytable VALUES
('doc1','1','0','New'),
('doc1','1','1','New'),
('doc1','1','2','New'),
('doc1','1','3','New'),
('doc1','2','0','New'),
('doc1','2','1','New'),
('doc1','2','2','New'),
('doc1','2','3','New'),
('doc1','3','0','New'),
('doc1','3','1','New'),
('doc1','3','2','New')
And it works. Whether or not its the best, I don't know but at least I'm not sending hundreds of queries to the DB.

Categories