PHP, MySQL Junction Table displays information incorrectly - php

My tables are as follows:
person2: personID
validPositions: positionID, positionDesc
personPositions: personID, positionID
I want to be able to display a person with their multiple positions (some may only have one position) on one line. Example: Sierra's Positions: s1, s2
Currently it displays each position they have, however on different lines AND it repeats the last position in the database twice. Example:
Sierra's Positions: S1
Sierra's Positions: S2
Sierra's Positions: S2
$sql = "SELECT * FROM person2";
// LEFT JOIN validTitles ON personTitle.positionID=validTitles.positionID GROUP BY person2.personID";
if ($result = mysqli_query($connection, $sql)) {
// loop through the data
//create 4 columns for the table
$columns=5;
$i = 0;
while($row = mysqli_fetch_array($result))
{
// the % operator gives the remainder of a division of two values
// in this case, 0/4, this tells the table row to jump to a new row
// if there are already 4 columns in one row
if($i % $columns == 0){
//begin table row
echo "<tr>";
} //end if
echo '<td class="staffImage badgeText frameImage displayInLine">
<br>
<strong>'.$row["firstName"].'</strong>
<strong>'.$row["lastName"].'</strong><br>'
.$row["position"].
'<div id="openModal'.$row["personID"].'" class="modalDialog">
<div>
X
<h2>' . $row["firstName"] . " " .
$row["lastName"].'</h2><br>
<img class="floatLeft" src="images/staff/'.$row["imgName"] .'.jpg">
<p><strong>Hire Date: </strong>'.$row["hireDate"].'<br>
<p><strong>Major: </strong>'.$row["major"].'<br>';
//if the field "major2" (Double Major) is not null, display it
if($row["major2"] != NULL)
{
echo ' & '.$row["major2"].'<br>';
}
//if the field "minor" is not null, display it
if($row["minor"] != NULL)
{
echo '<p><strong>Minor: </strong>'.$row["minor"].'<br>';
}
//if the field "concentration" is not null, display it
if($row["concentration"] != NULL)
{
echo '<p><strong>Concentration: </strong>'.$row["concentration"];
}
**$sql2 = "SELECT * FROM personPositions LEFT JOIN validPositions ON personPositions.positionID=validPositions.positionID ";
if ($result2 = mysqli_query($connection, $sql2)) {
// loop through the data
while($row2 = mysqli_fetch_array($result2))
{
echo '<p><strong>Position(s): </strong>'.$row2["positionDesc"];
}//end second while**
} //end second if
'</div>
</div>
</div> ';
echo '</td>';
Any help is greatly appreciated, I am new to PHP and MySQL and unsure what to do!
SAMPLE DATA:
personPositions table:
personID 1 | positionID 11
personID 1 | positionID 22
personID 2 | positionID 22
personID 2 | positionID 55
validPositions table:
positionID 11 | positionDesc S1
positionID 22 | positionDesc S2
positionID 55 | positionDesc S3

Something like this should work for you:
SELECT p.personID, GROUP_CONCAT(DISTINCT positionDesc ORDER BY positionDesc) AS positions
FROM person AS p
LEFT JOIN personPositions AS pp ON p.personID = pp.personID
LEFT JOIN validPositions AS vp ON pp.positionID = vp.positionID
GROUP BY p.personID
Output:
personID | positions
----------+-----------
1 | S1,S2
2 | S2,S3
Demo here

Related

MySQLi Repeat Row Depending on Column Quantity Value

I have created a simple SQL statement to display which products customers have purchased from a woo commerce store.
This is working great but i'm struggling to find a SQL function that repeats rows based on a value, maybe one doesn't exist and PHP is required, any help would be great.
Thanks
CURRENT VIEW
ORDER ID. | PRODUCT NAME. | QTY
-------------------------------------
123 | APPLE | 3
124 | ORANGE | 2
125 | PEAR | 1
DESIRED VIEW
ORDER ID. | PRODUCT NAME. | QTY
-------------------------------------
123 | APPLE | 3
123 | APPLE | 3
123 | APPLE | 3
124 | ORANGE | 2
124 | ORANGE | 2
125 | PEAR | 1
CODE
<?php
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT wppc_wc_customer_lookup.customer_id, wppc_wc_customer_lookup.first_name,
wppc_wc_customer_lookup.last_name, wppc_wc_customer_lookup.email,
wppc_wc_order_stats.customer_id, wppc_wc_order_stats.total_sales,
wppc_wc_order_stats.order_id, wppc_wc_order_stats.date_created, wppc_wc_order_stats.status,
wppc_woocommerce_order_items.order_id, wppc_woocommerce_order_items.order_item_name,
wppc_woocommerce_order_items.order_item_id, wppc_woocommerce_order_itemmeta.order_item_id,
wppc_woocommerce_order_itemmeta.meta_id, wppc_woocommerce_order_itemmeta.meta_key,
wppc_woocommerce_order_itemmeta.meta_value FROM wppc_wc_customer_lookup
LEFT JOIN wppc_wc_order_stats ON wppc_wc_customer_lookup.customer_id =
wppc_wc_order_stats.customer_id
LEFT JOIN wppc_woocommerce_order_items ON wppc_wc_order_stats.order_id =
wppc_woocommerce_order_items.order_id
LEFT JOIN wppc_woocommerce_order_itemmeta ON wppc_woocommerce_order_items.order_item_id =
wppc_woocommerce_order_itemmeta.order_item_id
WHERE wppc_woocommerce_order_itemmeta.meta_key = '_qty' AND
wppc_woocommerce_order_items.order_item_name = 'Product Name' AND
wppc_wc_order_stats.status = 'wc-completed'
ORDER BY wppc_wc_order_stats.date_created DESC";
$result = $conn->query($sql);
echo "<table border=\"1\"><tr>";
echo "<tr><th>Order ID</hr><th>First Name</hr><th>Last Name</hr><th>Email</hr><th>Total</hr>
<th>Order Date</hr><th>Product Name</hr><th>Quantity</hr>";
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
echo "<tr><td>".$row["order_id"]. " </td><td> " .$row["first_name"] . " </td><td> " .
$row["last_name"] . " </td> <td> " . $row["email"] . " </td><td> &pound".$row["total_sales"]
. " </td> <td>".$row["date_created"] . " </td> <td> ".$row["order_item_name"] . " </td> <td>
".$row["meta_value"] . " </td></tr> ";
}
} else {
echo "0 results";
}
echo "</table>";
$conn->close();
?>
This is working great but i'm struggling to find a SQL function that repeats rows based on a value, maybe one doesn't exist and PHP is required, any help would be great.
the idea is to make multiple select queries with different where clauses;
for instance in this case we need query like this:
SELECT * FROM table WHERE QTY > 0
UNION ALL
(SELECT * FROM table WHERE QTY > 1)
UNION ALL
(SELECT * FROM table WHERE QTY > 2)
ORDER BY `ORDER ID`
so we can create mysql procedure to generate such query depending on maximum qty like so
CREATE PROCEDURE GetAllProducts()
BEGIN
select max(QTY) - 1 into #maxQty from table;
set #resQry = 'SELECT * FROM table WHERE QTY > 0';
set #i = 0;
label1:
LOOP
IF #i = #maxQty THEN
leave label1;
END IF;
SET #i = #i + 1;
SET #resQry = CONCAT(#resQry, ' UNION ALL (SELECT * FROM table WHERE QTY > ', #i, ')');
END LOOP label1;
SET #resQry = CONCAT(#resQry, ' ORDER BY `ORDER ID`');
PREPARE stmt FROM #resQry;
EXECUTE stmt;
END;
and then you can call this procedure
CALL GetAllProducts();
If you are running MySQL 8.0, you can do this with a recursive query. Starting from your existing query, that would be:
with recursive
data as ( <... your query goes here ...> ),
rcte as (
select order_id, product_name, qty, 1 rn from data
union all
select order_id, product_name, qty, rn + 1 from rcte where rn < qty
)
select order_id, product_name, qty from rcte
In earlier versions, you could use a table of numbers. Here is an example that would quantities up to 3:
select d.*
from ( <... your query goes here ...> ) d
inner join (select 1 qty union all select 2 union all select 3) n
on n.qty <= d.qty
You can expand the subquery with more union alls to handle greater quantities.

