update a column so duplicated values becomes unique - php

Column title has a lot of duplicated values, more than once.
I need to update the column so, for example if 'gold' is duplicated - it becomes 'gold 1', 'gold 2', etc.
Something like this:
$st = $db->query("select id, title from arts order by title asc");
$st->execute();
$x = 0;
while($row = $st->fetch()){
$title = $row['title'];
//if($title.is duplicated){
$x++;
$title .= ' ' . $x;
$stb = $db->query("update arts set title = '" . $title . "' where id = " . $row['id']);
$stb->execute();
}
}
Any help?

It would be more efficient to do this in pure SQL rather than using PHP. Here is an approach that uses window functions, available in MySQL 8.0.
You can use a subquery to count how many title duplicates exists for each record, and assign a rank to each record within groups of records having the same title. Then, you can JOIN the subquery with the table to update. Where more than one record exists, you can append the row number to every record in the group.
Query:
UPDATE arts a
INNER JOIN (
SELECT
id,
title,
COUNT(*) OVER(PARTITION BY title) cnt,
ROW_NUMBER() OVER(PARTITION BY title ORDER BY id) rn
FROM arts
) b ON a.id = b.id
SET a.title = CONCAT(a.title, b.rn)
WHERE cnt > 1;
Demo on DB Fiddle
Sample data:
| id | title |
| --- | ------ |
| 10 | silver |
| 20 | gold |
| 30 | gold |
| 40 | bronze |
| 50 | gold |
| 60 | bronze |
Results after running the update query:
| id | title |
| --- | ------- |
| 10 | silver |
| 20 | gold1 |
| 30 | gold2 |
| 40 | bronze1 |
| 50 | gold3 |
| 60 | bronze2 |

Please see below code that working for me
// Create connection
$conn = new mysqli($servername, $username, $password,$dbname);
// get all row
$sql = "select id, title from arts order by title asc";
$result = $conn->query($sql);
while ($row=$result->fetch_assoc()) {
$title=$row['title'];
// select where title is same
$sql = "select * from arts where title='".$title."'";
$result2 = $conn->query($sql);
// if number of row is greater then one
if ($result2->num_rows > 1){
$x=0;
while ($row2=$result2->fetch_assoc()) {
$id=$row2['id'];
// skip first row
if($x>0){
$newTitle=$title.' '.$x;
$uquery = "update arts set title='".$newTitle."' where title='".$title."' and id=$id";
$update = $conn->query($uquery);
}
$x++;
}
}
}
and after query run

This works in MySql 5.7:
update arts a inner join (
select * from (
select t.id,
(
select count(*) + 1 from arts
where id < t.id and title = t.title
) counter
from arts t
) t
) t on t.id = a.id
set a.title = concat(a.title, ' ', t.counter)
where a.title in (
select h.title from (
select title from arts
group by title
having count(*) > 1
) h
);
See the demo.
For data:
| id | title |
| --- | -------- |
| 1 | silver |
| 2 | gold |
| 3 | diamond |
| 4 | bronze |
| 5 | gold |
| 6 | bronze |
| 7 | gold |
the result is
| id | title |
| --- | -------- |
| 1 | silver |
| 2 | gold 1 |
| 3 | diamond |
| 4 | bronze 1 |
| 5 | gold 2 |
| 6 | bronze 2 |
| 7 | gold 3 |

I think It would be more efficient to do this in SQL too, but you may can do a function to validate the duplicate, something like this:
function isDuplicated( $title, $db ){
$dp = $db->query("SELECT * FROM arts WHERE title = $title");
if ( $dp->num_rows > 1)
return true;
return false;
}
$st = $db->query("select id, title from arts order by title asc");
$st->execute();
$x = 0;
while($row = $st->fetch()){
$title = $row['title'];
if( isDuplicated( $title, $db ) ){
$x++;
$title .= ' ' . $x;
$stb = $db->query("update arts set title = '" . $title . "' where id = " . $row['id']);
$stb->execute();
}
}

