FIFO in MySQL db - mysql

I'm using Mysql (not MSSQL) database and bumped onto a problem I seem to be unable to solve. I would very much appreciate your help finding a solution.
I'd like to create a view using two tables, as described below:
1. product_in
product_code
received_time
received_amount
2. product_out
product_code
delivery_time
delivered_amount
The “view” should provide the following:
product_code
received_time
received_amount
of_which_delivered
My problem is that product_out is to be administered to the first incoming data (FIFO: first in first out), but since the amount delivered is either more or less than the received amount, I do not know how to calculate the “of_which_delivered”.
So far, I managed to put into order the incoming data, and sum up the outgoing (delivered) goods using SUM.
SELECT
sn,
product_code,
received_time,
received_amount,
delivered_amount
FROM
( SELECT
received_time,
received_amount,
#rend2 := If( #rend1 = product_code, #rend2 + 1, 1) as sn,
#rend1 := product_code AS product
FROM
product_in,
( SELECT #rend1 := 0, #rend2 := 0 ) AS tt
ORDER BY
product_code,
received_time ) AS k
LEFT JOIN
( SELECT
product_code AS prdct,
SUM(delivered_amount) AS delivered_amount
FROM
product_out
GROUP BY
product_code ) AS b
ON aru = product_code
I have not succeded in creating the loop that would make it possible to analyze if the output amount is more, or less than the received amount on a given day, and if more, then the difference be added to the received amount of another day.
To be more precise, here is an example:
Product Date Qty
nr.1 Sep 2 500
nr.1 Sep 3 300
nr1. Sep 4 200 on the 4th.
900 pcs were delivered out on the 5th.
In this case we should see the following in the view:
Product Action Date Qty
nr.1 received Sep 2 500 (delivered all 500)
nr.1 received Sep 3 300 (delivered all 300)
nr.1 received Sep 4 200 (only 100 delivered)
I would be very grateful to anyone who could help me find a solution!

Sorry no query adjustment for you, but... having worked with accounting systems in the past, your database structure appears short on a better handling of the in/out FIFO method (or even LIFO). What the underlying accounting system had was a inventory table for all receipts (abbreviated)
Table: ItemTrans
ItemTransID (auto-increment ID for any transaction)
ItemID (item id of the inventory item)
Status (status, such as In, Our or Inventory Adjustment)
Date (date of the activity)
Qty (quantity)
QtyUsed (running total column as inventory was used up)
As items were sold or adjustments. the Sales Details table would show which ItemTrans record the quantities were used for. So, if you had inventory on hand as you have in your sample, the sales order detail line would show the 900. The inventory used table would show which specific ItemTransID the quantities were allocated from and how many from said block, thus showing what your intended activity of release of actual product was at the time of sale.
This way, you don't have to recreate what inventory was what at a given point in time. Just query the sales order details and which items they were pulled from to get the data and how many from each block of receipt it went against.
This simplifies the process of generating the COGS (cost of goods sold) as would be reported to the General Ledger from a Sub-journal such as Accounts Receivable (Sales Orders) activity.
Do you have the ability to introduce such adjustments to your database structures?

Related

Total amount of sales done for each product using SQL

Here is the structure of 1st Table called Product.
PRODID PDESC PRICE CATEGORY DISCOUNT
101 BALL 10 SPORTS 5
102 SHIRT 20 APPAREL 10
Here is the structure of 2nd table called SaleDetail.
SALEID PRODID QUANTITY
1001 101 5
1001 101 2
1002 102 10
1002 102 5
I am trying to get total sales amount for each product by joining 2 tables. Here is the SQL i tried but its not giving correct result.
select a.prodid,
(sum((price - discount))),
sum(quantity),
(sum((price - discount))) * sum(quantity)
from product a
join saledetail b on a.prodid = b.prodid
group by a.prodid
2nd column of the query is giving incorrect final price. Please help me correct this SQL.
Please find an indicative answer to your question in the fiddle.
A problem stems from the aggregation of the difference of price. In case that the same product has two different prices, then these prices would be aggregated to one.
Moreover, you multiple the sums of the prices and quantities, while you need to perform the calculation on every sample. Look at the answer by #DanteTheSmith.
You might consider to use the SaleDetail table on the left side of your query.
SELECT SD.PRODID,
P.Price-P.Discount AS Final_Price,
SUM(SD.QUANTITY) AS Amount_Sold,
SUM((P.Price-P.Discount)*SD.QUANTITY) AS Sales_Amount
FROM SaleDetail AS SD
JOIN Product AS P
ON SD.PRODID = P.PRODID
GROUP BY SD.PRODID, P.Price-P.Discount
It would help if you built the example in SQL fiddle or gave the creates for the tables, but if I have to guess your problem is:
(sum((price - discount))) * sum(quantity)
needs to be:
sum((price - discount) * quantity)
(price - discount) * quantity is the function you wanna apply PER ROW of the joined table then you wanna add all those up with SUM() when grouping by prodid.
Furthermore, you can notice that (price - discount) needs to be done ONLY ONCE PER ROW so a quicker version would be to do:
(price-discount) * sum(quantity)
That would give you the total money earned for that product across all the sales you made, and I am guessing this is what you want?
I just notice you have a problem with 2nd column, dunno if that has been in question all along:
(sum((price - discount)))
Why are you summing? Do you want the money earned per product per unit of the product? Well guess what, your price is the same all the time, same as your discount so you can simply go with:
(price-discount) as PPP
NOTE: This assumes the discount is numerical (not percentage) and is applicable to all your sales, also the price is forever the same all which is not real life like.

MySQL query to calculate rent owed based on a partial payment

I could probably do the following via PHP code but I feel it could most likely been accomplished in MySQL, so just looking for someone to help me out on a query.
I have a contract table which defines a customer monthly payment, let's say for example it is £500 per month. I then have another table called rent, where every month rent is inputted. Each rent has a status called Paid, Unpaid and Partial.
My query so far is the following, do I have to do multiple sub-queries, or is their a simple way.
SELECT cc.property_id, cc.property_rent, r.order_total, r.order_status,
SUM(CASE WHEN r.order_status = 'Partial' THEN cc.property_rent - r.order_total ELSE 0 END) AS partial_rent_owed
FROM t_customers_contract cc JOIN
t_customers_rent r
ON cc.customer_id = r.customer_id WHERE cc.property_id = 62 AND r.transaction_type = 'rent' AND
(r.date_created BETWEEN '2017-04-05' AND '2019-04-05')
GROUP BY cc.property_id
Basically, if the rent status is partial then subtract it from what is usually and then total sum of what is owed.
The desired result would be in the total SUM of what is owed and what has been contributed per property as a following output:
property_id, total_rent_made, total_rent_owed
The current contract table structure and data is as follows:
The current rent table structure and data is as follows:
As you can see order_id 20 and 27 are Partial payments and the actual payment to be made based on the contract for these ID's should be 750 and 700.
I have managed to resolve it, it is the following query. If anyone can improve it, happy to take a suggestion based on performance or it being more cleaner.
SELECT cc.property_id,
SUM(CASE WHEN r.order_status = 'Partial' THEN (cc.property_rent - r.order_total) ELSE 0 END) AS partial_rent_owed
FROM t_customers_contract cc JOIN t_customers_rent r ON (cc.property_id = r.property_id)
WHERE cc.contract_id = r.contract_id
AND cc.customer_id = 7866
AND r.transaction_type = 'rent'
AND (r.date_created BETWEEN '2016-04-05' AND '2019-04-05')
GROUP BY cc.property_id

Relational Database Logic

I'm fairly new to php / mysql programming and I'm having a hard time figuring out the logic for a relational database that I'm trying to build. Here's the problem:
I have different leaders who will be in charge of a store anytime between 9am and 9pm.
A customer who has visited the store can rate their experience on a scale of 1 to 5.
I'm building a site that will allow me to store the shifts that a leader worked as seen below.
When I hit submit, the site would take the data leaderName:"George", shiftTimeArray: 11am, 1pm, 6pm (from the example in the picture) and the shiftDate and send them to an SQL database.
Later, I want to be able to get the average score for a person by sending a query to mysql, retrieving all of the scores that that leader received and averaging them together. I know the code to build the forms and to perform the search. However, I'm having a hard time coming up with the logic for the tables that will relate the data. Currently, I have a mysql table called responses that contains the following fields,
leader_id
shift_date // contains the date that the leader worked
shift_time // contains the time that the leader worked
visit_date // contains the date that the survey/score was given
visit_time // contains the time that the survey/score was given
score // contains the actual score of the survey (1-5)
I enter the shifts that the leader works at the beginning of the week and then enter the survey scores in as they come in during the week.
So Here's the Question: What mysql tables and fields should I create to relate this data so that I can query a leader's name and get the average score from all of their surveys?
You want tables like:
Leader (leader_id, name, etc)
Shift (leader_id, shift_date, shift_time)
SurveyResult (visit_date, visit_time, score)
Note: omitted the surrogate primary keys for Shift and SurveyResult that I would probably include.
To query you join shifts and surveys group on leader and taking the average then jon that back to leader for a name.
The query might be something like (but I haven;t actually built it in MySQL to verify syntax)
SELECT name
,AverageScore
FROM Leader a
INNER JOIN (
SELECT leader_id
, AVG(score) AverageScore
FROM Shift
INNER JOIN
SurveyResult ON shift_date = visit_date
AND shift_time = visit_time --depends on how you are recording time what this really needs to be
GROUP BY leader ID
) b ON a.leader_id = b.leader_id
I would do the following structure:
leaders
id
name
leaders_timetabke (can be multiple per leader)
id,
leader_id
shift_datetime (I assume it stores date and hour here, minutes and seconds are always 0
survey_scores
id,
visit_datetime
score
SELECT l.id, l.name, AVG(s.score) FROM leaders l
INNER JOIN leaders_timetable lt ON lt.leader_id = l.id
INNER JOIN survey_scores s ON lt.shift_datetime=DATE_FORMAT('Y-m-d H:00:00', s.visit_datetime)
GROUP BY l.id
DATE_FORMAT here helps to cut hours and minutes from visit_datetime so that it could be matched against shift_datetime. This is MYSQL function, so if you use something else you'll need to use different function
Say you have a 'leader' who has 5 survey rows with scores 1, 2, 3, 4 and 5.
if you select all surveys from this leader, sum the survey scores and divide them by 5 (the total amount of surveys that this leader has). You will have the average, in this case 3.
(1 + 2 + 3 + 4 + 5) / 5 = 3
You wouldn't need to create any more tables or fields, you have what you need.

Adding or subtracting values based on another field's contents

I have a table with transactions. All transactions are stored as positive numbers, if its a deposit or withdrawl only the action changes. How do i write a query that can sum up the numbers based on the action
-actions-
1 Buy 2 Sell 5 Dividend
ID ACTION SYMBOL PRICE SHARES
1 1 AGNC 27.50 150
2 2 AGNC 30.00 50
3 5 AGNC 1.25 100
So the query should show AGNC has a total of 100 shares.
SELECT
symbol,sum(shares) AS shares,
ROUND(abs(sum((price * shares))),2) AS cost,
FROM bf_transactions
WHERE (action_id <> 5)
GROUP BY symbol
HAVING sum(shares) > 0
I was originally using that query when i had positive/negative numbers and that worked great.. but i dont know how to do it now with just positive numbers.
This ought to do it:
SELECT symbol, sum(case action
when 1 then shares
when 2 then -shares
end) as shares
FROM bf_transactions
GROUP BY symbol
SQL Fiddle here
It is however good practice to denormalize this kind of data - what you appear to have now is a correctly normalized database with no duplicate data, but it's rather impractical to use as you can see in cases like this. You should keep a separate table with current stock portfolio that you update when a transaction is executed.
Also, including a HAVING-clause to 'hide' corrupted data (someone has sold more than they have purchased) seems rather bad practice to me - when a situation like that is detected you should definitely throw some kind of error, or an internal alert.

Summary of a day or run a query and sum the results?

Since I don't know to calculate efficiency I'll ask here and I hope someone could tell me what is better and explain it a bit.
The scenario:
Currently I have a table that insert rows of production of each worker.
Something like: (Worker1) produced (product10) with (some amount) for a Date.
And that goes for each station he worked in though the day.
The Question:
I need to generate a report of the sum of amounts that worker produced for each date. I know how to generate the report either way but the question is how is it more efficient?
Having to run a query for each person that sums up the production for each date? or having a table that I'll insert the total amount, workerID and date?
Again if you could explain it a bit further it would be nice, if not than at least an educated answer would help me a lot with this problem.
Example:
This is what I have right now in my production table:
ID EmpID ProductID Amount Dateofproduction
----------------------------------------------------------
1 1 1 100 14/01/2013
2 1 2 20 14/01/2012
This is what I want in the end:
EmpID Amount DateofProduction
-----------------------------------
1 120 14/01/2013
Should I start another table for this? or should I just sum what I have in the production table and take what I need?
Bear in mind that the production table will get larger and larger each day (of course).
i) Direct :
select EmpId, sum(Amount) as Amount, DateOfProduction
from ProductionTable
group by EmpId, DateOfProduction.
ii) Now, the size of the table will keep growing. And you need only day-wise reports.
Is this table being used by anyone else? Can some of the data be archived? If some of the data can be archived, I would suggest, after each day and reporting, backup all the data from this table to a secondary archive table. So, every day you will have to query only today's worth of records.
Secondly, you can consider adding an index to DateOfProduction. You will then be able to restrict your queries in date range. For example, select EmpId, sum(Amount) as Amount, DateOfProduction from ProductionTable group by EmpId, DateOfProduction where DateOfProduction = Date(now()). (or something similar)
Because it is just a single table and no complicated queries, MySql will be easily able to take care of millions of records. Try EXPLAIN on the queries to check the number of records being touched and indexes being used.
Unless I am missing something, it sounds like you just want this:
select empid,
sum(amount) TotalAmount,
Dateofproduction
from yourtable
group by empid, Dateofproduction
See SQL Fiddle with Demo
Result:
| EMPID | TOTALAMOUNT | DATEOFPRODUCTION |
------------------------------------------
| 1 | 120 | 2013-01-14 |
Note: I am guessing that the second row of data you provided is supposed to be 2013 not 2012.