Indicating checkbox values from MySQL/PHP

I have 4 MySQL tables:
properties (these are properties)
id | address | price
1 | 123 St. | 100,000
2 | 456 St. | 200,000
categories (these are categories of amenities for properties)
id | title
1 | Kitchen
2 | Entertainment
items (these are items under the categories above)
id | catid | item
1 | 1 | Stove
2 | 1 | Fridge
3 | 2 | TV
4 | 2 | Couch
propertyamenities (these are the particular amenities for a property)
id | listingid | amenityid
1 | 1 | 3
2 | 1 | 4
I have built a form where the user can select the various amenities for a given property using checkboxes, which inserts from PHP into MySQL tables above.
I now need to allow the user to edit the amenities (using checkboxes) for a given property. The code I am currently using is:
$result = mysqli_query($con,"SELECT DISTINCT a.title AS maincategory, b.item AS smallcategory, b.id as itemid FROM categories a, items b WHERE a.id = b.catid ORDER BY maincategory ASC, smallcategory ASC") or die(mysqli_error($con));
// keep track of previous maincategory
$previous_maincategory = NULL;
while ($row = mysqli_fetch_assoc($result))
{
// if maincategory has changed from previouscategory then display it
if ($previous_maincategory != $row['maincategory']) {
echo '<tr>
<td colspan="2" valign="top"><strong>';
echo $row['maincategory'];
echo '</strong></td>
</tr>';
}
echo "<tr>";
echo "<td>";
echo "<input type='checkbox' name='amenities[]' value='".$row['itemid']."'>".$row['smallcategory'];
echo "</td>";
echo "</tr>";
// record what the previous category was
$previous_maincategory = $row['maincategory'];
}
This displays the categories and the items as checkboxes. I'm not sure how for a given property, to modify this script and have the associated checkboxes "checked". I presume I need to add in the propertyamenities table to the query, and a WHERE clause to indicate which property (e.g.,1) is being edited, but not sure how to add in the third table.
EDIT
I am closer, but it is returning the same item twice.
$result = mysqli_query($con,"SELECT DISTINCT a.title AS maincategory, b.item AS smallcategory, b.id as itemid, c.amenityid as amenity FROM categories a, items b, propertyamenities c WHERE a.id = b.catid and c.listingid = '$id' ORDER BY maincategory ASC, smallcategory ASC") or die(mysqli_error($con));
// keep track of previous maincategory
$previous_maincategory = NULL;
$myprevious_maincategory = NULL;
while ($row = mysqli_fetch_assoc($result))
{
// if maincategory has changed from previouscategory then display it
if ($previous_maincategory != $row['maincategory']) {
echo '<tr>
<td colspan="2" valign="top"><strong>';
echo $row['maincategory'];
echo '</strong></td>
</tr>';
}
echo "<tr>";
echo "<td>";
if ($row['amenity'] == $row['itemid'])
{
echo "<input type='checkbox' name='amenities[]' value='".$row['itemid']."' checked>".$row['smallcategory'];
}
else
{
echo "<input type='checkbox' name='amenities[]' value='".$row['itemid']."'>".$row['smallcategory'];
}
echo "</td>";
echo "</tr>";
// record what the previous category was
$previous_maincategory = $row['maincategory'];
}
?>
EDIT
Screenshot of current state:
Here is a possible solution:
/* Arrays will be used to store the property address and amenity ids */
/* in order to check against undesired duplicate echoing. */
$arr_address = [];
$arr_amenity_ids = [];
/* The four tables are joined here to get the required fields. */
/* Here, let $pid be the id of the property being considered: */
$s = "SELECT a.address as Address, b.title as Amenity, c.item as Item, b.id, c.id FROM properties a JOIN categories b JOIN items c JOIN propertyamenities d ON b.id = c.catid AND a.id = d.listingid AND c.id = d.amenityid AND a.id = '$pid';";
$q = mysqli_query($con,$s);
while(list($address, $amenity, $item, $amenity_id, $item_id) = mysqli_fetch_array($q)){
/* Can have a heading for the property address */
if(!in_array($address, $arr_address)){
echo '<h2>Property Address: '.$address.'</h2>'; $arr_address[] = $address;
}
/* A heading for each amenity once: */
if(!in_array($amenity_id,$arr_amenity_ids)){
echo '<strong>'.$amenity.'</strong>'; $arr_amenity_ids[] = $amenity_id;
}
echo '<br/><input type="checkbox" value="'.$item_id.'"/> '.$item;
}

