How to improve the fields name - mysql

I am trying to improve the fields name to be more obvious what it means.. is the fields name ok? or what can be changed to make it more obvious?
mysql> select * from order_items;
+----+----------+--------+----------+------------+-------+
| id | order_id | name | quantity | item_price | total |
+----+----------+--------+----------+------------+-------+
| 1 | 5 | Item 1 | 2 | 3.00 | 6.00 |
| 2 | 5 | Item 2 | 1 | 2.00 | 2.00 |
+----+----------+--------+----------+------------+-------+
mysql> select * from orders;
+----+---------+---------+-----------------+---------------+----------------+----------+---------------+------------+----------------+------------+----------------------+--------------------+--------------------+----------------------+
| id | shop_id | user_id | shipping_method | shipping_fees | payment_method | card_fee | commision_fee | item_total | customer_total | shop_total | shop_total_remaining | our_commission_net | our_commission_vat | our_commission_gross |
+----+---------+---------+-----------------+---------------+----------------+----------+---------------+------------+----------------+------------+----------------------+--------------------+--------------------+----------------------+
| 5 | 29 | 9 | delivery | 1.00 | card | 0.50 | 13 | 8.00 | 9.50 | 9.00 | 7.83 | 1.17 | 0.23 | 1.40 |
+----+---------+---------+-----------------+---------------+----------------+----------+---------------+------------+----------------+------------+----------------------+--------------------+--------------------+----------------------+
Fields name description:
item_total Total cost from order_items.order_id = 5
customer_total Total cost for customer to pay (item_total + card_fee + shipping_fee)
shop_total Total order for the shop (item_total + shipping_fees)
shop_total_remaining Total remaining to pay back to shop (shop_total - our_commission_net)
our_commission_net Commission I will make (commission_fee * shop_total / 100)
our_commission_gross Commission inc VAT (our_commission_net + our_commission_vat)
Did I add unnecessary fields?
I used PHP to calculate the cost.

I can see you've actually added many fields that where not necessary. Think of it this way: If you can explain how to calculate a field by applying any operations to other fields, then it is a calculated field.
Those fields shouldn't be part of a minimalistic design as they can be obtained by operating over other fields. Let's go for the most complex example in your tables.
If you wanted to get the total of all the items for an order in your example you would just select the item_total field. But what happens if it wasn't there? Could you still get that number? Yes. This is how:
select sum(oi.total) from order_items oi
inner join order o on (oi.order_id = o.id)
where (o.id = 5)
Now, we've got rid of one field, but can we remove the order_items.total field and still get this result? Yes. Because it is also a calculated field. This is how:
select sum(oi.quantity * oi.item_price) from order_items oi
inner join order o on (oi.order_id = o.id)
where (o.id = 5)
Applying a similar pattern you can get rid of all the fields you've mentioned. And then you'll have a minimal design.
One thing that worths mentioning is that calculating fields is more complex than just querying the value so they are more expensive in terms of CPU and HD usage. The advantage of calculated fields is that you avoid data redundancy and save a bit of space too :)

Related

MySQL Case Conditions Deduction to generate balance

I have a MySQL table named, "store_update_stock" to store purchase & issue of items. Order status column maintain the states, "purchase" or "issue" when need. Purchase quantity has denoted by plus values (eg:-10) & issue quantity has denoted by minus values (eg:- -2) in the table.
To get the purchase & issue summary I used the following query.
SELECT item_id, item_name, order_status, (CASE order_status
WHEN "issue" THEN store_update_stock_details.qty * (-1)
ELSE store_update_stock_details.qty
END) quantity FROM store_update_stock
And generated the expected output as follows :
+---------+-----------+--------------+----------+
| item_id | item_name | order_status | quantity |
+---------+-----------+--------------+----------+
| 1000 | A4 | purchase | 10 |
| 1001 | A3 | purchase | 5 |
| 1000 | A4 | issue | 2 |
| 1000 | A4 | issue | 3 |
| 1001 | A3 | purchase | 6 |
+---------+-----------+--------------+----------+
But I need to get further the balance of each items after performing purchases & issues by modifying the above query and get the output as follows :
+---------+-----------+---------+
| item_id | item_name | balance |
+---------+-----------+---------+
| 1000 | A4 | 5 |
| 1001 | A3 | 11 |
+---------+-----------+---------+
What can be done in my query to get the desired output. Can anyone helpme ?
It looks like all you might need is a simple aggregate sum and a group by:
select item_id, item_name, sum(quantity) as balance
from store_update_stock
group by item_id, item_name
maybe?

