PHP: Count Duplicates and Remove Duplicate Rows - php

I have a mysql database of visitor IPs, and I'm trying to create an HTML table that shows how many of those IPs are from X country. I can get the script to show which country the visitor is from, but I cannot seem to find a way to show it in groups rather than line by line.
For example, currently I'm getting:
Country | Visitors
------------------
US | Array
UK | Array
UK | Array
UK | Array
US | Array
MX | Array
MX | Array
What I want is:
Country | Visitors
------------------
US | 2 Visitors
UK | 3 Visitors
MX | 2 Visitors
I've tried array_count_values, but it still lists every visitor by line and assigns a value of "Array" to every line.
The IPs are in a database and the script pulls the IPs and then assigns the IP a Country Code value based on the data in a separate file.
Here's what I hacked up with my amateur skills. If there's a better way to accomplish this, suggestions welcome!
require_once("geoip.inc");
$gi = geoip_open("GeoIP.dat",GEOIP_STANDARD);
include_once($_SERVER['DOCUMENT_ROOT'] . 'connect.php');
/* Performing SQL query */
$data = mysql_query("SELECT * FROM the_ips LIMIT 100")
or die(mysql_error());
echo "<table>";
echo "<td><strong>Country</strong></td><td><strong>Visitors</strong></td></tr>";
while($info = mysql_fetch_array( $data ))
{
$ip = $info['ip_address'];
$country_code = geoip_country_code_by_addr($gi, $ip);
$countrylist = array($country_code);
$frequency = array_count_values($countrylist);
echo "<tr><td style='width: 100px;'>".$country_code."</td><td style='width: 100px;'>".$frequency."</td></tr>";
}
echo "</table>";
geoip_close($gi);

SELECT
Count(Distinct country) As country_number
, country
FROM the_ips
GROUP BY country
LIMIT 100
try this for a start

