Getting Number of Rows using Left Join - php

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.

Related

How to display mysql data as table header and then all data related to that result as table data in html

I'm working on a venue programming system for festivals to update our current system of just using loads of spreadsheets. I'm trying to figure out a way to display a table that shows all subvenues related to a festival as table headings and then all timeslots related to that subvenue as table columns. I want it to look something like this at the end.
screenshot of current spreadsheet used:
The idea is that you will be able to click on one of the free timeslots, and open a modal to allocate a show to that slot or display shows already attached to it. Ideally each subvenue will be drag and dropped into order but these are problems for later.
So far I'm trying to use a loop to create a table with only 1 column. have an sql query return the header and then inside that return loop have another sql query that returns all of the timeslots, then close the first loop. but this is only displaying 1 table and not looping round to return the others.
Code is
<?php
//selects subvenue
$sql = "SELECT *
FROM Subvenue S
JOIN Venue V
ON S.venueId = V.venueId
JOIN festvenue FV
ON V.venueId = FV.venueId
WHERE FV.festId = $festId;";
$result = mysqli_query($conn, $sql);
if (!$result) die ("Database access failed");
$rows = $result->num_rows;
//starts loop to display subvenues
for ($j = 0 ; $j < $rows ; ++$j) {
$row = $result->fetch_array(MYSQLI_NUM);
$subvenueId = htmlspecialchars($row[0]);
$subvenueName = htmlspecialchars($row[2]);
echo <<<_END
<table>
<tr>
<th id="$subvenueId">$subvenueName<th>
</tr>
_END;
$sql = "SELECT * FROM TimeSlot
WHERE subVenId = $subvenueId
ORDER BY (start >= '05:00:00') desc, start;";
$result = mysqli_query($conn, $sql);
if (!$result) die ("Database access failed");
$rows = $result->num_rows;
for ($j = 0 ; $j < $rows ; ++$j) {
$row = $result->fetch_array(MYSQLI_NUM);
$timeId = htmlspecialchars($row[0]);
$type = htmlspecialchars($row[3]);
$start = htmlspecialchars($row[4]);
$end = htmlspecialchars($row[5]);
$length = htmlspecialchars($row[8]);
echo <<<_END
<tr id="$timeId" class="timeslot-time">
<td class="$type-$length">$start - $end</td>
</tr>
_END;
}
echo "</table>";
}
?>
The sample data I have is below
Subvenue Table
+----------+---------+------------------------+
| subVenId | venueId | subVenName |
+----------+---------+------------------------+
| 1 | 2 | Subvenue 1 |
| 2 | 2 | subvenue 2 |
+----------+---------+------------------------+
timeslot Table
+--------+--------+----------+-------+----------+----------+--------+
| timeId | festId | subVenId | type | start | end | length |
+--------+--------+----------+-------+----------+----------+--------+
| 1 | 11 | 1 | show | 12:00:00 | 13:00:00 | 60 |
| 2 | 11 | 1 | show | 13:30:00 | 14:30:00 | 60 |
| 3 | 11 | 1 | break | 13:00:00 | 13:30:00 | 30 |
| 4 | 11 | 1 | break | 14:30:00 | 15:00:00 | 30 |
| 5 | 11 | 1 | show | 15:00:00 | 16:00:00 | 60 |
| 6 | 11 | 2 | show | 16:30:00 | 17:30:00 | 60 |
| 7 | 11 | 2 | show | 18:00:00 | 19:00:00 | 60 |
| 8 | 11 | 2 | show | 19:30:00 | 20:30:00 | 60 |
| 9 | 11 | 1 | show | 21:00:00 | 22:00:00 | 60 |
| 10 | 11 | 2 | show | 22:30:00 | 23:30:00 | 60 |
+--------+--------+----------+-------+----------+----------+--------+
I'm not even sure a table it the best thing for this or would lists or something else be better?
At the end I want it to display
+-------------------+. +-------------------+
| subvenue 1 | | subvenue 2 |
+-------------------+. +-------------------+
| 12:00:00-13:00:00 | | 16:30:00-17:30:00 |
| 13:30:00-14:30:00 | | 18:00:00-19:00:00 |
| 13:00:00-13:30:00 | | 19:30:00-20:30:00 |
| 14:30:00-15:00:00 | | 22:30:00-23:30:00 |
| 15:00:00-16:00:00 |. +-------------------+
| 21:00:00-22:00:00 |
+-------------------+
etc
I've managed to figure this out. I decided to output a series of lists instead of using a table.
Apart from that my main fix was instead of using the same $sql and $stmt variables in the mySQL query I used $subSql and $subStmt for the second query.
require_once "header.php";
$festId = mysqli_real_escape_string($conn, $_GET["festival_Id"]);
?>
<div id="programming" class="tabcontent">
<h2 >Programming</h2><br>
<?php
//gets the subvenue and starts first loop
$sql = "SELECT *
FROM Subvenue S
JOIN Venue V
ON S.venueId = V.venueId
JOIN festvenue FV
ON V.venueId = FV.venueId
WHERE FV.festId = ?;";
$stmt = mysqli_stmt_init($conn);
if(!mysqli_stmt_prepare($stmt, $sql)) {
header("location: ../festival.php?error=sqlerror&festival_Id".$festId);
exit();
}
else {
mysqli_stmt_bind_param($stmt, "s", $festId);
mysqli_stmt_execute($stmt);
$result = $stmt->get_result();
$rows = $result->num_rows;
for ($j = 0 ; $j < $rows ; ++$j)
{
$row = $result->fetch_array(MYSQLI_NUM);
$subVenueId = htmlspecialchars($row[0]);
$subVenueName = htmlspecialchars($row[2]);
echo <<<_END
<div id="$subVenueId" class="programme">
<ul>
<li class="programme-heading">$subVenueName</li>
<ul>
_END;
//select all timeslots with that subvenue
$subSql = "SELECT * FROM TimeSlot
WHERE subVenId = ?
ORDER BY (start >= '05:00:00') desc, start;";
$subStmt = mysqli_stmt_init($conn);
if(!mysqli_stmt_prepare($subStmt, $subSql)) {
header("location: ../festival.php?
error=sqlerror&festival_Id".$festId);
exit();
}
else {
mysqli_stmt_bind_param($subStmt, "s", $subVenueId);
mysqli_stmt_execute($subStmt);
$subResult = $subStmt->get_result();
while ($row = mysqli_fetch_assoc($subResult)) {
$timeId = htmlspecialchars($row['timeId']);
$type = htmlspecialchars($row['type']);
$start = htmlspecialchars($row['start']);
$end = htmlspecialchars($row['end']);
$length = htmlspecialchars($row['length']);
echo <<<_END
<li id="[$timeId" class="$type-$length">$start - $end</li>
_END;][1]
}
}
echo "</ul></ul></div>";
}
}
That looks exactly how I wanted it. in the initial question