Related

How to fetch results in while loop

I have the following 2 tables.
| ID | Name | Category |
|----|-------------|----------|
| 1 | Foo bar | 3 |
| 2 | Bar foo | 2 |
| 3 | Baz Foo | 3 |
| 4 | Baz Foo2 | 1 |
| 5 | Baz Foo3 | 1 |
| 3 | Baz Foo | 1 |
| ID | Category_name |
|----|---------------|
| 1 | Cat 111 |
| 2 | Cat 222 |
| 3 | Cat 3333 |
I want to display all categories with counter, example:
Cat111 - 3
Cat222 - 2
Cat333 - 2
I tried to do it by the following way, but its not working:
$query = mysqli_query('SELECT * FROM gallery');
while($row = mysqli_fetch_assoc($query)) {
$query_cat = mysqli_query($conn, "SELECT * FROM `pics_cat` WHERE id = '".$row['category']."' GROUP BY category_name");
$rowCat = mysqli_fetch_assoc($query_cat);
echo $rowCat['category_name'];
echo $rowCat['cnt'];
}
You are not sharing the names of the tables, but I assume the first one is Gallery and the second one is pics_cat
If your tables are not going to be very large, I suggest you to solve everything with a single join query, which simplifies the logic of your script.
$query = mysqli_query($conn, 'SELECT p.Category_name,COUNT(g.ID) AS cnt FROM `gallery` AS g LEFT JOIN `pics_cat` AS p ON p.ID = g.Category GROUP BY p.ID');
while($row = mysqli_fetch_assoc($query)) {
echo $rowCat['Category_name'];
echo $rowCat['cnt'];
}
If you prefer to do this with 2 queries in a loop, it's much easier to start from the Category table and then move to the gallery
$query = mysqli_query($conn, 'SELECT * FROM `pics_cat` ORDER BY ID');
while($row = mysqli_fetch_assoc($query)) {
$query_count = mysqli_query('SELECT COUNT(ID) AS cnt FROM `gallery` WHERE Category = '.$row['ID'].'');
$row_count = mysqli_fetch_assoc($query_count);
echo $row['Category_name'];
echo $row_count['cnt'];
}

Generate JSON booleans from Db