Inner Join MYSQL Query Combining 3 tables

How can I combine 3 tables in a INNER JOIN?
The end result I am after is getting a list of CATEGORIES belonging to a PRODUCT - including the CATEGORY'S PARENT ID CATEGORY value (ie: Sneakers and Nike).
The CATEGORIES table and PRODUCTS table are joined in the PRODUCTS & CATEGORIES table. A product can belong to many categories and a category can have many products.
Here's more-or-less the setup I have in my database...
CATEGORIES TABLE:
CAT ID | PARENT ID | CATEGORY
1 | 0 | Sneakers
2 | 1 | Nike
3 | 2 | Jordan
PRODUCTS TABLE:
PROD ID
1
2
3
PRODUCTS & CATEGORIES TABLE:
CAT ID | PROD ID
1 | 0
1 | 1
2 | 3
I am running these queries and I am getting some results, but at the moment I am running 2 separate queries...
$q1 = "SELECT prodid, GROUP_CONCAT(catid SEPARATOR ' // ') as catid FROM products_categories group by prodid order by prodid";
$result1 = $conn->query($q1);
if ($result1->num_rows > 0) {
while($prods = $result1->fetch_assoc()) {
echo "Product Id:" . $prods["prodid"] . " ––> " . "Categories Id:" . $prods["catid"];
}
} else {
echo "0 results";
}
$q2 =
" SELECT `ID`.`category` as `IDName`, `LABEL`.`category` as `LabelName`, `LABEL`.`catid` as `LabelId`
FROM `categories` as ID
INNER JOIN `categories` as LABEL
ON `ID`.`catid` = `LABEL`.`parentid`";
$result2 = $conn->query($q2);
if ($result2->num_rows > 0) {
while($prods = $result2->fetch_assoc()) {
echo "ID# " . $prods["LabelId"] . " is called: ". $prods["LabelName"] . "<br>";
}
} else {
echo "0 results";
}
$conn->close();
I have tried adding another INNER JOIN with no luck in the results.
The end result I am after would be: PROD ID #0 belongs to Sneakers, Nike, Jordan.
Anyone can point me in the right direction?
Thank you so much,
Sergio
UPDATE - 10/11/16
The Query:
$q =
" SELECT PC.productid as productid, concat_WS('~',C1.category, C2.category, C3.category) as breadcrumb
FROM xcart_categories as C1
INNER JOIN xcart_products_categories as PC
ON C1.categoryid = PC.categoryid
LEFT JOIN xcart_categories as C2
ON C1.categoryid = C2.parentid
AND C1.parentid = 0
LEFT JOIN xcart_categories as C3
ON C2.categoryid = C3.parentid
WHERE C1.parentid = 0
";
The Fetch:
$result = $conn->query($q);
if ($result->num_rows > 0) {
while($prods = $result->fetch_assoc()) {
echo $prods['productid'] . ' Belongs in these categories: ' . $prods['breadcrumb'] . '<br>';
}
} else {
echo "0 results";
}
This assumes 3 levels of hierarchy no more and a separate join is needed to "put each record on the same line" so they can be combined into a single value result. I thin you were trying to use Group_concat but I can't see how that's going to work as you don't have a way to walk the hierarchy.
SELECT PC.ProductID, concat_WS('-',C1.Category, C2.Category, C3.Category) as breadcrumb
FROM categories C1
INNER JOIN ProductsCategories PC
on C1.categoryID = PC.CategoryID
LEFT JOIN categories C2
on c1.CategoryID = C2.ParentID
and C1.parentID = 0
LEFT Join Categories C3
on C2.CategoryID = C3.ParentID
WHERE C1.ParentID = 0
Working SQL Fiddle example ( this only supports 3 levels deep, but could be altered with added left joins to support a max level but not a undetermined max level..)
I see you're trying to use group concat to bring all the rows for the same product category.productID of 0 to the same line
However as 0 references catID of 1 it would only return "sneakers" on the inner join. You would need to traverse the tree (all of it) somehow, thus the above, or you have to take multiple trips to the db or use some sort of dynamic SQL or method mentioned in link in comments.
This would be fairly simple in SQL Server, Oracle or other Enterprise RDBMS systems, however without recursive queries or engine specific hierarchy queries, this is no easy feat in MySQL on a single trip.
Maybe I'm missing something so it may help to see the actual expected results for your sample data. What is the record set look like that you want back?

