MySQLi Repeat Row Depending on Column Quantity Value - php

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.

Related

How to join and sum a column in two tables based on another column using mysqli and php (just using query)

I have 2 tables about blood bank:
donates
orders
in donates table I have 2 fields showing how many donations we have:
------------------------
| blood_group | amount |
------------------------
| A+ | 2 |
| B- | 3 |
| O+ | 4 |
| A+ | 3 |
| O+ | 1 |
in orders table I have 2 column that how many requests we submit based on blood group:
------------------------
| blood_group | amount |
------------------------
| A+ | 4 |
| B- | 3 |
| O+ | 4 |
| AB- | 6 |
My problem is I want to use mysqli query to get an array that show me this result based on these conditions:
show how many we need group by blood_group
if we don't need any blood_group or we don't have any request for that blood type show zero (not showing null)
not showing negative number for our blood shortage
I manage to do this so far:
<?php
$con = mysqli_connect("localhost", "root", "", "test");
// Check connection
if (mysqli_connect_errno()) {
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
$sql ="SELECT donates.blood_group as blood_group,
donates.amount as donates_amount,
orders.amount as orders_amount,
FROM `donates`
LEFT JOIN `orders`
ON donates.blood_group = orders.blood_group
GROUP BY donates.blood_group";
// Perform queries
$result = mysqli_query($con, $sql);
if (!$result = mysqli_query($con, $sql)) {
echo "SQLSTATE error: " . mysqli_sqlstate($con);
echo "<br>";
echo "SQLSTATE error: " . mysqli_error($con);
exit;
}
$result = mysqli_fetch_all($result, MYSQLI_ASSOC);
var_dump($result);
mysqli_close($con);
That query shows me sum of blood_groups but here is the main question:
So here are the main questions:
how to subtract (donates_amount and orders_amount)
how to make them positive (subtract which one first)
how to show the result even if one blood group is not presented on the other (full join)
Use union all and group by:
select blood_group, sum(donate_amount) as donate_amount,
sum(order_amount) as order_amount
from ((select blood_group, amount as donate_amount, 0 as order_amount
from donates
) union all
(select blood_group, 0 as donate_amount, amount as order_amount
from orders
)
) od
group by blood_group;
The only caveat is that a blood group needs to be in one of the tables. If you have a separate table of all of them, you should use that. For instance:
select bg.*,
coalesce(donate_amount, 0) as donate_amount,
coalesce(order_amount, 0) as order_amount
from blood_groups bg left join
(select blood_group, sum(amount) as donate_amount
from donates
group by blood_group
) d
on d.blood_group = bg.blood_group left join
(select blood_group, sum(amount) as order_amount
from donates
group by blood_group
) o
on o.blood_group = bg.blood_group ;
In either of these queries, you can get the difference using - and show negative numbers as 0 using greatest(). For instance:
greatest(sum(donate_amount) - sum(order_amount), 0)
To answer your first question :
how to subtract (donates_amount and orders_amount)
You must use SUM() function with a minus sign:
SUM(donates.amount - orders.amount);
this will subtract the total sum of two tables
But we have some problem here: you may have null values (because you may not have some of the blood groups present in one of tables) that give the wrong result. you must change the null values to zero with COALESCE() function:
SUM(COALESCE(donates.amount,0) - COALESCE(orders.amount,0))
We must extra check if the result does not equal to null:
COALESCE(SUM(COALESCE(donates.amount,0) - COALESCE(orders.amount,0)),0)
how to make them positive (subtract which one first)
And at last if you want to avoid negative numbers you must use mysqli math functions named ABS() that give you absulute value:
ABS(COALESCE(SUM(COALESCE(donates.amount,0) - COALESCE(orders.amount,0)),0))
so your query will look like this:
$sql = "SELECT donates.blood_group as blood_group,
COALESCE(donates.amount,0) as donates_amount,
COALESCE(orders.amount,0) as orders_amount,
ABS(COALESCE(SUM(COALESCE(donates.amount,0) - COALESCE(orders.amount,0)),0)) as needed_amount
FROM `donates`
LEFT JOIN `orders`
ON donates.blood_group = orders.blood_group
GROUP BY donates.blood_group";
how to show the result even if one blood group is not presented on
the other (full join)
In order to make full join you must use union with the invers form of your query. so that you find other records in orders table and unite the results into one results:
$sql = "SELECT donates.blood_group as blood_group,
COALESCE(donates.amount,0) as donates_amount,
COALESCE(orders.amount,0) as orders_amount,
ABS(COALESCE(SUM(COALESCE(donates.amount,0) - COALESCE(orders.amount,0)),0)) as needed_amount
FROM `donates`
LEFT JOIN `orders`
ON donates.blood_group = orders.blood_group
GROUP BY donates.blood_group
UNION
SELECT orders.blood_group as blood_group,
COALESCE(donates.amount,0) as donates_amount,
COALESCE(orders.amount,0) as orders_amount,
ABS(COALESCE(SUM(COALESCE(orders.amount,0) - COALESCE(donates.amount,0)),0)) as needed_amount
FROM `orders`
LEFT JOIN `donates`
ON orders.blood_group = donates.blood_group
GROUP BY orders.blood_group";

Get data from 2 tables and make a table in one query

I have two MySQL tables:
offers:
id | rid | name
------------------------------
1 | 1234 | mary
2 | 1235 | john
3 | 5342 | liam
And
geo_in_off:
offer_id | geo_id
------------------------------
1 | 1234
2 | 1235
3 | 5342
I need to make a table on my website looging like:
1(number) | 1234(rid) | name(name) | 1(geo_id)
But I got MySQL troubles.
My code is:
require('../config.php');
echo "<table class=\"offer-table\">";
echo "<tr><th id=\"off_col_num\">№</th><th id=\"off_col_id\">offer ID</th><th id=\"off_col_name\">Название</th><th id=\"off_col_geo\">Geo</th></tr>";
$i = 1;
$sql ="SELECT * FROM `offers` ORDER BY `rid` JOIN geo_in_off ON geo_in_off.id = offers.id";
$result = mysql_query($sql) or die(mysql_error());
while ($row=mysql_fetch_assoc($result)) {
echo "<tr><td>" . $i . "</td><td>" . $row['rid'] . "</td><td>" .
$row['name'] . "</td><td>" .$i . "</td></tr>" ;
$i++;
}
echo "</table>";
I've got problems with MySQL synaxis and the logic how to get the data from 2 tables and give the result in one "while loop".
So I need to join 2 queries in one:
$sql ="SELECT * FROM `offers` ORDER BY `rid`";
$sql ="SELECT `geo_id` FROM `geo_in_off` WHERE `offer_id` = '$each_offer_id_from_offers'";
And optional but not necessary:
$sql ="SELECT `name` FROM `geo` WHERE `id` = '$geo_id_got_from_table_geo_in_off'";
And get a table where I get the offer id, the offer name and the offer geo.
Try this query, it does help you (full join query)
SELECT offers.*, geo_in_off.geo_id FROM offers,
geo_in_off WHERE offers.id=geo_in_off.offer_id
Change
$sql ="SELECT * FROM `offers` ORDER BY `rid` JOIN geo_in_off ON geo_in_off.id = offers.id";
To
$sql ="SELECT * FROM `offers` JOIN `geo_in_off` ON `geo_in_off`.`offer_id` = `offers`.`id` ORDER BY `offers`.`rid`";
1) ORDER BY order is wrong in your query
SELECT column1, column2, ...
FROM table_name
ORDER BY column1, column2, ... ASC|DESC;
2) Instead of geo_in_off.id = offers.id use geo_in_off.offer_id = offers.id
Try this query:
$sql = 'SELECT of.id, of.rid, of.name, geo.id
FROM of.offers
INNER JOIN of.id = geo.offer_id
WHERE offer_id = \''.$each_offer_id_from_offers.'\' order by of.rid'

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?