I need to generate a JSON String using this format:
{"content":{"brands":1},"brands":[{"id":"1","name":"brand 1","description":"description","icon":"icon","url":"example.com","categories":{"1":"true","2":"true","3":"false","4":"false","5":"false","6":"false"}},{"id":"2","name":"brand2","description":"description","icon":"icon","url":"example.com","categories":{"1":"true","2":"true","3":"false","4":"false","5":"false","6":"false"}}]}
From this tables:
brands:
| id | name | description | icon | url |
|----|--------|-------------|------|-----|
| 1 | name 1 | description | icon | url |
| 2 | name 2 | description | icon | url |
| 3 | name 3 | description | icon | url |
| 4 | name 4 | description | icon | url |
| 5 | name 5 | description | icon | url |
| 6 | name 6 | description | icon | url |
categories:
| id | name | description | icon | url |
|----|--------|-------------|------|-----|
| 1 | name 1 | description | icon | url |
| 2 | name 2 | description | icon | url |
| 3 | name 3 | description | icon | url |
| 4 | name 4 | description | icon | url |
| 5 | name 5 | description | icon | url |
| 6 | name 6 | description | icon | url |
objects:
| id | id_brand | id_category |name | description | icon | url |
|----|----------|-------------|-------|-------------|------|-----|
| 1 | 1 | 1 |name 1 | description | icon | url |
| 2 | 1 | 2 |name 2 | description | icon | url |
| 3 | 2 | 1 |name 3 | description | icon | url |
| 4 | 2 | 2 |name 4 | description | icon | url |
this is my relevant code so far
public function actionBrand($id = null) {
if (empty($id)) {
// Obtiene datos de la base
$sql = "SELECT DISTINCT objects.id_brand AS id, brands.name AS name, brands.description AS description, brands.icon AS icon, brands.url AS url, objects.id_category, categories.name AS category " .
"FROM objects " .
"LEFT JOIN brands ON objects.id_brand = brands.id " .
"LEFT JOIN categories ON objects.id_category = categories.id " .
"ORDER BY objects.id_brand, objects.id_category ";
} else {
// Obtiene datos de la base
$sql = "SELECT DISTINCT objects.id_brand AS id, brands.name AS name, brands.description AS description, brands.icon AS icon, brands.url AS url, objects.id_category, categories.name AS category " .
"FROM objects " .
"LEFT JOIN brands ON objects.id_brand = brands.id " .
"LEFT JOIN categories ON objects.id_category = categories.id " .
"WHERE brands.id = " . (int) $id . " " .
"ORDER BY objects.id_brand, objects.id_category ";
}
$data = Yii::$app->db->createCommand($sql)
->queryAll();
// Obtiene categorias
$categories = Yii::$app->db->createCommand('SELECT id FROM categories ORDER BY id')
->queryAll();
if (!empty($data)) {
// Construye primer registro
$brands[0]['id'] = $data[0]['id'];
$brands[0]['name'] = $data[0]['name'];
$brands[0]['description'] = $data[0]['description'];
$brands[0]['icon'] = $data[0]['icon'];
$brands[0]['url'] = $data[0]['url'];
$total = count($data);
for ($i = 1, $j = 0; $i < $total; $i++) {
if ($brands[$j]['id'] == $data[$i]['id']) {
continue;
} else {
$j++;
$brands[$j]['id'] = $data[$i]['id'];
$brands[$j]['name'] = $data[$i]['name'];
$brands[$j]['description'] = $data[$i]['description'];
$brands[$j]['icon'] = $data[$i]['icon'];
$brands[$j]['url'] = $data[$i]['url'];
}
}
} else {
$brands = array();
}
// Construye y envia JSON
$json['content']['brands'] = count($brands);
$json['brands'] = $brands;
echo json_encode($json);
}
It generates the first part of the JSON that i need, but im stuck at the categories part i need to select the data from the base and convert it to id : (true)(false) on each brand
{"content":{"brands":1},"brands":[{"id":"1","name":"brand 1","description":"description","icon":"icon","url":"example.com"},{"id":"2","name":"brand2","description":"description","icon":"icon","url":"example.com"}]}
Can you help me?
Regards
After you are done building the brands, loop on the categories, searching brands for the existing id match. If it matches, then adjust a true false value for the categories. Then when the categories are completed, append them to every brand in a final loop.
$cleancats = [];
foreach ($categories as $cat) {
$result = false;
foreach($brands as $brand) {
if ($cat['id'] == $brand['id']) {
$result = true; break;
}
}
$cleancats[ $cat['id'] ] = $result;
}
array_walk ($brands,function(&$brand) use ($cleancats) {
$brand['categories'] = $cleancats;
});
(note put this after your for ($i = 1, $j = 0; $i < $total; $i++) loop ends)
That should get the categories to every brand in brands, as you would like.
If you need the categories to be a LITERAL "true" and "false" then adjust this one line above:
$cleancats[ $cat['id'] ] = ($result?'true':'false');

select all products from child categories in parent category