update a column so duplicated values becomes unique

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();
}
}

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";
}

Get Thread information from Single Database

I am trying to learn how to create a simple forum and i stuck in here;
My Database is something like this; (post table)
ThreadID | PostID | Author | Title | Content | Date | Time
--------------------------------------------------------------
1 | 1 | Jack | Thread 1 | ... | 14/12/2015 | 20:21
1 | 2 | Arn | | ... | 15/12/2015 | 19:28
1 | 3 | Hank | | ... | 15/12/2015 | 20:24
2 | 1 | Tom | Thread 2 | ... | 15/12/2015 | 22:41
2 | 2 | Frank | | ... | 16/12/2015 | 13:06
Post table contains both replies and threads
And i would like to get some information from database on category segment
and this is what i want;
Title | Author | Replies | Views | Last Poster
-------------------------------------------------------------------------
Thread 1 | Jack | 2 | - | Hank - 20:24 15/12/2015
Thread 2 | Tom | 1 | - | Frank - 20:24 15/12/2015
and finally my code is;
$get_threads = "SELECT threadid, title, author, postid, date, time FROM post WHERE categoryid = '" . $category . "'";
$threads = mysqli_query($conn, $get_threads); if (mysqli_num_rows($threads) > 0) {while($row = mysqli_fetch_assoc($threads)) {
$get_reply = "SELECT (MAX(postid)-1) rn, (max(postid)) lp FROM post WHERE threadid = '".$row["threadid"]."'";
$reply = mysqli_query($conn, $get_reply); if (mysqli_num_rows($reply) > 0) {while($rod = mysqli_fetch_assoc($reply)) {
$get_lp = "SELECT op.author, op.date, op.time FROM post op INNER JOIN (SELECT author, date, time FROM post WHERE POSTID = '".$rod["lp"]."' GROUP BY threadid) lp ON op.author = lp.author AND op.date = lp.date AND op.time = lp.time WHERE threadid = '".$row['threadid']."'";
$lp = mysqli_query($conn, $get_lp); if (mysqli_num_rows($lp) > 0) {while($roc = mysqli_fetch_assoc($lp)) {
echo "<div class='thread'><div class='threadbox'>";
echo "<a href='forum.php?post=".$row["threadid"]."'class='ttitle'>".$row["title"]."</a>";
echo "<div class='tauthor'>".$row["author"]."</div>";
echo "<div class='treplies'>".$rod["rn"]."</div>";
echo "<div class='tviews'>".$row["date"]."</div>";
echo "<div class='tlastposter'>".$roc['author']." - ".$roc["time"]." ".$roc["date"]."</div>";
echo "</div></div><div class='threaddiv'></div>";
}}}}}} else {echo "<center>Content Not Found</center>";}
Well it may be the worst code ever written but its my first, i apologize for that anyway its duplicating some results i couldnt figure out.
Please help me about how i can handle it?
If you improove and shorten this code i would be glad ^^
Thanks in Advance