PHP, MySQL Junction Table displays information incorrectly

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

Sum query not working properly properly sql and php

Why this query instead of displaying the sum of points for each user, it display the sum of all together
I have written an SQL query that displays the sum of all point for all users, whereas I would like it to: display the sum of points for each user.
The table that I have written contains the following:
id | Score
1 | 20
2 | 30
2 | 50
1 | 10
Total table :
id | points
1 | 30
1 | 40
What I want is to add the score for user(1) = 30 and user(2) = 80
Id: 1 = 30 = Fail
Id: 2 = 80 = Pass
The query I have written :
$query = "SELECT SUM(sg.score) as sum, SUM(a.total) as suma FROM points as sg, totalpoints as a
WHERE a.id = 1 GROUP BY sg.id";
And related PHP code is as follows:
<?php
foreach($rowsum as $rowsm):
if( ' '. htmlentities($rowsm['sum']) . '' > 50 )
echo 'Pass';
else if( ' '. htmlentities($rowsm['sum']) . '' >= 40 )
echo 'Failed';
echo ' ' . htmlentities($rowsm['sum'], ENT_QUOTES, 'UTF-8') . '<br>';
endforeach;
?>
You need to group by the users ID:
SELECT SUM(score) as sum FROM points GROUP BY id ORDER BY id
You also have an incorrect WHERE clause
WHERE id=id
isn't needed.
I guess you should look forward using the GROUP BY clause :
SELECT
SUM(score) AS sum
FROM
points
GROUP BY
id
ORDER BY
id
You've omitted the GROUP BY clause:
$query = "SELECT SUM(score) as `sum` FROM points GROUP BY id ORDER BY id";
Your WHERE clause wasn't needed.
You need to do group by as below and it will give you the sum of scores for each user
SELECT SUM(score) as sum FROM points
group by id
ORDER BY id
If you need to find it for a specific user just add a where condition as
SELECT SUM(score) as sum FROM points
where id = 1
The above will give the sum of scores for id = 1 and can change for other values as needed.

Categories