Please help with my database design or sql optimization:
I have two tables stock and stock_tally. The stock table contain the list of items while the stock_tally contains all transactions on the items.
My Queries?
Add new product
add product to stock table
add same to stock_tally
Issue product
remove stock from stock table
enter code here retrieve stock_level from stock table
add the stock_level and the quantity to issue into stock_tally
Issue code
try{
$this->db->beginTransaction();
$q1 = $this->db->prepare("UPDATE stock SET stock_level =? where id=?");
$res1 = $q1->execute(array($qty, $id ));
$q2 = $this->db->prepare("SELECT * FROM stock WHERE id= ?");
$q2->bindValue(1, $id);
$q2->execute();
$res2 = $q2->fetch();
$q3 = $this->db->prepare("INSERT INTO stock_TALLY (id_fk, stocl_level, issues, update) VALUES (?,?,?,?)" );
$res3 = $q1->execute(array($id,$res2,0, $qty));
if($res2 && $res3){
$this->db->commit();
return true;
}
}catch(PDOException $e){
$this->db->rollBack(); //echo $e->getMessage();
return false;
}
The first thing you would do when designing a database is to write down clearly what it should be able to do, now, and possibly in the near future. You haven't done that, or at least you haven't told us. How do you expect us to tell you whether your database design and sql are optimal?
Related
I'm using Phalcon and I have a database in MySql. I have three tables in the database:
user: id, name, sold
company: id, name, cost
transactions: id_company, id_user, cost
An user has to do a transaction if has enough money (sold). So I have to do it:
Step 1:
retrieve the sold of the user:
select sold
from user
where id='Charlie'
Step 2:
retrive the cost from the company:
select cost
from company
where id='Tango'
Step 3:
to check if the user has enough money:
if (sold-cost >= 0)
create the transaction
else
do not create the transaction.
My question is:
Is there a way in order to block the db in order to do the three steps without the db could change?
I would like do this:
lock db
step 1
step 2
step 3
unlock db
But I have not found a solution for it.
I'm not sure how it implemented in Phalcon framework, but PDO extension implements transactions that can be helpful here:
<?php
$pdo->beginTransaction();
$stmt = $pdo->prepare('select sold from user where id= ?');
$stmt->execute(['Charlie']);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$sold = $row['sold'];
$stmt = $pdo->prepare('select cost from company where id= ?');
$stmt->execute(['Tango']);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$cost = $row['cost'];
printf("Sold: %d, Cost: %d", $sold, $cost);
if ($sold >= $cost) {
//reduse user sold
$stmt = $pdo->prepare('update user set sold = sold - ? where id= ?;');
$stmt->execute([$cost, 'Charlie']);
// write transaction
$stmt = $pdo->prepare('insert into transactions values (?, ?, ?);');
$stmt->execute(['Charlie', 'Tango', $cost]);
$pdo->commit();
} else {
$pdo->rollBack();
}
$stmt = $pdo->prepare('select * from transactions');
$stmt->execute();
$transactions = $stmt->fetchAll(PDO::FETCH_ASSOC);
print_r($transactions);
PHP PDO fiddle here
I want to update the stock quantities of my products in the DB after a purchase.
My code already works fine, but I want to know if there is a best way to do, with only one SQL statement?
// All the product in the member's cart
if ($stmt = $conn->prepare("SELECT product_id, quantity FROM tbl_cart WHERE member_id = ?")) {
$stmt->bind_param("i", $memberId);
$stmt->execute();
$result = $stmt->get_result();
$stmt->close();
$cartItem = $result->fetch_all(MYSQLI_ASSOC);
// Set the quantity after purchase
foreach ($cartItem as $key => $item) {
$stmt = $conn->prepare("UPDATE tbl_product SET stock = stock-? WHERE id = ?");
$stmt->bind_param("ii", $item['quantity'], $item['product_id']);
$stmt->execute();
$stmt->close();
}
}
I don't know if this is better way but you may try this Sql statement.
UPDATE tbl_product
SET stock = stock - ( SELECT quantity FROM tbl_cart
WHERE product_id = tbl_product.id AND member_id = ? )
WHERE id IN ( SELECT product_id FROM tbl_cart WHERE member_id = ? )
You can try using trigger function. Look here: mysql after insert trigger which updates another table's column
You could update other column when order is placed.
In my opinion, the trigger function is not the best option, because it will move logic to other place.
But it is one of the alternative, that you ask for.
That's the solution I found. I think it's just more clean.
$stmt = $conn->prepare("UPDATE tbl_product AS p
JOIN tbl_cart AS c
ON p.id = c.product_id
SET p.stock = p.stock-c.quantity
WHERE c.member_id = ?");
$stmt->bind_param("i", $memberId);
$stmt->execute();
$stmt->close();
Thank you for your help.
Hi guys I have this part of a code:
foreach($cartItems as $item){
$sql .= "INSERT INTO order_items (order_id, product_id, quantity) VALUES ('".$orderID."', '".$item['id']."', '".$item['qty']."');";
$newstock = $item['stock']-$item['qty'];
$sql1 .= "UPDATE product SET stock='".$newstock."' WHERE ID='".$item['id']."';";
}
// insert order items into database
$insertOrderItems = $conn->multi_query($sql);
$updateProductQty = $conn->multi_query($sql1);
and I am having problem about updating a table "product".. the first query is 1 and is ok, but sql1 output is OK but nothing happens, I tried to update this query manually in phpmyadmin and it works there.. Do you see anything what I dont? Thanks
edit: so it should look more like this?
$conn->begin_transaction(MYSQLI_TRANS_START_READ_WRITE);
foreach($cartItems as $item) {
$newstock = $item['stock']-$item['qty'];
$stmt = $conn->prepare('update product set stock = ? where ID = ?');
$stmt->bind_param('ii',$newstock,$item['id']);
$stmt->execute();
}
$conn->commit();
$conn->close();
this?
I am having a little issue getting data from a table with while loop. What i want to do is simple I want to take all data from table cart with cookie value from table orders that matches a cookie value and query tables cart to extract data that matches the cookie value in the cart table and place them in table orders_final . Now this is . Now the final part after querying cart table with cookie value gotten from order table, i now want to place the data into orders_final table with all that matches that cookie value from order and cart
$zomo = $_COOKIE['shopa']; // this is the cookie that is stored in the cart table and updated when the transaction is successful
$get_products = "SELECT * FROM `cart` WHERE cookie_value = '$zomo'";
$limo = mysqli_query($con, $get_products);
while($colo = mysqli_fetch_array($limo)){
$product_id = $colo['product_id'];
$order_quantity = $colo['order_quantity'];
$cookie_value = $colo['cookie_value'];
//var $dance is when i update the table with data after payment and data gotten from my payment processing company
$dance = "UPDATE `orders` SET `status`='$r_status',`time`='$r_time',`date`='$r_date',`reference`='$r_reference',`transaction_status`='$r_transaction_status',`transaction_method`='$r_transaction_method',`final_price`='$r_final_price',`order_id`='$r_order_id',`currency`='$r_currency',`referrer`='$r_referrer' WHERE cookie_bought = '$zomo'";
$uii = mysqli_query($con, $dance);
if ($uii){
//this variable insert is where i want to insert all data gotten from cart table above and insert into orders_final, where order table holds the cookie value which was created during shopping which is cookie name shopa held in the variable zomo
$insert = "INSERT INTO `orders_final`(`product_id`, `cookie_value`, `trx_id`, `order_quantities`) VALUES ('$product_id','$zomo','$r_reference','$order_quantity')";
$bena = mysqli_query($con, $insert);
if ($bena){
$delc = "DELETE FROM `cart` WHERE cookie_value = '$zomo'";
$tipee = mysqli_query($con, $delc);
if ($tipee){
perform_success();
}
}
}
}
A better approach is to run fewer queries, that do more. Instead of selecting an entire table and looping over it to run up to 3 queries per iteration (which quickly becomes a lot of queries!), you can use a INSERT INTO...SELECT query instead. Using a transaction, it's also possible to ensure that everything goes through before committing the changes - so you don't end up deleting something that didn't transfer properly.
The code below has been altered to reduce the amount of queries down to three (and none is looped!), and usage of prepared statements has been implemented.
$stmt = $con->prepare("INSERT INTO orders_final (`product_id`, `cookie_value`, `trx_id`, `order_quantities`)
SELECT product_id, ?, order_quantity, ?
FROM cart
WHERE cookie_value=?");
$stmt->bind_param("sss", $zomo, $r_reference, $zomo);
if ($stmt->execute()) {
$stmt->close();
$stmt = $con->prepare("UPDATE orders
SET status=?, time=?, date=?, reference=?, transaction_status=?,
transaction_method=?, final_price=?, order_id=?,
currency=?, referrer=?
WHERE cookie_bought=?");
$stmt->bind_param("sssssssssss", $r_status, $r_time, $r_date, $r_reference, $r_transaction_status, $r_transaction_method, $r_final_price, $r_order_id, $r_currency, $r_referrer, $zomo);
$dance = "UPDATE `orders` SET `status`='$r_status',`time`='$r_time',`date`='$r_date',
`reference`='$r_reference',`transaction_status`='$r_transaction_status',`transaction_method`='$r_transaction_method',`final_price`='$r_final_price',`order_id`='$r_order_id',`currency`='$r_currency',`referrer`='$r_referrer' WHERE cookie_bought = '$zomo'";
$stmt = $con->prepare("DELETE FROM cart WHERE cookie_value=?");
$stmt->bind_param("s", $zomo);
$stmt->execute();
$stmt->close();
}
mysqli::prepare()
mysqli_stmt::bind_param()
MySQL INSERT INTO..SELECT
So pretty much my issue is that I need to send multiple SQL entries using information based on another SQL entry.
I've simplified the code down that I was using so it's easily understandable.
$sql = mysql_query("SELECT product FROM `cart` WHERE username = '".$user."' LIMIT 10");
while ($rowcart = mysql_fetch_row($sql)) {
$sendorder = "INSERT INTO Orders (order_id, product) VALUES ('NULL', '".$rowcart[0]."')";
mysql_query($sendorder);
}
When I ran it, it had failed to work; so I tried to echo $sendorder to see exactly what was sending and it turns out it's copying the INSERT INTO part on each entry, instead of just copying the values.
Example output:
INSERT INTO Orders (order_id, product) VALUES ('NULL', 'Cakes')
INSERT INTO Orders (order_id, product) VALUES ('NULL', 'Sweets')
INSERT INTO Orders (order_id, product) VALUES ('NULL', 'Cakes')
INSERT INTO Orders (order_id, product) VALUES ('NULL', 'Brownies')
INSERT INTO Orders (order_id, product) VALUES ('NULL', 'Cakes')
You said, "I need to send multiple SQL entries using information based on another SQL entry." The following approach is more efficient than what you are attempting. Note that I use neither php nor mysql so I might have some syntax errors.
insert into orders
(product)
select product
from cart
where username = $user
As far as the limit 10 goes, if you want to restrict the person to 10 items, you should do something to ensure that only 10 rows go into the cart table.
Mysqli example
<?php
$mysqli = new mysqli('localhost', 'my_user', 'my_password', 'my_database');
/* check connection */
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
$stmt = $mysqli->prepare("SELECT product FROM `cart` WHERE username = ? LIMIT 10");
$stmt->bind_param('s', $user);
$stmt->execute();
$stmt->bind_result($product);
while($stmt->fetch()) {
$tvalue[] = $product;
}
$stmt = $mysqli->prepare("INSERT INTO Orders (product) VALUES (?)");
$stmt->bind_param("s", $one);
foreach ($tvalue as $one) {
$stmt->execute();
}
printf("%d Row inserted.\n", $stmt->affected_rows);
/* close statement and connection */
$stmt->close();
/* close connection */
$mysqli->close();
?>
If i understand correctly, what you want to do is to send an unique query, you can do this by appending every value to be inserted at the end of a single query string:
<?php
// code
$sql=mysql_query("SELECT product FROM cart WHERE username='".$user."' LIMIT 10");
$result=mysql_query($sql);
if(mysql_num_rows($result)) {
$rowcart=mysql_fetch_row("$result");
$sendorder="INSERT INTO Orders (order_id, product) VALUES ('NULL', '".$rowcart[0]."')";
while($rowcart=mysql_fetch_row($result))
$sendorder.=", ('NULL', '".$rowcart[0]."')";
mysql_query($sendorder);
}
// code
?>
I assume, your order_id is a primary key, and auto_increment. You can leave that:
INSERT INTO Orders (product) VALUES ('Cakes')
or if you really want to insert it, then use
INSERT INTO Orders (order_id, product) VALUES (NULL, 'Cakes')
if you add quotes ' around it, then it will be parsed as a string. And since, that is not a string, but integer, it will cause syntax error.
You should be able to do this in a single SQL statement something like this..
INSERT INTO Orders(order_id,product)
SELECT null,product
FROM cart
WHERE username = $name
LIMIT 0,10
To refactor even further I would suggest you probably dont need to insert the null value just do:
INSERT INTO Orders(product)
SELECT product
FROM cart
WHERE username = $name
LIMIT 0,10
If your table is structured to allow NULL in the order_id col then this will be populated as null by default.
And as Dan just said doesnt seem to be much point putting a limit on either