PHP MySQL Insert / Select multiple one to Many - php

I've been struggling with this one for a while, and have searched various sources (books / the web / this site) but not found a solution yet. I haven't been using PHP / MySQL for that long, so I may not know the relevant vernacular to be able to target my search. Anyway...
I have a basic order management system. The customer can enter multiple lines for an item they want tested, each line having the item to be tested, and the suite of tests required. I then need to expand each of these items into its constituent tests for the test lab to manage its workflow. I need to be able to tie the test back to the order item, and the order.
I have an order table where the overall order is captured with an order_id field (auto-incremented) and some other information (who placed the order etc). I then populate an order item table with the order_id from the order table and each item / test suite combination that the customer wants, each order item has an order_item_id (auto incremented).
$sql1="INSERT INTO tbl_order(od_date, od_customer_id)
SELECT ct_date, ct_customer_id
FROM tbl_cart
WHERE ct_customer_id='$customerID' AND ct_session_id='$sid'";
mysql_query($sql1);
// get order_id
$orderid=mysql_insert_id();
// save order details to tbl_order_item
$sql2="INSERT INTO tbl_order_item(cat_code, pd_code, pd_description, pc_code, smpl_type, test_suite_name)
SELECT cat_code, pd_code, pd_description, pc_code, smpl_type, test_suite_name
FROM tbl_cart
WHERE ct_customer_id='$customerID' AND ct_session_id='$sid'";
mysql_query($sql2);
// update date fields
$sql3="UPDATE tbl_order SET od_date='$date' WHERE ct_date='0000-00-00 00:00:00'";
$sql4="UPDATE tbl_order SET od_username='$username' WHERE od_username=''";
mysql_query($sql3);
mysql_query($sql4);
// set the order id in the order item and lab item tables
$sql5="UPDATE tbl_order_item SET od_id='$orderid' WHERE od_id='0'";
That all works, however I'm now stuck when I try and take each each order item and expand it into its relevant tests. I have created a table which shows the test_suite / test_name combination, but I can't get the output inserted into the lab_item table.
//$sql6="UPDATE tbl_lab_item SET od_id='$orderid' WHERE od_id=''";
mysql_query($sql5);
// Expand Suite into tests in lab item table
$sql7="INSERT INTO tbl_lab_item(od_id,test_name)
VALUES ('$orderid',(SELECT test_name FROM tbl_suite_select WHERE tbl_order_item.test_suite_name=tbl_suite_select.test_suite_name))";
mysql_query($sql7);
Should I be doing this straight into the database, or should I pull the data out into variables first? If so, how?
Many thanks in advance.
UPDATE
I've solved the problem now. Thanks for the input, if anyone is interested, this is was the query that eventually solved it. It was all hinged on calling the right table / cell in the Select for the final table. I was missing a field, so it didn't have a reference.
// Insert individual tests into tbl_lab_item
$sql6="INSERT INTO tbl_lab_item(od_item_id, test_suite_name,test_name)
SELECT tbl_order_item.od_item_id,tbl_order_item.test_suite_name,tbl_suite_select.test_name
FROM tbl_order_item,tbl_suite_select
WHERE tbl_order_item.test_suite_name=tbl_suite_select.test_suite_name";
mysql_query($sql6);