I have the following 'categories' table:
+--------+---------------+----------------------------------------+
| ID | Parent ID | Name |
+--------+---------------+----------------------------------------+
| 1 | 0 | Computers |
| 2 | 1 | Apple |
| 3 | 1 | HP |
| 4 | 2 | Macbook Air |
| 5 | 2 | Macbook Pro |
| 6 | 1 | Dell |
| 7 | 6 | Inspiron |
| 8 | 6 | Alienware |
| 9 | 8 | Alienware 13 |
| 10 | 8 | Alienware 15 |
| 11 | 8 | Alienware 17 |
| 12 | 0 | Smartphones |
| 13 | 12 | Apple |
| 14 | 12 | Samsung |
| 15 | 12 | LG |
+--------+---------------+----------------------------------------+
Let's say I have the following 'products' table:
+--------+---------------+----------------------------------------+
| ID | Category ID | Name |
+--------+---------------+----------------------------------------+
| 1 | 13 | Apple iPhone 8 |
| 2 | 13 | Apple iPhone 8 Plus |
| 3 | 14 | Samsung Galaxy S8 |
+--------+---------------+----------------------------------------+
With the following query, I select all the products in a category:
SELECT
id,
name
FROM
products
WHERE
category_id = ?
Ok, my question:
The product 'Apple iPhone 8' is in the category Apple, this is a subcategory of the category Smartphones. If I replace the '?' in my query with 13 (the category ID of Apple), I get the product. When I replace the '?' in my query with 12 (the category ID of Smartphones), I don't get the product. I want to select all products that are in the category or in one of the child/grandchild/... categories. How can I do this with a single query (if possible)?
you can use join .
your query should be like this
SELECT
id,
name
FROM
products
JOIN
categories
ON
products.category_id = categories.id;
It can be achieved using join
SELECT
id,
name
FROM
products
JOIN
categories
ON
products.category_id = categories.id
WHERE products.category_id = 13 OR categories.parent_id = 12
SELECT id, name FROM products LEFT JOIN categories ON products.category_id = categories.id
1) A QUERY. I'm taking a query from this answer.
How to create a MySQL hierarchical recursive query Please read it for a full explanation of the query. The query assumes that the parent ID will be less than the child IDs (like 19 is less than 20,21,22).
select * from products where `Category ID` in
(select ID from
(select * from categories order by `Parent ID`, ID) categories_sorted,
(select #pv := '12') initialisation
where (find_in_set(`Parent ID`, #pv) > 0
and #pv := concat(#pv, ',', ID)) or ID = #pv)
You have to set the "12" to be whatever the parent category is.
2) Via two sections in PHP, one that loops until you have all category IDs. Then a second section that gets all products in those categories. This is far more verbose but I like how clear you can see what is happening.
$db = new mysqli(host, user, password, database);
$all_ids = []; // total ids found, starts empty
$new_ids = [12]; // put parent ID here
do {
// master list of IDs
$all_ids = array_merge($new_ids, $all_ids);
// set up query
$set = "(".implode($new_ids, ',').")";
$sql = "select ID from categories where `Parent ID` in $set";
// find any more parent IDs?
$new_ids = []; // reset to nothing
$rows = $db->query($sql) or die("DB error: (" . $db->errno . ") " . $db->error);
while ($row = mysqli_fetch_assoc($rows)) {
$new_ids[] = $row['ID'];
}
} while (count($new_ids) > 0);
// get products
$set = "(".implode($all_ids, ',').")";
$sql = "select * from products where `Category ID` in $set";
$rows = $db->query($sql) or die("DB error: (" . $db->errno . ") " . $db->error);
while ($row = mysqli_fetch_assoc($rows)) {
echo "{$row['Name']}<br>\n";
}

Getting Number of Rows using Left Join

I have two tables, one is comments, and another is likesordislikes
comments
+----+--------+---------------+---------+
| id | userid | usercom | comname |
+----+--------+---------------+---------+
| 35 | 5 | check comment | 12 |
| 36 | 6 | comment test | 12 |
| 37 | 6 | third comment | 12 |
| 38 | 5 | number four | 12 |
| 39 | 7 | fifth | 13 |
| 40 | 4 | 6th | 13 |
| 41 | 18 | seven | 13 |
+----+--------+---------------+---------+
likesordislikes
+----+-------+------+-------+
| id | vtype | uid | comid |
+----+-------+------+-------+
| 1 | 0 | 5 | 35 |
| 2 | 1 | 6 | 35 |
| 3 | 1 | 7 | 35 |
| 4 | 0 | 8 | 36 |
| 5 | 1 | 5 | 36 |
| 6 | 1 | 9 | 35 |
| 7 | 1 | 10 | 36 |
| 8 | 1 | 11 | 36 |
| 9 | 1 | 20 | 35 |
| 10 | 0 | 9 | 35 |
| 11 | 1 | 21 | 37 |
+----+-------+------+-------+
In comments table userid is session id (logged in user) and comname is the post unique id on which comments are made by logged in users.
In likesordislikes table vtype is vote type where (0 = dislike , 1 = like), uid is logged in user id who likes or dislikes a comment and comid is from comments table (id) column
Now, i want to show total number of likes or dislikes under each comment for this specific comment.
the PHP code i am trying is here
$query1 = "SELECT
comments.id,
comments.usercom,
COUNT(likesordislikes.id) AS count
FROM
comments
LEFT JOIN likesordislikes ON
comments.id=likesordislikes.comid
WHERE likesordislikes.vtype='1'
GROUP BY
comments.id";
$query2 = "SELECT
comments.id,
comments.usercom,
COUNT(likesordislikes.id) AS count
FROM
comments
LEFT JOIN likesordislikes ON
comments.id=likesordislikes.comid
WHERE likesordislikes.vtype='0'
GROUP BY
comments.id";
$stmt = $DB->prepare($query1);
$stmt->execute();
$likes = $stmt->fetchAll();
$tlikes = count($likes);
$stmt = $DB->prepare($query2);
$stmt->execute();
$dislikes = $stmt->fetchAll();
$tdislikes = count($dislikes);
$slt = "SELECT * FROM `comments` where `comname` = '$c_name' and `post` = '$type'";
$res = mysqli_query($con, $slt);
while($fetch = mysqli_fetch_array($res)) {
echo $fetch['usercom']."<br />";
echo "Likes ".$tlikes."<br />";
echo "Dislikes ".$tdislikes;
}
that way, its not showing each comments likes/dislikes under that comment
in more clearer way, I want this result
Comments:
check comment
Likes 4 - Dislikes 2
comment test
Likes 3 - Dislikes 1
third comment
Likes 1 - Dislikes 0
number four
Likes - Dislikes
fifth
Likes - Dislikes
6th
Likes - Dislikes
seven
Likes - Dislikes
But its showing Likes 4 - Dislikes 2 on each comment on the article 12
Can anyone please check whats wrong in it?
You're basically getting all the likes and dislikes, for all the comments:
$query1 = "...";
$query2 = "...";
// ...
$tlikes = count($likes);
// ...
$tdislikes = count($dislikes);
And printing them for all the comments:
echo $fetch['usercom']."<br />";
echo "Likes ".$tlikes."<br />";
echo "Dislikes ".$tdislikes;
Which is why you're getting the same values for every comment. In order to fix your current code, you could create a map for $likes and $dislikes, like this:
$stmt = $DB->prepare($query1);
$stmt->execute();
$likes = array();
while($like = $stmt->fetch(PDO::FETCH_ASSOC){
$likes[$like['id']] = $like['count'];
}
And then, change your printing to something like:
while($fetch = mysqli_fetch_array($res)) {
echo $fetch['usercom']."<br />";
echo "Likes ".(empty($likes[$fetch['id']])?0:$likes[$fetch['id']])."<br />";
// ...
}
Note: You probably should filter the likes and dislikes queries the same way you filter the comments query (so you don't ask for things you won't use)
An alternative way is changing your three queries for a single one, which would change your code to something like this:
$slt =
"SELECT" .
" c.usercom," .
" SUM(CASE WHEN lod.vtype=1 THEN 1 ELSE 0 END) likes," .
" SUM(CASE WHEN lod.vtype=0 THEN 1 ELSE 0 END) dislikes" .
" FROM" .
" comments c LEFT JOIN likesordislikes lod ON lod.comid=c.id" .
" WHERE" .
" c.comname = '$c_name' AND c.post = '$type'" .
" GROUP BY" .
" c.id"
;
$res = mysqli_query($con, $slt);
while($fetch = mysqli_fetch_array($res)) {
echo $fetch['usercom']."<br />";
echo "Likes ".$fetch['likes']."<br />";
echo "Dislikes ".$fetch['dislikes'];
}
Here you have the query in case you want to see it working
Update
If you don't want to show two zeros, you could change your while to something like:
while($fetch = mysqli_fetch_array($res)) {
echo $fetch['usercom']."<br />";
if($fetch['likes']==='0' && $fetch['dislikes']==='0'){
$fetch['likes'] = '';
$fetch['dislikes'] = '';
}
echo "Likes ".$fetch['likes']."<br />";
echo "Dislikes ".$fetch['dislikes'];
}
Your query looks at the comname columns meaning it would give you everything for article 12 as you call it summed up and not separate for each of them.
You should be looking at the ids - something like
$slt = "SELECT * FROM `comments` where `id` = '$id' and `post` = '$type'";
where $id is the id in your comments table.
I am not sure how you can get this given the code you have shown but give it a try.

Advanced ORDER BY when searching

We have made a search field where you can search for multiple ingredients and find recipes.
We would like to sort the recipes according to the recipe with most ingredients from the search box.
if (isset($_POST['search'])) {
$searchquery = $_POST['search'];
$vals = "'" . str_replace(",", "','", $searchquery) . "'";
$query = mysql_query("SELECT * FROM opskrifter WHERE id IN
(SELECT opskrifterid FROM ingredienser WHERE ing_name IN ('$vals'))") or die("search failed");
Is it possible to sort them?
EDIT:
Recipe-table
+---------+----------+-------------+------------+------------+--+
| id | name | procedure | category | image_url | |
+---------+----------+-------------+------------+------------+--+
| 1 | Sausage | Fry it | Main dish | www....com | |
| 2 | Pizza | Bake it | Main dish | www....com | |
| 3 | Burger | Eat it | Main dish | www....com | |
+---------+----------+-------------+------------+------------+--+
Ingredient-table
+---------+----------+-------------+------------+------------+--+
| id | recipeid | ing_num | ing_meas | ing_name | |
+---------+----------+-------------+------------+------------+--+
| 1 | 1 | 1 | stack | sausage | |
| 2 | 2 | 200 | g | wheat | |
| 3 | 2 | 100 | g | beef | |
+---------+----------+-------------+------------+------------+--+
UPDATE
I've tried implementing the solution from Beginner/Raymond:
"SELECT *, COUNT(*) as `total_ingredients`
FROM opskrifter as k
, ingredienser as i
WHERE k.id = i.opskrifterid
AND i.ing_name IN ($vals)
GROUP BY k.id
ORDER BY COUNT(*) DESC"
Where $vals = "'" . str_replace(",", "', '", $searchquery) . "'";
and $searchsquery = $_POST['search']; //From the searchfield
Unfortunately the search only takes the first word into account, example:
"salt, pasta" it shows every recipe containing salt. But the recipe containing both ingredients is not the top sorted one.
What did I miss?
The answer below before me just missed a GROUP BY that's why it only returns one row
SELECT k.id
, k.name
, COUNT(*) as `total_ingredients`
FROM receipts as k
, ingredients as i
WHERE k.id = i.receipt_id
AND i.ing_name IN ('sausage','beef', 'wheat', 'sauce', 'flour', 'wheat', 'beef', 'ketsup', 'onion', 'garlic')
GROUP BY k.id, k.name
ORDER BY COUNT(*) DESC;
JS Fiddle Here
Jacob!
I think this query can solve your problem.
Please, try it.
SELECT k.id
, k.name
, COUNT(*)
FROM opskrifter k
, ingredienser i
WHERE k.id = i.opskrifterid
AND i.ing_name IN ('sausage','beef', 'wheat')
ORDER BY COUNT(*) DESC

Categories