SUM in just one table - Mysql

I have the following situation. I'm setting up a stock control system and through it, I control the in and out of products.
In a table I have the products (code, description).
In the other table, record the movement of the products (code, product_code, type [in, out], quantity, date).
When I try to make the calculation of what comes in, the less that comes out, it does not work.
MY QUERY:
SELECT SUM(s1.quantity) - SUM(s2.quantity)
FROM `stock_movement` AS s1
JOIN `stock_movement` AS s2
ON s1.code_product = s2.code_product
WHERE s1.type = 'IN'
AND s2.type = 'OUT';
 
RESULTS: 80
The correct result here should be (40 + 20) - (10 + 10) = 40, but this query is considering that all inputs are of type IN and OUT, so the result 80.
Anyone have any idea what I'm doing wrong? The table data below are as follows:
TABLE STOCK_MOVEMENT:
| CODE | CODE_PRODUCT | TYPE | QUANTITY | DATA |
| 1 | 30 | IN | 20 | 2018-01-20 |
| 2 | 30 | IN | 40 | 2018-02-03 |
| 3 | 30 | OUT | 10 | 2018-01-20 |
| 4 | 30 | OUT | 10 | 2018-02-03 |
 
TABLE STOCK:
| CODE | DESCRIPTION |
| 30 | TEST_PRODUCT |
You don't need to self join here, just use conditional aggregation:
SELECT
CODE_PRODUCT,
SUM(CASE WHEN TYPE = 'IN' THEN QUANTITY ELSE 0 END) -
SUM(CASE WHEN TYPE = 'OUT' THEN QUANTITY ELSE 0 END) AS diff
FROM stock_movement
GROUP BY CODE_PRODUCT;
Note that I am aggregating by product. For your particular sample data set, there is only one product, so we could remove GROUP BY and get the same result. But in practice you might want a solution which handles multiple products.

Ordering price according to actual value by currency type in mysql

I would be grateful if you help me. I just want to order products by price according to currency. There is two type of DB.
This is products table in first DB.
+----+--------------+-------+-------------+
| id | product_name | price | currency_id |
+----+--------------+-------+-------------+
| 1 | X PRODUCT | 11.00 | 1 |
| 2 | Y PRODUCT | 10.00 | 2 |
+----+--------------+-------+-------------+
And this is currency table in first DB.
+----+------+-------+
| id | name | value |
+----+------+-------+
| 1 | USD | 1.00 |
| 2 | EUR | 1.26 |
+----+------+-------+
This is second option for DB structure. There is only one table.
+----+--------------+-------+----------+
| id | product_name | price | currency |
+----+--------------+-------+----------+
| 1 | X PRODUCT | 11.00 | USD |
| 2 | Y PRODUCT | 10.00 | EUR |
+----+--------------+-------+----------+
I don't know which DB I should use and I don't know how to order by price according to actual price in USD.
Using the first option (synthetic keys in case a currency changes name), you'd end up with a simple JOIN to get the correct ordering;
SELECT p.*, value*price adjusted_price
FROM products p
JOIN currency c
ON c.id = p.currency_id
ORDER BY adjusted_price
An SQLfiddle to test with.
The "adjusted price" column is the price adjusted to USD, and then you can just order by that.
I think, that it's better to keep your prices within one currency. And if you need to keep another currency — just create another field for that usd_price or the whole new table with currency prices:
product_prices: product_id(int), currency_id(int), price(double)
UPD: create method for actual prices for different currencies
def usd_price
price * currency_rates[:usd]
end

Aggregate calculation query on single SQL table