How to get result from 2 table and dont show duplicates

I have 2 tables
USERS
------------
id | user
1 | john
2 | George
3 | Andy
Text
--------------------------------
id | user |date |text
1 | 1 |2012/10/2 | ABC
2 | 3 |2012/11/2 | ABCD
3 | 2 |2012/12/2 | ABCDE
4 | 2 |2012/1/2 | ABCDE
In the Text table the user column gets the id from the Users table.
I have the following query:
$sql= mysql_query ("SELECT
user.id,
users.user,
CASE
WHEN text.date = CURDATE() THEN '1'
ELSE '0'
END AS 'today',
text.date,
text.text
FROM users user, text text
WHERE users.id = text.user;");
if (mysql_num_rows($sql)<=0)
echo "NO ENTRIES FOUND";
while ($row = mysql_fetch_assoc($sql))
{
$user =$row['user'];
$id =$row['id'];
$date2 =$row['date'];
if ($row['today'] == 1) { echo"
<div class='z'><a href='viewuser.php?id=$user'>$user</a></div><br>
"; } else { echo"
<div class='zx'><a href='viewuser.php?id=$user'>$user</a></div><br>
"; };
}
My question is:
How can I display the users whose date is equal with today's date, the first div(zx), and if not with the second div(z), comparing from 2 tables and display once if is it 2 times the user.
<!-- language: lang-sql -->
SELECT
u.id,
u.user,
CASE
WHEN t.date = CURDATE() THEN '1'
ELSE '0'
END AS 'today',
t.date,
t.text
FROM users u, text t
WHERE u.id = t.user;
Now to display the results just say
if ($row['today'] == 0) { class="zx"; } else { class="z"; }
Code is abbreviated, let me know if you need it detailed out.
Here is your updated code with my solution from above:
$sql = mysql_query ("
SELECT
u.id,
u.user,
CASE
WHEN MAX(t.date) = CURDATE() THEN '1'
ELSE '0'
END AS 'today'
FROM users u, text t
WHERE u.id = t.user
GROUP BY u.id, u.user");
if (mysql_num_rows($sql) <= 0)
echo "NO ENTRIES FOUND";
while ($row = mysql_fetch_assoc($sql))
{
$user =$row['user'];
$id =$row['id'];
if ($row['today'] == 1) {
echo "<div class='zx'><a href='viewuser.php?id=$id'>$user</a></div><br>";
} else {
echo "<div class='z'><a href='viewuser.php?id=$id'>$user</a></div><br>";
}
}

Checking users options from default

I have table:
user_id | song_id| points
--------|----------------
2 | 1 | 0
2 | 2 | 1
2 | 3 | 2
2 | 4 | 3
2 | 5 | 4
And I need to check if the user have changed the points value.
Therefore it should be something like:
while ($row = mysql_fetch_array($query)) {
$userID = $row['user_id'];
$songID = $row['song_id'];
$points = $row['points'];
if($songID-$points==1){
echo $userID."<br>";
}
But this will print out every occasion of userID where the song-id - points=1.
I need to print out only these user_id's that have all the values =1 and the username must echo'd only once.
EDIT:
SELECT DISTINCT user_id WHERE (song_id - points) = 1
This is half way there. This echo's user_ids' where the song_id - points = 1, but if the user is reordered (i use jQuery sortable) the list, then there can be some rows that is "song_id - points = 1".
My script must echo only these user_id-s, where users every song_id - points = 1, not only one
SELECT DISTINCT user_id FROM table WHERE (song_id - points) = 1
After edit:
SELECT table.user_id
FROM table
LEFT JOIN (
SELECT user_id, COUNT(*) AS C FROM table) AS T2
ON table.user_id = T2.user_id
WHERE (table.song_id - table.points) = 1
GROUP BY table.user_id
HAVING COUNT(*) = T2.C
You can first filter the users which has modified point values:
SELECT DISTINCT user_id FROM table
WHERE (song_id - points) != 1
Then you can use fetch the users which doesn't fit the above condition:
SELECT DISTINCT user_id FROM table
WHERE user_id NOT IN (
SELECT DISTINCT user_id FROM table
WHERE (song_id - points) != 1
)
According to your last edit this last SQL statement might work.
You can check a working example.
Here is what you're looking for:
select user_id from (
select user_id, if(song_id - points = 1, 0, 1) flag from t
) as S
group by user_id
having sum(flag) = 0
And here is a working example.
In case I didn't understand the requirements this shows all users who don't even have one row in which song_id - points != 1, i.e, all users who have all rows that match song_id - points = 1
Or maybe, if you prefer a different approach that might be more efficient:
select distinct t1.user_id from t t1
where not exists (
select * from t t2
where t2.song_id - t2.points != 1 and t1.user_id = t2.user_id
)
Here is the working example.
Not sure I understand the why of the situation, but a simple control-break structure will achieve the desired result ...
$old_id = '';
$good = false;
while($row = mysql_fetch_array($query)){
//check to see if we have a new user ...
if($row['user_id'] != $old_id){
//check to see if all values were == 1
if($good){
echo $old_id . '<br />';
}
//re-initialize variables
$good = true;
$old_id = $row['user_id'];
}
//if value != 1, we won't print the user ...
if($row['song_id'] - $row['points'] != 1){
$good = false;
}
}
//final end-of-loop condition ...
if($good){
echo $old_id . '<br />';
}
OK, here's a query that's a lot more simple than the join above:
SELECT user_id, sum(song_id) as song_total, sum(points) as point_total, count(*) AS cnt FROM table GROUP BY user_id
If the difference between song_id and points is 1 for every song, then the difference between the totals will equal the number of rows for that user ... so using this query:
while($row = mysql_fetch_array($query)){
if($row['cnt'] == ($row['song_total'] - $row['point_total'])){
echo $row['user_id'] . '<br />';
}
}

Categories