PHP - Searching for multiple rows with numbers/commas - php

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)

Related

Many to many relationships - Moving data from many tables to a single table

I have a table with users and one with labels
A label can have many users and a user can have many labels, so a Many to Many relationship
A joining table is needed, that's why I have label_user
Below you can see pictures of what they contain with example data:
Users:
https://i.stack.imgur.com/E5E6O.png
Labels:
https://i.stack.imgur.com/1NFjq.png
label_user:
https://i.stack.imgur.com/tW2Uo.png
Let's say I have 5000 users and I can sort them by gender. Let's say 2800 of them are males, how can I assign them all to a label?
Here's some things I tried:
public function add_users_to_label($label_id, $condition, $value)
{
$db = new Database();
$conn = $db->db_connect();
$label_id = escape_string($conn, $label_id);
$query = $conn->query("INSERT INTO `label_user`(`label_id`, `user_id`) SELECT :label_id, psid FROM `iris_messenger_users` WHERE $condition = $value");
$query->bind_param("iss", $label_id, $condition, $value);
if ($query->execute()) {
return true;
}
else {
return "Error inserting data: " . $conn->error . "\n";
}
}
On the user side I have a simple form with select that let's you select a label and then this code:
if(isset($_POST['label-select'])) {
if ($_GET['show_only_gender'] == 'male') {
$condition = 'gender';
$user->add_users_to_label($_POST['label-select'], $condition, $_GET['show_only_gender']);
}
}
Basically, I want to get all users that are male and assign them to a label and put that into label_user with respectively the label_id and the user_id(psid)
Even if this worked I'd still have to do it 2699 times more. What can I do here to optimize and make it to run with 1 query if possible?
I don't think using foreach and running it as much times as there are users is the best option, is it?
Is there any better approach I can take to make this possible?
Although what you are describing does not make sense to have a "label" associated with a person for this specific component, the gender is already on the user table you should be able to get all male based on
select * from user where gender = 'male'
no need to JOIN to a label table on this field. Similarly if you were trying to find people based on a name starting with something... you would not create a label for the name either. Query directly from the table that has that specific component association.
Now, to answer your question, how to insert into the label table for each instance in bulk, you could do something like... I am doing this based on some label ID = 123 as just an example in your labels table that represents gender.
I am doing a LEFT-JOIN in the select so we dont try to add for any user IDs that are already on file do not try to get re-added.
insert into label_user
( label_id,
user_id )
select
123 as label_id,
U.id as user_id
from
users U
left join label_user LU
on U.id = LU.user_id
AND LU.label_id = 123
where
U.gender = 'male'
AND LU.user_id IS NULL
You obviously need to adjust for php.

How do I SELECT all rows WHERE from a table?

By the way, before it is mentioned, I am well aware I should be using mysqli. Thanks in advance.
This is my code:
$q5 = "select listingid FROM userlisting WHERE userid = '$_SESSION[UserID]'";
$r5 = mysql_query($q5) or die(mysql_error());
$a5 = mysql_fetch_array($r5);
The userlisting table is a 'lookup' table and has two columns:
userid and listingid
It has a many to many relationship. In other words, there could be one userid attached (associated) to multiple listingids and thus having multiple rows in that table.
e.g.
userid|listingid
1|1
1|2
1|3
2|1
etc
To keep things simple: What I want to do is check the following:
$a5['listingid'] == $_GET['id']
And if it is True I will display information and if it is False the information will not be displayed.
So on the page mywebsite.com there will be an id as so, mywebsite.com?id=[id here]. I am trying to see if the user $_SESSION[UserID] has an entry in userlisting table that matches the id of the page (well, it is a property website and the id is that of the property listing).
At the moment the code I have above just searches/checks for the first row for that userid only. In the example I gave above that would be listingid ='1' It is not seeing that row 2 and 3 also have entries in them too, listingid = '2' and '3' respectively. So on mywebsite.com?id=1 it is true, but on ?id=2 and id=3 it is coming up false, but userid = 1 has three rows with entries 1, 2 and 3.
I have been trying to find a solution for a while and I am starting to feel frustrated now. I would much appreciate it if someone could come up with a quick solution for me.
You can check both on SQL with some clause like
WHERE userid=XX AND listingid=XX
And remember to escape the get parameter ;)
PS: You can use too a while for iterate the mysql_fetch_row and search if anyone is correct. Something like:
$correct_check = false;
while($a5 = mysql_fetch_array($r5)) {
if($a5['listingid'] == $_GET['id']) $correct_check = true;
}
if($correct_check) ....
else ....
Try something like this
$page_id = $_GET['id'];
$q5 = "select listingid FROM userlisting WHERE userid = '$_SESSION[UserID]' and listingid = '$page_id' ";
$res = mysql_qury($result);
$num_rows = $mysql_num_rows($res);
if($num_rows > 0)
//your ok code
else
//fail message