I'm not sure I understand the question fully, but here are some improved queries (using pdo). I think the main point I added was the join statement in the last query. I also combined some of your previous queries to add the rows in one go (instead of inserting than updating). I didn't test anything.
$sqlStmt = $pdo->prepare("
INSERT INTO tbl_order
( od_date
, od_customer_id
, od_date
, od_username)
SELECT
ct_date
, ct_customer_id
, ? AS od_date
, ? AS od_username
FROM tbl_cart
WHERE ct_customer_id = ?
AND ct_session_id = ?
");
$sqlStmt->execute(array(
$date,
$username,
$customerID,
$sid
));
$orderid = $pdo->lastInsertId();
$sqlStmt = $pdo->prepare("
INSERT INTO tbl_order_item
( cat_code
, pd_code
, pd_description
, pc_code
, smpl_type
, test_suite_name
, od_id)
SELECT
cat_code
, pd_code
, pd_description
, pc_code
, smpl_type
, test_suite_name
, ? AS od_id
FROM tbl_cart
WHERE ct_customer_id = ?
AND ct_session_id = ?
");
$sqlStmt->execute(array(
$orderid,
$customerID,
$sid
));
$sqlStmt = $pdo->prepare("
INSERT INTO tbl_lab_item
( od_id
, test_name)
SELECT
? AS od_id
, tss.test_name
FROM tbl_suite_select tss
INNER JOIN tbl_order_item toi
ON toi.test_suite_name = tss.test_suite_name
WHERE toi.od_id = ?
");
$sqlStmt->execute(array(
$orderid,
$orderid
));

This should transform your code into working code:
$sql7="INSERT INTO tbl_lab_item(od_id,test_name)
SELECT '$orderid', test_name FROM tbl_suite_select
WHERE tbl_order_item.test_suite_name=tbl_suite_select.test_suite_name";
I don't quite get whether the condition applied with where is sufficient, though.

Related

How to merge two UPDATE queries into one, different WHERE and SET?

I was wondering if it's possible to combine these two queries as they do not work separately (one of the two only works). They are
$addquery = "UPDATE winners SET mem_name='$addname' WHERE mem_name='$deletename'";
$addresult= mysqli_query($connect, $addquery);
$query = "UPDATE winners INNER JOIN members SET winners.mem_id = members.mem_id
WHERE winners.mem_name = members.mem_name";
$result = mysqli_query($connect, $query);
Can this be done in just one query? Thank you!!!
I am not saying you should do it, but judging from the flow of the code you provided, this is how you could do it.
UPDATE winners w
SET w.mem_name = '$addname'
, w.mem_id = IFNULL(SELECT m.mem_id
FROM members AS m
WHERE m.mem_name = '$addname'
ORDER BY m.mem_id DESC
LIMIT 1
, w.mem_id
)
WHERE w.mem_name = '$deletename'
;
Note, the ORDER BY is technically optional; your question does not state whether mem_name is guaranteed unique in members. If it is unique, the order by should not be needed; if it is not, it at least adds some consistency to the expected value retrieved.
If you have control over the database design, I would suggest removing mem_name from winners altogether. It is/would be redundant data if you were managing the relation primarily by mem_id to begin with.

PHP Deleting multiple rows with returning values to another table

I have a code here that deletes a single row of data from sales_order and then returns the value of the qty to the products table.
<?php
include('connect.php');
//get values from href
$id=$_GET['id'];
$invoice=$_GET['invoice'];
$qty=$_GET['qty'];
$product=$_GET['product'];
//returns quantity to stock
$sql = "UPDATE products
SET stock=stock+?
WHERE productid=?";
$q = $db->prepare($sql);
$q->execute(array($qty,$product));
//execute query
$result = $db->prepare("DELETE FROM sales_order WHERE transaction_id= :id");
$result->bindParam(':id', $id);
$result->execute();
header("location:pos.php?invoice=$invoice");
?>
Now what I'm aiming to do here is to have a reset button that will delete all rows with the same invoice number from sales_order and then each row returns the quantity value to the products table. I can't seem to find a way to do it. Can anyone help me?
You can do an UPDATE with a JOIN:
UPDATE products AS p
JOIN sales_order AS s on s.productid = p.productid
SET p.stock = p.stock + s.quantity
WHERE s.invoice_number = :invoice
Then you can delete all the sales_order rows with that invoice number. You should use a transaction to ensure that this is all done atomically.
I'm making some assumptions above about the schemas of the tables, you'll need to adjust to your actual column names.
I think you can go with an array concept. I think its simple to retrieve all the required values using select query before deletion. Then you can save it to the required tables. The you can delete the values

Perform query on existing SQL result? Find result from subset of SQL result

I have a script that goes through all order history. It takes several minutes to print the results, but I noticed I perform several SQL statements that are similar enough I wonder if you could do another query on an existing SQL result.
For example:
-- first SQL request
SELECT * FROM orders
WHERE status = 'shipped'
Then, in a foreach loop, I want to find information from this result. My naive approach is to perform these three queries. Note the similarity to the query above.
-- grabs customer's LTD sales
SELECT SUM(total) FROM orders
WHERE user = :user
AND status = 'shipped'
-- grabs number of orders customer has made
SELECT COUNT(*) FROM orders
WHERE user = :user
AND status = 'shipped'
AND total != 0
-- grabs number of giveaways user has won
SELECT COUNT(*) FROM orders
WHERE user = :user
AND status = 'shipped'
AND total = 0
I end up querying the same table several times when the results I seek are subsets of the first query. I'd like to get information from the first query without performing more SQL calls. Some pseudocode:
$stmt1 = $db->prepare("
SELECT * FROM orders
WHERE status = 'shipped'
");
$stmt1->execute();
foreach($stmt1 as $var) {
$username = $var['username'];
$stmt2 = $stmt1->workOn("
SELECT SUM(total) FROM this
WHERE user = :user
");
$stmt2->execute(array(
':user' => $username
));
$lifesales = $stmt2->fetchColumn();
$stmt3 = $stmt1->workOn("
SELECT COUNT(*) FROM this
WHERE user = :user
AND total != 0
");
$stmt3->execute(array(
':user' => $username
));
$totalorders = $stmt3->fetchColumn();
$stmt4 = $stmt1->workOn("
SELECT COUNT(*) FROM this
WHERE user = :user
AND total = 0
");
$stmt4->execute(array(
':user' => $username
));
$totalgaws = $stmt4->fetchColumn();
echo "Username: ".$username;
echo "<br/>Lifetime Sales: ".$lifesales;
echo "<br/>Total Orders: ".$totalorders;
echo "<br/>Total Giveaways: ".$totalgaws;
echo "<br/><br/>";
}
Is something like this possible? Is it faster? My existing method is slow and ugly, I'd like a quicker way to do this.
We could do one pass through the table to get all three aggregates for all users:
SELECT s.user
, SUM(s.total) AS `ltd_sales`
, SUM(s.total <> 0) AS `cnt_prior_sales`
, SUM(s.total = 0) AS `cnt_giveaways`
FROM orders s
WHERE s.status = 'shipped'
GROUP
BY s.user
That's going to be expensive on large sets. But if we are needing that for all orders, for all users, that's likely going to be faster than doing separate correlated subqueries.
An index with leading column of user is going to allow MySQL to use the index for the GROUP BY operation. Including the status and total columns in the index will allow the query to be satisfied entirely from the index. (With the equality predicate on status column, we could also try an index with status as the leading column, followed by user column, then followed by total.
If we only need this result for a small subset of users e.g. we are fetching only the first 10 rows from the first query, then running a separate query is likely going to be faster. We'd just incorporate the condition WHERE s.user = :user into the query, as in the original code. But run just the one query rather than three separate queries.
We can combine that with the first query by making it into an inline view, wrapping it in parens and putting into the FROM clause as a row source
SELECT o.*
, t.ltd_sales
, t.cnt_prior_sale
, t.cnt_giveaways
FROM orders o
JOIN (
SELECT s.user
, SUM(s.total) AS `ltd_sales`
, SUM(s.total <> 0) AS `cnt_prior_sales`
, SUM(s.total = 0) AS `cnt_giveaways`
FROM orders s
WHERE s.status = 'shipped'
GROUP
BY s.user
) t
ON t.user = o.user
WHERE o.status = 'shipped'
I'm not sure about that column named "prior" sales... this is returning all shipped orders, without regard to comparing any dates (order date, fulfillment date, shipment date), which we would typically associate with a concept of what "prior" means.
FOLLOWUP
noticing that the question is modified, removing the condition "status = 'shipped'" from the count of all orders by the user...
I will note that we can move conditions from the WHERE clause into the conditional aggregates.
Not that all these results are needed by OP, but as a demonstration...
SELECT s.user
, SUM(IF(s.status='shipped',s.total,0)) AS `ltd_sales_shipped`
, SUM(IF(s.status<>'shipped',s.total,0)) AS `ltd_sales_not_shipped`
, SUM(s.status='shipped' AND s.total <> 0) AS `cnt_shipped_orders`
, SUM(s.status='canceled') AS `cnt_canceled`
, SUM(s.status='shipped' AND s.total = 0) AS `cnt_shipped_giveaways`
FROM orders s
GROUP
BY s.user
Once the results are returned from the database, you can not run an SQL on top of them. However you can store them in a temporary table, to reuse them.
https://dev.mysql.com/doc/refman/8.0/en/create-temporary-table.html
https://dev.mysql.com/doc/refman/8.0/en/create-table-select.html
https://dev.mysql.com/doc/refman/8.0/en/insert-select.html
You need to create a temporary table, and insert all the data from the select statement, and then you can run queries on that table. Not sure if it would help much in your case.
For your particular case you can do something like:
select user, (total = 0) as is_total_zero, count(*), sum(total)
from orders
where status = 'shipped'
group by user, total = 0
However you would have to do some additional summing to get the results of the second query which gives you the sums per user, as they would be divided into two different groups with a different is_total_zero value.

Insert tables in PHP

I have two tables, Order and OrderDetails.
Order Table
OrderID
Order_Details_Id
Date
Cust_Id
Order Details
Order_Details_Id
prod_name
prod_amt
OrderID
Basically i want to insert several order details in one order table. My issue is i am not quite sure how to set OrderID in the Order table to the OrderID in Order Details in PHP. I have made an attempt at it with no luck, this is what i have so far :
$order = "INSERT INTO Order (orderID, cust_id, date)
VALUES ('$cust_id, '23/11/2017')";
$DBcon->exec($order);
foreach ($prodname as $prodname) {
foreach ($prodamount as $prodamount) {
$od = "INSERT INTO Order_Details (OrderDetails_id, prod_name, prod_amt,orderID)
VALUES ('$prodname' , '$prodamount',orderID(?) )";
$DBcon->exec($od);
}
}
Any help would be greatly appreciated, i am relatively new to using PHP and SQL!
Use mysqli::$insert_id to get last inserted id
$DBcon->exec($order);
$orderID = $DBcon->insert_id;
Now use this varibale instead of orderID(?) in your Second INSERTcode.

Copy multiple rows from one table to another, directly in MySQL?

I am trying to copy values directly from one mysql table to another... but the trouble is, there are many rows to copy, so that sub-select statement will return many rows but the param :order_id is just written once.
Given that problem, I'm having trouble figuring out how to write this process:
# 1: Create new Order & Copy the cart basket to the Order basket
$order_id = addOrder($customer_id);
if ($order_id > -1) {
$sql = "INSERT INTO tbl_order_basket(order_id, product_id, product_name,
basket_qty, basket_price, subtotal)
VALUES (:order_id, (SELECT (cart.product_id, product.name,
cart.cart_qty, cart.cart_price, cart.subtotal)
FROM tbl_shopping_cart AS cart
INNER JOIN tbl_product AS product
ON cart.product_id = product.id
WHERE cart.php_sesskey = :session_id)
)
WHERE order_id=:order_id;";
}
Here is the data I'm trying to copy:
ORDER BASKET CART BASKET PRODUCTS
FK order_id FK php_sesskey
FK product_id <-- FK product_id === PK id
product_name <-- <-- name
basket_qty <-- cart_qty
basket_price <-- cart_price # <-- : copy to
subtotal <-- subtotal # === : join on
How should I go about doing this?
Note: I'm using PHP 5 and MySQL 5.5
Maybe you can use this one?
INSERT INTO ... SELECT ... FROM ... WHERE ...
To create the query, first make a SELECT which gives you all the fields which you want to insert. Then prepend INSERT INTO <table> and you're done.
Look into SELECT INTO TABLE
INSERT INTO tbl_temp2 (fld_id)
SELECT tbl_temp1.fld_order_id
FROM tbl_temp1 WHERE tbl_temp1.fld_order_id > 100;
Try using the query below; this type of syntax has worked for me in the past. Since there is no WHERE in a MySQL INSERT statement (manual) I have removed that part. Also, do not use the VALUES keyword when doing an INSERT...SELECT (manual).
$sql = '
INSERT INTO tbl_order_basket(order_id, product_id, product_name,
basket_qty, basket_price, subtotal)
SELECT :order_id, cart.product_id, product.name,
cart.cart_qty, cart.cart_price, cart.subtotal
FROM tbl_shopping_cart AS cart
INNER JOIN tbl_product AS product
ON cart.product_id = product.id
WHERE cart.php_sesskey = :session_id)
';

Categories