Can Mysql IN() function be used to Match ALL of its values instead of ANY?

My question:
Can Mysql IN() function be used to Match ALL the values passed to it instead of ANY? If it cannot, is there another function that can do so?
See an example of what I am running into:
$catIds = implode(',' array(8, 15));
$q = "SELECT account_id
FROM category_records
WHERE user_id = '5'
AND category_id IN ($catIds) ";
$r = mysqli_query($mysqli, $q);
Table Architecture:
id | user_id | category_id | account_id
1 | 5 | 8 | 150
2 | 5 | 12 | 150
3 | 5 | 15 | 150
4 | 5 | 8 | 153
5 | 5 | 15 | 153
6 | 5 | 12 | 160
7 | 5 | 3 | 160
8 | 5 | 15 | 165
9 | 5 | 8 | 165
$catArray = ('8', '15');
In above rows Account #150 & #165 should be returned since both are in Category 8 and 15.
Would it actually be possible for a column to match ALL the values?
If I understand correctly, you have a table similar to the following:
| id | use_id | category_id |
|-----|---------|--------------|
| 1 | 1 | 3 |
| 2 | 5 | 1 |
| 3 | 12 | 4 |
| 4 | 9 | 4 |
| 5 | 15 | 1 |
So, each row can only ever be equal to one item on the array. If that is the case then IN is your answer. Answers describing the use of AND operand instead will not work here since one category_id cannot match against 3 or 4 ids.
The answer could be different if 'category_id' column contains multiple Ids per row (which would not be very good design).
-- EDIT --
Given the table information, I think I understand what you need. You need to select rows whose category_id is in 8 and 15 AND have the same account_id. In that case, try the code with the following query:
$catIds = implode(',' $catArray);
$q = "SELECT cr.* FROM category_records cr "
. "INNER JOIN ( "
. "SELECT account_id FROM category_records "
. " WHERE category_id in (". catIds . ") "
. " GROUP BY account_id HAVING count(account_id ) > 1 "
. " ) dup "
. "ON cr.account_id = dup.account_id "
. "WHERE"
. " cr.category_id in (". catIds . ") "
. "AND"
. " cr.user_id = ". $this->user_id;
$r = mysqli_query($mysqli, $q);

Categories