Update selected check box data and remove unchecked data

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.

Check if a value exists in mysql column

Is there a way to check if a value exists in a mysql column? I have table songs, and there are some columns, one of them is called 'agent_ip' where i will put a list/array of all user ip's that will visit the site. I need to check if current user ip is present in column 'agent_ip'. Here is some of my code:
public function voteSong($song_id, $case, $agent_ip) {
$query = $this->link->prepare("SELECT * FROM songs WHERE id = ? LIMIT 1");
$query->bindValue(1, $song_id);
$query->execute();
$rowcount = $query->rowCount();
if ($rowcount != 0)
{
if (!in_array($agent_ip, $r['ip']))
{
if ($case === 'like')
{
while($r = $query->fetch())
{
$vote = $r['votes'] + 1;
}
}
elseif ($case === 'dislike')
{
while ($r = $query->fetch())
{
if ($r['votes'] > 0)
{
$vote = $r['votes'] - 1;
}
else
{
$vote = 0;
}
}
}
$query = $this->link->prepare("UPDATE songs SET datetime = ?, votes = ?, agent_ip = ? WHERE id = ?");
$query->execute(array(date("Y-m-d H:i:s"), $vote, $agent_ip, $song_id));
}
}
}
The line if(!in_array($agent_ip, $r['ip'])) contains the wrong function which won't work, but i need an alternative for mysql. $r['ip'] variable is data from the 'agent_ip' column which look like this 127.0.0.1, 127.0.0.1, 127.0.0.1 (using 127.0.0.1 just for example, every 127.0.0.1 is a different ip)
If you're only checking against a single IP, why don't you just modify your query from:
"SELECT * FROM songs WHERE id = ? LIMIT 1"
To:
"SELECT * FROM songs WHERE id = ? AND agent_ip = ? LIMIT 1"
It seems a bit wasteful to query your whole result set when you are only querying against a specific IP and returning a single row.
EDIT: Your current method would be extremely inefficient, you are passing a unique agent_ip each time you want to query a song to check if the IP exists, that would be fine, but you are creating a new DB connection every time from which you pull back all info which belongs to that song.
Lets say we have 1 song, and 3IP's, currently the application would work like this:
1) Call the method, passing IP_1
2) Query the database getting all songs for ID1
3) Check if IP_1 is in the result set and do process
4) Call the method, passing IP_2
5) Query the database getting all songs for ID1
6) Check if IP_2 is in the result set and do process
7) Call the method, passing IP_3
8) Query the database getting all songs for ID1
9) Check if IP_2 is in the result set and do process
As you can see, there is a lot of repetition here which is going to hinder your apps performance as it scales, you would be so much better modifying your current function to accept a list of results for a song which is pre-queried only once and then recursively call a check function by passing that result array with your unique IP address.
UPDATE You stated I understand that i need to have 2 tables(1 = songs; 2 = votes). But i cannot imagine how i will get songs from database, arranged by votes quantity.
You should read SQL's JOIN documentation, the concept is simple - JOIN allows you to pull back a more detailed set of information based on what you want to query, in your example you may want to find out how many votes a specific song has.
Your tables may look like:
Songs
SONG_ID Primary Key
SONG_TITLE
SONG_DURATION
SONG_TAGS
Votes
VOTE_ID Primary Key
SONG_ID Foreign Key - (references the song_id table)
VOTE_RES Bool (either 0 for no, 1 for yes)
AGENT_IP Who sent the vote
You could then find out how many people said they liked the song by performing a join:
SELECT * FROM songs
JOIN votes
ON songs.song_id = votes.song_id
WHERE songs.song_id = 1
AND votes.vote_res = 1;
This would return all the song with the id of 1 and all of its associated likes. Hope that helps a bit :)
First you need to deserialize/decode the data from the column to the proper php array and then you can use in_array function. In your post edit you stated that you have a comma separated list of IP's, so to convert it to array you need to use an explode function:
$ip_list = explode(', ', $r['ip']);
now you can use the in_array function on the new array:
if(!in_array($agent_ip, $ip_list))

