how to show values from mysql database without repeating values in php - php

When Iam showing database value from table its just repeating them according to content. First table which is content.
id | category_id | name
-----------------------------
1 | 2 | Jason
2 | 1 | Richard
3 | 2 | John
category table:-
id | name
---------
1 | Manager
2 | Employee
I use query:
$query=mysql_query("SELECT * FROM content");
while($row=mysql_fetch_array($query)){
$data[] = array('id'=> $row['id'],
'category_id' => $row['category_id'],
'name' => $row['name']
);
}
for($i=0;$i<count($data);$i++){
echo $data[$i]['category_id'];
echo $data[$i]['name']."<br/>";
}
OUTPUT:-
2 Jason
2 John
1 Richard
But what i want is:-
OUTPUT:-
2 Jason John
1 Richard
Now when echo out the result it shows me category_id 2 times having category_id=2 with its 2 name but what i want is to show only category_id 1 times and its corresponding 2 name of category_id=2. Please help.

Try this approach, it worked for me:
$query = mysql_query("SELECT COUNT(*), category_id, name,
GROUP_CONCAT(name separator ' ') as name FROM content
WHERE category_id=1 OR category_id=2 GROUP BY category_id
ORDER BY category_id DESC");
while($row=mysql_fetch_array($query)){
echo $row['category_id'] . " " . $row['name'] . "<br>";
}
Plus, just a quick note about mysql_* functions being deprecated.
Use mysqli with prepared statements, or PDO with prepared statements, they're much safer.

Use this query:
SELECT id, category_id, GROUP_CONCAT(name SEPARATOR ' ') AS name
FROM content
GROUP BY category_id
See here for some quick tutorials on how to use GROUP BY and GROUP_CONCAT:
w3schools.com / GROUP BY
mysqltutorial.org / GROUP_CONCAT

You shouldn't be using mysql as it is deprecated so to help you along I've done this with PDO to show its not as daunting as you may think.
PDO connection to database:
try{
$db = new PDO ('mysql:host=YourHost;dbname=YourDatabaseName', 'YourDatabaseUserName', 'YourDatabasePassword');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}catch(PDOException $e){
echo $e->getMessage();
die();
}
The above connection uses a a try catch with a way of making any errors cleaner and easier to read by making use of the getMessage() function which removes a lot of the un needed info.
The query you're after in PDO:
$sql = "SELECT GROUP_CONCAT(name SEPARATOR ' ') AS names FROM yourtable GROUP BY category_id";
$query = $db->query($sql);
$results = $query->fetchAll(PDO::FETCH_OBJ);
foreach($results as $result){
echo "<pre>", ($result->names), "</pre>";
}
This is the most basic way to use PDO to query your database but if you start using it like this you will find it easier to then develop the more in depth and secure ways of putting data in and getting data out of your database that help to prevent sql injection.
Hope this is a little food for thought and helps in getting you to move to a more secure API.

try this
select DISTINCT(*) from content
or you can be more specific with ie DISTINCT(columnname)

<?php
// Using MySQL GROUP_CONCAT
$sql = 'SELECT category_id, GROUP_CONCAT(name separator \' \') FROM content GROUP BY category_id';
// Or PHP reduce as below
// Dummy Records
$records = [
['id' => 1, 'category_id' => 2, 'name' => 'Jason'],
['id' => 2, 'category_id' => 1, 'name' => 'Richard'],
['id' => 3, 'category_id' => 2, 'name' => 'John']
];
$result = array_reduce($records, function($carry, $record) {
if (!array_key_exists($record['category_id'], $carry)) {
$carry[$record['category_id']] = [];
}
$carry[$record['category_id']][] = $record['name'];
return $carry;
}, []);
foreach ($result as $categoryId => $names) {
echo $categoryId . ' ' . implode(' ', $names) . "\n";
}

Related

Best practice for writing code in php for foreach->foreach->if(1st loop id == 2nd loop id)->result

First loop table
user_id | fname | lname
1 | first | emp
2 | second| emp
3 | third | emp
Second loop table
shift_id | employee_id
1 | 1
2 | 2
3 | 2
if($employees)
{
foreach ($employees as $employee)
{
if($employee['user_id'] == $shift['employee_id'])
{
echo ucwords($employee['fname']. ' ' .$employee['lname']);
}
}
}
I am getting the right result but I think there is some better way of writing this.
You can use joins in table. Left join means that the user line has to exists (because: LEFT) and the shifts enty is optional.
SELECT user.user_id, user.fname, user.lname, shifts.shift_id
FROM yourUserTable AS user
LEFT JOIN yourShiftsTable AS shifts ON(user.user_id = shifts.employee_id)
Now you get it in your initial array, as if you'd select it as one row from a table and no longer need to do tricks in PHP to combine information. If you can, always try to get the database to manage data, it does that way faster than PHP can.
Please note, the query could be a little off, I just wrote this out of the top of my head.
Just some test code I whipped up to test this from the information provided for this "Demonstration Code".
Note: I have used the mysqli class for the database (instantiating $db ) and have excluded the SQL Table setup.
What you would have had is something along the lines of this...
Case 1 - The original
$db = new mysqli('localhost', 'root', 'test', 'phptutorials_st26');
echo '<h2>Create $employees </h2>';
$query = "SELECT * FROM users";
$result = $db->query($query);
$employees = $result->fetch_all(MYSQL_ASSOC);
var_dump($employees);
echo '<h2>Create $shifts </h2>';
$query = "SELECT * FROM shifts";
$result = $db->query($query);
$shifts = $result->fetch_all(MYSQL_ASSOC);
var_dump($shifts);
echo '<h2>Using foreach on $employees and $shifts</h2>';
if ($employees) {
foreach ($employees as $employee) {
foreach ($shifts as $shift) {
if ($employee['user_id'] == $shift['employee_id']) {
echo ucwords($employee['fname'] . ' ' . $employee['lname']);
echo '<br>';
}
}
}
}
The Result from the above is
First Emp
Second Emp
Second Emp
Case 2 - Using a Join
Well using a join, as everyone has already stated, is the way to go...
$sql = "SELECT u.user_id, u.fname, u.lname, s.shift_id
FROM users AS u
JOIN shifts AS s ON(u.user_id = s.employee_id)
";
$result = $db->query($sql);
$employees = $result->fetch_all(MYSQL_ASSOC);
// To see what comes out because we always check things.
var_dump($joined_result);
(Don't ask me why I love using very abbreviated aliases for the table names! It's just "a thing".)
Then your "loop" simply becomes...
echo '<h2>Using foreach on join</h2>';
foreach ($employees as $employee) {
echo ucwords($employee['fname'] . ' ' . $employee['lname']);
echo '<br>';
}
And the result is...
First Emp
Second Emp
Second Emp
Case 2 - has reduced the code and only requires 1 Trip to the Database.
Does that help you any?
You could do it this way also. Its a little shorter.
SELECT TABLE1.FNAME, TABLE1.LNAME, TABLE2.EMPLOYEE_ID
FROM TABLE1, TABLE2
WHERE TABLE1.USER_ID = TABLE2.EMPLOYEE_ID;

DISTINCT Comma Separated SQL Table Rows

I have a b_topics table with tags column in multiple rows
id | tags
1 | Joshua, Janet, Hannah
2 | Glory, Jon, Celina, Johanna
3 | Bridge,Terry, Sterling
4 | Daniel, Florence, Joanne
I want to check for the related tags with the input Jo so i have the below sql select
$query = Jo;
$sql = mysql_query("SELECT DISTINCT tags FROM b_topics WHERE tags LIKE '%{$query}%'");
while ($row = mysql_fetch_array($sql)) {
$array[] = array ( 'label' => $row['tags'], 'value' => $row['tags'], );
}
echo json_encode ($array);
This is the output:
[{"label":"Joshua, Janet, Hannah","value":"Joshua, Janet, Hannah"},{"label":"Glory, Jon, Glory","value":"Glory, Jon, Glory"},{"label":"Daniel, Florence, Joanne","value":"Daniel, Florence, Joanne"}]
I want the matched words to be on foreach();
Expected output:
[{"label":"Joshua","value":"Joshua"},{"label":"Jon","value":"Jon"},{"label":"Johanna","value":"Johanna"},{"label":"Joanne","value":"Joanne"}]
Fix your data structure! You should not be storing multiple values in a string delimited list. This is simply not the right way to store the data. SQL has this great data structure for storing lists. It is not called "string". It is called "table". You want a table called TopicTags with one row per topic and per tag.
Although you can do nasty string functions to get what you want, this would be much simpler with the right data structure:
select topic_id, tag
from TopicTags tt
where tag like '%Jo%';
You can aggregate if you want the results in a particular format.
set your tables because when your query return a row then you can store that row not only a single tag.
$array[] = array ( 'label' => $row['tags'], 'value' => $row['tags'], );
where $row['tags']=Joshua, Janet, Hannah so its gives result like
'label' ="Joshua, Janet, Hannah"
$sql = "select * from test where tags like '%Jo%'";
$res = mysqli_query($con,$sql);
$string = '';
while($row=mysqli_fetch_array($res)){
$string .= ','.$row['tags'];
}
$array = explode(',', $string);
$search = preg_quote('Jo', '~'); // don't forget to quote input string!
$result = preg_grep('~' . $search . '~', $array);
print_r($result);

Extracting several elements using php

I have these two tables in my database :
table_A: table_B:
id user id user
1 Mike 1 Mike
2 Dan 2 Dan
3 Tom 3 Tom
4 Lina 4 Lina
5 Cynthia
6 Sam
And i'm using this SQL query, to detect which users in table_B do not exist in table_A, and select those users (not existing in table A) ids:
SELECT table_B.id FROM table_B WHERE table_B.id NOT IN (SELECT table_A.id FROM table_A);
And i'm using this php script to put the ids in a variable $not:
$sql=" SELECT table_B.id FROM table_B WHERE table_B.id NOT IN (SELECT table_A.id FROM table_A)";
$result = mysqli_query($con,$sql);
if (!$result) {
printf("Error: %s\n", mysqli_error($con));
exit();
}
while($row = mysqli_fetch_array($result)){
$not=$row[0];
}
Now if i want to extract 2 elements with my sql query:
SELECT table_B.id, table_B.name FROM table_B WHERE table_B.id NOT IN (SELECT table_A.id FROM table_A);
How can i place both, each in a variable using the above script, so i'll be able to insert each element in a different table in the database?
You can put the values in a multidimensional array.
$not = array();
while($row = mysqli_fetch_array($result, MYSQLI_ASSOC)){
$not[] = array(
"id" => $row['id'],
"name" => $row['name']
);
}
echo $not[0]['id']; //Returns 5
echo $not[0]['name']; //Returns Cynthia
echo $not[1]['id']; //Returns 6
echo $not[1]['name']; //Returns Sam
in your query define table_B.id as user_id and table_B.name as user
then in your php use mysqli_fetch_object like this
while($row = mysqli_fetch_object($result)){
$not= $row;
//or if you want to store all the results use $not[] = $row; and define $not as array
}

How to prevent mysql injection when using mysql IN clause without activeRecord in Yii?

I have an array with ids that I get from client. And I want use those ids in my sql query with IN clause. But this query goes on a table that has no model. So there is no active record (criteria) query possible.
** Table userTasks **
--------------------
| idUser | idTasks |
---------+----------
| 1 | 1 |
---------+----------
| 1 | 2 |
---------+----------
| 1 | 3 |
---------+----------
First approach does not work because params are always considered as strings. So :tasks is a string '1,2,3' instead of a comma separated list of ids:
$sql = 'SELECT COUNT(*) AS matches
FROM userTasks
WHERE idUser = :idUser
AND idTask IN (:tasks)';
$result = Yii::app()->db->createCommand($sql)
->queryRow(true,[
':idUser' => $idUser,
':tasks' => implode(',', $tasks)]); //$tasks is a simple array of ids [1,2,3]
So my workaround:
foreach($tasks as $task) //$tasks is a simple array of ids [1,2,3]
{
$inTasks[] = (int) $task;
}
$sql = 'SELECT COUNT(*) AS matches
FROM userTasks
WHERE idUser = :idUser
AND idTask IN (' . implode(',', $inTasks . ')';
$result = Yii::app()->db->createCommand($sql)
->queryRow(true,[':idUser' => $idUser]);
Having come across this problem a few times in my projects I have come-up with the following Yii work-around using CDbCriteria which is a little hacky, but gives the security of param count matching.
I would also use queryScalar() in this instance to get the result directly.
When applied to your example my code would be:
$idUser = 1;
$tasks = array(1,2,3);
$criteria = new CDbCriteria();
$criteria->addInCondition('idTask',$tasks);
$sql = '
SELECT COUNT(*) matches
FROM userTasks
WHERE idUser = :idUser
AND '.$criteria->condition;
$command = Yii::app()->db->createCommand($sql);
$command->bindValue('idUser',$idUser);
$command->bindValues($criteria->params);
$result = $command->queryScalar();
For preventing SQL injection in Yii IN clause we need to bind parameters in IN clause, Yii CDB criteria queries don't have this functionality in built. so you can use below code.
$products_ids = array(234,100,405,506);
map the array for binding
$in_query = implode(',', array_fill(0, count($products_ids), '?'));
Prepare the commadn object for select
$command = Yii::app()->db->createCommand()
->select('product_id, product_name, product_image, product_price')
->from('products')
->where('product_id IN(' . $in_query . ')');
bind the parameters
foreach ($products_ids as $k => $product_id){
$command->bindValue(($k+1),$product_id,PDO::PARAM_INT);
}
get the result
$products = $command->queryAll();

How to retrieve liker id based on ques_id?

Here is my database:
id | liker | ques_id
1 | 15 | 2342
2 | 22 | 2342
3 | 22 | 2311
4 | 15 | 2389
What I need to get is all the liker's who have liked ques_id. So the result should look something like this:
Question 2342 has been liked by 15 and 22.
Question 2311 has been liked by 22 and so on
My current code produces separate row for each liker and ques_id:
$sqlq=mysql_query("SELECT * FROM likes");
while($rowq=mysql_fetch_array($sqlq)){
$qid=$rowq['ques_id'];
$sql=mysql_query("SELECT * FROM likes where ques_id='$qid'");
$num=mysql_num_rows($sql);
$cont='';
while($row=mysql_fetch_array($sql)){
$liker=$row['liker'];
$cont .="$qid being liked by $liker<br>";
}
echo $cont;
}
I haven't tested this but it should get you started:
$sqlq=mysql_query("SELECT DISTINCT(ques_id) FROM likes");
$cont='';
while($rowq=mysql_fetch_array($sqlq)){
$qid=$rowq['ques_id'];
$sql=mysql_query("SELECT * FROM likes where ques_id='$qid'");
$num=mysql_num_rows($sql);
$row=mysql_fetch_array($sql)
$cont .= "$qid being liked by $liker ";
while($row=mysql_fetch_array($sql)){
$liker=$row['liker'];
$cont .= " and $liker";
}
$cont .= ".<br>";
}
echo $cont;
You need no second query to DB, it enough iterate only first result.
For example, you may collect all the likers, that match a specific ques_id in a assoc array with keys are ques_id, like this:
$mathces = array();
$sqlq=mysql_query("SELECT * FROM likes");
while($rowq=mysql_fetch_array($sqlq)){
$qid=$rowq['ques_id'];
$liker=$rowq['liker'];
$matches[ $qid ][] = $liker;
}
Then, you may foreach $mathces array and build you string.
foreach ($matches as $qid => $likers) {
$cont .= "$qid being liked by " . implode(' and ', $likers );
if (count($likers) == 1)
$cont .= ' and so on';
echo "$cont<br>";
}
I didn't test my code. It need to additional validations (e.g. for $linkers in second loop)
SELECT GROUP_CONCAT(liker), ques_id FROM likes GROUP BY ques_id
That will pull each liker grouped together for each ques_id.
You then only have to process the rows returned.
You should steer clear of the mysql extension as it is deprecated; use PDO or mysqli instead.
$sql = 'SELECT GROUP_CONCAT(liker) as likes, ques_id FROM likes GROUP BY ques_id';
foreach ($conn->query($sql) as $row) {
printf('Question %s is liked by %s', $row['ques_id'], $row['likes']);
}

Categories