In my database I have a table, payers_payments, consisting of some foreign keys (payer_id, payment_id) and some pivot fields (amount, pays).
| id | payer_id | payment_id | amount | pays |
|----|----------|------------|--------|------|
| 1 | 1 | 1 | 20 | 0 |
| 2 | 2 | 1 | 23 | 1 |
| 4 | 1 | 2 | 14 | 1 |
| 5 | 2 | 2 | 17 | 1 |
| 6 | 1 | 3 | 10 | 1 |
| 7 | 2 | 3 | 0 | 0 |
Each row represents a contribution a payer made towards a payment. The pays field specifies whether that payer should be included in the payment.
The total cost of a payment is the sum of amount for a given payment_id. So if I wanted to see how much payment 2 cost I would run:
SELECT SUM(amount) AS sumOfPayment
FROM payers_payments
WHERE payment_id=2;
Likewise the total amount a given payer (say, payer 1) has contributed is:
SELECT SUM(amount) AS sumOfPayment
FROM payers_payments
WHERE payer_id=1;
Now, what I want to do is use the concept of fair share. Fair share is the total cost of the payment divided by the number of payers who have pay=1 for that payment. The simplest way I can express this is with a sub-query:
SELECT SUM(payers_payments.amount) / (SELECT count(*)
FROM payers_payments
WHERE pays
AND payers_payments.payment_id = 3
) AS FairShare
FROM payers_payments
WHERE payment_id=3
GROUP BY
payers_payments.payment_id;
For a given payer and payment their fair share is defined as:
SELECT IF(pays, FairShare, 0) as payerFairShare
FROM payers_payments
WHERE payer_id = 1
AND payment_id=3; --FairShare is the query as above
My question is that I want a query to get the total fair share for each payer based on the fair share of each payment and whether or not they are included in the payment. (If pays=false then their fair share is 0)
Based on the above data this is the kind of result I'm after for the 2 payers:
| payer_id | total_paid | fair_share |
|----------|------------|------------|
| 1 | 44 | 25.5 |
| 2 | 40 | 58.5 |
Is there a way to achieve this in a single query or must I do some looping of result sets?
I am agnostic on RDMS but something MySQL-like is good.
I would start by writing a query that works what a single share of a payment is. That is, per payment_id, the sum of all the amounts, divided by the number of people it needs to pay. That result can then be joined back to the original data.
SELECT
payers_payments.payer_id,
SUM(payers_payments.amount ) AS total_paid,
SUM(payers_payments.pays * payments.single_share) AS fair_share
FROM
payers_payments
INNER JOIN
(
SELECT
payment_id,
SUM(amount) / SUM(pays) AS single_share
FROM
payers_payments
GROUP BY
payment_id
)
AS payments
ON payers_payments.payment_id = payments.payment_id
GROUP BY
payers_payments.payer_id
It will be of benefit to have indexes on both (payment_id) and (payer_id).
It will be of benefit to have the amount field in a DECIMAL data-type, though you need to consider what you want to do with rounding. (A total payment of 10.00 needs to be divided three ways, 3.33 each and then what do you want to happen to the spare 0.01?)

constructing a query for the following table

can anyone generate a query for me.
Lets say i have a table sales(saleID, date_of_sales, customerID, itemID, saleprice)
date_of_sales is the datetime field which stores the time of the sale.
customerID is self exlpaining tells to whom item was sold.
itemID is ID of the item sold.
saleprice is the price that the item was sold.
I want to construct a query which will give out the detail of the last purchase by each customers. this could be done by using date_of_sales.
Example table
saleID | date_of_sales | customerID | itemID | saleprice
101 | 2008-01-01 | C2000 | I200 | 650 |
102 | 2010-01-01 | C2000 | I333 | 200 |
103 | 2007-01-01 | C3333 | I111 | 800 |
104 | 2009-12-12 | C3333 | I222 | 100 |
this is the example data table, there are only two customer for simplicity.
customer C2000 did his last purchase
on 2010-01-01
customer C3333 did his last purchase
on 2009-12-12
I want to get a result like this
customerID | date_of_sales | itemID | saleprice
C2000 | 2010-01-01 | I333 | 200 |
C3333 | 2009-12-12 | I222 | 100 |
This might be what you are looking for...
SELECT *
FROM sales
WHERE sales.date_of_sales = (SELECT MAX(date_of_sales)
FROM sales s2
WHERE s2.customerID = sales.customerID);
There is a slight problem with it; if there were two sales on the same day to the same customer, you'll get two rows (unless your date-of-sales column includes the time as well). I think the same applies to the answer above, though.
Additionally, if you DO want to get results based on only a SINGLE entry of the maximum date, I would use the query by #Sachin Shanbhag above, but add a maximum sales ID value too... Since that would be implied as sequential, whichever was entered last would probably be the most recent.
SELECT S.* FROM
sales S
INNER JOIN
( SELECT
customerID,
MAX(date_of_sales) dos,
MAX(SalesID) maxSale
FROM
sales
GROUP BY customerID
) S2 ON S.customerID = S2.customerID
AND S.date_of_sales = S2.dos
AND S.SalesID = S2.maxSale