MySQL query searching (advanced)

I have a course page, this page is setup to display the details (easy), then who is teaching it first, second, third, and forth period of each semester. The problem with that is, my teachers data is handled in one giant chain.
Bobby: 1-1-1-1-1-1-1-1
Tina: 20-20-20-10-1-1-1-1
Joey: 20-1-1-1-49-432-10-19
What I want to do is find a course: 20, when all of the information is shown a search through teachers would be made to find out who is teaching course 20 and what period?
Ex.
Course id: 20
Period 1: Joey, Tina
Period 2: Tina
Period 3: Tina
I want to get every teacher teaching that course in one search but given the obstacle of the capact data which may be a problem.
Teacher Table:
id / name / link / course (1-1-1-1-1-1-1-1)
Course Table:
id / name / code / grade / level
Teachers Course contains the id's from course Table
While re-structuring your db might be the best answer, I thought I'd post a straight php solution that works with your current structure and presumptively the rest of your code.
//I set up variables to contain your expected search results so I could test
$search_course = '20';
$search_results = array(
'Bobby' => '1-1-1-1-1-1-1-1',
'Tina' => '20-20-20-10-1-1-1-1',
'Joey' => '20-1-1-1-49-432-10-19'
);
//explode the course strings into arrays and store with teacher names so
//you can loop through them later
foreach($search_results as $teacher=>$string_courses){
$array_courses = explode('-',$string_courses);
$search_results[$teacher] = $array_courses;
}
//Match course you are searching for to the elements in your array
//Create a result array with period and matching teachers
foreach($search_results as $teacher=>$courses){
foreach($courses as $period => $course){
if($course == $search_course){
$results[$period][] = $teacher;
}
}
}
//Loop through your result array and show the results
//I expect you'll have different html for this
foreach($results as $period => $teachers){
echo 'Period: ';
echo $period+1;
echo implode(',',$teachers);
echo '<br>';
}
The printed results match the list you wanted in your OP
Create a new table
Something like this:
CREATE TABLE `TeacherToPeriod` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`TeacherID` INT NOT NULL ,
`CourseID` INT NOT NULL ,
`Period` INT NOT NULL
) ENGINE = MYISAM ;
Insert the data
Here is some PHP-Code:
foreach($lines as $line){
$line_data = split($line, ': ');
$teacher = $line_data[0];
// SELECT your $teacher_id from the database
$courses = split($line_data[0], '-');
$i = 0;
foreach($courses as $course_id){
$i++;
$sql = "INSERT INTO `TeacherToPeriod` (`TeacherID` ,`CourseID` ,`Period`) ";
$sql.= "VALUES ($teacher_id, $course_id, $i);"
mysql_query($sql);
}
}
Select the data you want
SELECT * FROM `TeacherToPeriod` WHERE `CourseID` = 20 ORDER BY `Period` ASC;
You should change the structure of your db, instead of storing a string of all the periods, you should have an additional table with three columns: teacher,course,period and have a separate row in this table for each course that a teacher is teaching. Then determining who is teaching what course would simply be a matter of querying that table by course id and then sorting by period. e.g:
SELECT teacher_id, course_id, period FROM course_info WHERE course_id = 20
ORDER BY period;

Categories