For anyone else looking for the exact method to remove duplicate entries, here it is. Assuming that your duplicates are in the 'name' column. Just change the 'name' and 'id' to your tables respective column names.
// Select duplicate entries
$result = mysql_query("SELECT id, name
FROM tble_name
GROUP BY name
HAVING COUNT(*) > 1");
// Loop through the results
while($rows=mysql_fetch_assoc($result)){
// Delete each row by id
mysql_query("DELETE FROM tble_name
WHERE id = ".$rows['id']."");
}
Note: This will remove the selected results for good, be sure to backup your data before messing with any DELETE queries if you're uncertain.
I hope this helps :)

Related

Select mysql column (which is array) where other column is defined,:- and insert it into php variable

I have a two columns in database:- copy (which is array) and bookid. My code is;
$query = mysqli_query($db, "select copy from book_outward WHERE bookid like 'B1'");
while ($row = mysqli_fetch_array($query)) {
$copyid = $row['copy'];
}
Database shows like this
+----------------+
| id copy bookid |
+----------------+
| 1 1 B1 |
| 2 2,3 B1 |
| 3 4 B1 |
| 4 2 B2 |
+----------------+
but it stores only last values which was entered in 'B1'. I also tried
$copyid[] = $row['copy'];
but in this case I have to change array keys manually every time.
My aim is to insert copy into column bookid='B1' and before it has to make sure that only UNIQUE values can be stored in database for B1.
HTML :-
<input type="text" name="bookid" />
<input type="text" name="copies[]" />
PHP code for inserting:-
$book_id = $_POST['bookid'];
$copies = implode(',',$_POST['copies']);
$result = mysqli_query($db, "insert into book_outward(bookid,copy) values ('$book_id','$copies')");
As some of the comments mention it it not the best way to to it but it is possible.
You can obtain all the copyid data by:
$query = mysqli_query($db, "select copy from book_outward WHERE bookid like 'B1'");
$copyid = "";
while ($row = mysqli_fetch_array($query)) {
$copyid .= $row['copy'] . ",";
}
$copyidsFromDB = explode(",",rtrim($copyid , ','));
After that you can check if what you got in the request are in there using array_intersect:
$copies = $_POST['copies']
// if not an array use: $copies = explode(",", $_POST['copies'])
if (count(array_intersect($copies, $copyidsFromDB) == 0)
// insert to DB
Solved by myself by adding one line only
`$copies2 = explode(",",rtrim($copies , ','));`
before array_intersect Thanks code helps greatly.
So, sounds to me like the issue is the original data DB schema has some issues.
A sql-like DB is not a great place to put 'array' style value. It already has a mechanism for doing this: a link table.
That'd save you the trouble of having to manually shuffle around your primary keys in this table. It's way easier to check and validate.
If you HAVE to do this via php code, for example you don't have SQL access, like in a controlled build environment, you should take advantage of 'string keys' to on your $copyid, (hint rename to $copyidmap) to group together like pieces of data and preserve key relationships.
so the following would be unique:
map->{bookId}->{copyid}->{id} // Where id is that primary table id. Obviously, validate and do your undefined array assignments.

Selecting random rows using MySql and using the same row information on another page

I have a working quiz now, where I receive a selection of 3 random rows from a database currently of only 5 rows (expansion in progress). The database of format:
| id | question | option1 | option2 | option3 | answer |
--------------------------------------------------------
| 1 | What is my name? | Dave | Bob | Charles | Linda |
Within connect.php: (I am aware MySql statements are deprecated and will be updating to MySQLi in due course)
$query="SELECT * FROM (SELECT * FROM mytable ORDER BY rand() LIMIT 3) T1 ORDER BY id";
$result = mysql_query($query, $connect);
Then within quiz.php:
require (connect.php);
while($row = mysql_fetch_assoc($result)) {
echo '<p>';
echo $row['id'] . '. ';
echo $row['question'];
echo '</p>';
}
Radio buttons are generated within the while loop which associate the options with the generated question, this section works perfectly even with randomisation so is not included above.
Before I began attempting randomisation of the database row selections I could use the same while loop in quiz.php and quizresults.php as they would both select the same 5 rows which was perfect. But now I have decided to randomise the selection I can't use the same while loop within each page as it selects a random 3 rows for the quiz.php (the question section) and then selects a different random 3 rows for the quizresults.php which isn't ideal when the results don't match the questions...
So what I want to do is when the query is made in quiz.php, I want to assign all of $row 's values to a mutlidimensional array (i think) and then use that array within quizresults.php to echo those rows with all the values from the original query using I'm hoping the same format as within the results page.
Within quizresults.php:
require (connect.php);
while($row = mysql_fetch_assoc($result)) {
echo '<p>';
echo $row['id'] . '. ';
echo $row['question'];
echo '</p>';
}
It uses the same loop to print the questions but obviously with some radio button verification code, which I trimmed for the purpose of the question.
So to summarise, my question is: How can I achieve randomisation in the quiz.php (questions page) and then use the selection of random rows within the quizresults.php (answer page) without using the same query?

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.

Combing MySQL Rows With Same ID PHP

I have a table "orders" which saves all the orders made on a website. It saves the data in the following way:
ID | Session_id | image | item | extra | customer_name
Sample date
12 | sdgfafjhsf | image1.jpg | coffee | milk | roger
13 | sdgfafjhsf | image1.jpg | muffin | jam | roger
14 | fjgjgsdfjg | image3.jpg | coffee | none | John
Currently I have the PHP accessing the database and spitting out all of the listings one by one.
mysql_connect("localhost", "root", "") or die(mysql_error()) ;
mysql_select_db("store") or die(mysql_error()) ;
//Retrieves data from MySQL
$data = mysql_query("SELECT * FROM orders WHERE status ='ordered'") or die(mysql_error()); //Puts it into an array
while($info = mysql_fetch_array( $data ))
{
//Outputs the image and other data
Echo "$info[customer_name] <img src='cameras/$info[image]'/> : $info[item] with $info[extras] <br />";
}
I am ideally wanting the data to group by the session ID. So it prints out the name of the customer and the image once and then all of the items associated with it.
eg. Roger , coffee, milk, muffin, jam
Any ideas?
Thanks!
A simple way would be to order the SQL so that you get all entries from each session following each other, and just remember the last session id you fetched to tell if you should output the name and picture or not; here's some pseudo code to show what I mean;
$data =
mysql_query("SELECT * FROM orders WHERE status ='ordered' ORDER BY session_id")
or die(mysql_error());
$last_session_id = "**DUMMY**"; // Set a dummy value to not match the first row
while($info = mysql_fetch_array( $data ))
{
if($info['session_id'] != $last_session_id)
{
//Outputs the image and other data if a new session_id has been found
echo "$info[customer_name] <img src='cameras/$info[image]'/> : $info[item] with $info[extras] <br />";
$last_session_id = $info['session_id'];
} else {
// Same session_id as last row, skip name and picture
echo "$info[item] with $info[extras] <br />";
}
}
As a side note, the mysql_* database API is deprecated, you should look into using mysqli or pdo instead.
well try this..
SELECT Session_id,image,item,extra,customer_name FROM orders WHERE status='ordered' group by Session_id,image,item,extra,customer_name
Here you have to run two separate query. In first query you have to find all distinct customer name, better if you use customer id rather than customer name because id cannot be duplicate. Then after getting all customer who have ordered item, iterate them in loop and inside loop run another query to retrieve all orders of that customer by again customer id or by customer name.
In you while($info = mysql_fetch_array( $data ))-loop, where you print the output, you could add it to a standard array where the keys are the Session_ids.
Then you call echo $foo['sdgfafjhsf'] to get an array with db-entries which you can enumerate through print accordingly.

php simple following-follower system, removing a value from array and updating with foreach (updating doesn't work)

I made a simple following-follower system with php(pdo) and mysql. My problem is,
Let's say there is a user name Mike with ID number 99. And Mike has two followers, Greg(id = 77) and Jenny(id = 88)
Greg's following list looks like this (1,2,3,4,99), and Jenny's following list looks like this (5,6,99,7)
What I am trying to do is, when Mike(99) deletes his account, I want to remove id number 99 from Greg(77) and Jenny(88)'s following lists.
Table called 'mytable' has 3 fields. id(INT), following_list(text), follower_list(text).
I've been struggling with this problem for a few days. Can anyone please give me some advice? Thank you so much in advance!!!
Below is my update function
public function update_following_list(){
// $this->myid is Mike's id number(99) who is about to delete his account
// Selecting the list of people following Mike
$query = "SELECT 'follower_list' FROM 'mytable' WHERE 'id' = $this->myid";
$result = $this->dbc->query($query);
foreach($result as $row){
$this->follower_list = $row['follower_list'];
// When I echo $this->follower_list, I get 77,88
// Now querying Greg(77) and Jenny(88)'s following_lists, which have Mike's id number(99) in them.
$query = "SELECT 'following_list' FROM 'mytable' WHERE 'id' IN ($this->follower_list)";
$result = $this->dbc->query($query);
foreach($result as $row){
$this->following_list = $row['following_list'];
// When I echo $this->following_list, I get both Greg(1,2,3,4,99) and Jenny(5,6,99,7)'s following lists
// Here, I am turning following list into array by using explode
$this->following_array = explode(",", $this->following_list);
foreach($this->following_array as $key => $value){
if($value == $this->myid){
// Removing Mike(99)'s id number from Greg and Jenny's following lists
unset($this->following_array[$key]);
// Add back commas, which will then become string
$this->new_following_list = implode(",", $this->_following_array);
// When I echo $this->new_following_list, I get both Greg(1,2,3,4) and Jenny(5,6,7)'s new list without Mike(99)'s id number
// My problem starts here. I was able to remove Mike's id number from Greg and Jenny's lists. But I am having a trouble updating Greg and Jenny's following lists with new following lists.
// The update query below does not work...
$query = "UPDATE 'mytable' SET 'following_list' = $this->new_following_list WHERE 'id' IN ($this->follower_list)";
$result = $this->dbc->query($query);
} // End of if($value == $this->myid)
} // End of foreach($this->following_array as $key => $value)
}
}
} // End of function
This will not scale properly. It's better to normalize your data model like this:
following
user_id | following_user_id
---------------------------
77 | 1
77 | 2
77 | 3
77 | 4
77 | 99
88 | 5
88 | 6
88 | 7
88 | 99
And add two indexes:
UNIQUE(user_id, following_user_id)
INDEX(following_user_id)
To get followers of 99:
SELECT * FROM following WHERE following_user_id=99;
To see who 77 follows:
SELECT * FROM following WHERE user_id=77;
I think your database setup is not optimal. Store comma-separated values in one field, screams for a binding table and some normalization !!
You should have your comma separated value list to be a table/relation, e.g.:
followTable:/ user_id | follows
Deletion of mike's id is then as simple as:
DELETE FROM followTable WHERE follows = 99 OR user_id = 99
The latter makes sure the links between Mike and his followers are being deleted also.

Categories