Ensure the sum of amounts is zero - mysql

I have a database with basically 3 tables:
accounts <-- operations --> orders
I would like to allow transactions of money from n accounts to m accounts.
For instance, let's consider a transaction of $11 from Alice and Sophia to Bob ans Joe:
| Sender | Receiver |
| ------ | -------- | ------ |
| 1 | | Alice |
| 10 | | Sophia |
| | 6 | Bob |
| | 5 | Joe |
What I would like to ensure is that, the sum of the moved amount is always equal to zero (in this example: (1 + 10) - (6 + 5) == 0).
In this example, I would have 4 rows inside the operations table.
Until now, I did some control using an programming language such as PHP to ensure that the sum is always zero. But I would really prefer to ensure it on the physical layer, with MySQL.
Also, if possible, I would prefer to DRY the amount which is equal between the sender and the receiver sides. Right now, I don't think that my database structure is normalized. Do you think m
Thank you for your feedback/advice!
Edit
There is what the SQL tables look like:
____________
| accounts |
| -------- |
| id |
------------
______________
| operations |
| ---------- |
| id |
| account_id |
| order_id |
| amount | <- can be positive or negative
--------------
__________
| orders |
| ------ |
| id |
----------

Related

Max function in multiple tables

Cannot get the maximum value of state per project
The only I get is all states from one to last, in the list. that output
| idState | idProject |
| -------- | -------------- |
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 1 | 2 |
| 2 | 2 |
and so on.
What I need is
| idState | idProject |
| -------- | -------------- |
| 4 | 1 |
| 2 | 2 |
Where State is the maximum value from the column per project
SELECT e.idState, e.delivery, ep.idProject
FROM State e
CROSS JOIN StatexProject ep ON e.idState = ep.idState
WHERE ep.idProject = (SELECT MAX(exp.idState) FROM StatexProject exp
WHERE ep.idState = exp.idState);
I think you're looking for basic aggregation (max per group). Your reply to Gordon's question in the comments doesn't make sense given your expected output. Give this a try and we can go from there
SELECT max(e.idState) as idState, ep.idProject
FROM State e
JOIN StatexProject ep ON e.idState = ep.idState
GROUP BY ep.idProject;

MySQL 5.7 - Total quantity column for Bill of Materials

I would like to ask for a help.I use MySQL 5.7 and I have a Bill of Materials table called BOM.
I have many columns there which I will add later to the query, but for this questionID, Name, ParentID and Quantity is important. I would like to add a calculated column totalQty to my table that will multiply children quantities with parent quantities up to the top level. here is an example of what I want to achive:
+-------+------+------+----------+----------+--+
| ID | Name | Qty | ParentID | TotalQty | |
+-------+------+------+----------+----------+--+
| 1 | A | 1 | 0 | 1 | |
| 1.1 | AA | 2 | 1 | 2 | |
| 1.1.1 | AAA | 1 | 1.1 | 2 | |
| 1.2 | AB | 5 | 1 | 5 | |
| 1.2.1 | ABA | 2 | 1.2 | 10 | |
| 2 | B | 3 | 0 | 3 | |
| 2.1 | BA | 2 | 2 | 6 | |
+-------+------+------+----------+----------+--+
I need this to create a list of materials used per project, so I will multiply totalQTY with unit mass and unit length to get total mass and total length of part. Then I will group and aggregate them by material type.
I have searched the forums, and I have found out that I need to use Common table expressions (CTE) for this. But I have found no explanation in plain english on how to do it for my case.
Thank you very much for any help or hint how to understand the concept.

What is the best way to reduce MXN table search in MySQL

I have two MySQL tables called tasks and users. All I want to do is I don't want to display the tasks that is already done by a user in his panel. Suppose the task table has about 1000 entries and there are about 50000 users. Also the users and the tasks keep increasing.
One solution I can think of, 1st is creating a separate table of task x user size.
For example:
user table
+---------+--------+-------+
| user_id | fname | lname |
+---------+--------+-------+
| 1 | John | Smith |
| 2 | Steve | Mark |
+---------+--------+-------+
task table
+---------+-------------+---------------+
| task_id | task | task_duration |
+---------+-------------+---------------+
| 1 | Do task 1 | 1 hour |
| 2 | Do task 2 | 1 hour |
+---------+-------------+---------------+
Creating a separate table called display
+------------+---------+---------+
| display_id | task_id | user_id |
+------------+---------+---------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
| 4 | 2 | 2 |
+------------+---------+---------+
So only listed tasks will be shown to the particular user.
The problem is that This does not look like an efficient solution. How can I design table in this scenario in an efficient way. If not what are the other ways?

Selecting rows with more than one occurence of column value from joined table

I have a query which returns a joined table, it looks like the following:
| id_cart | id_customer | date_add |
| ------- | ----------- | --------------------- |
| 1 | 100 | 2017-07-24 | 10:48:00 |
| ------- | ----------- | --------------------- |
| 2 | 101 | 2016-02-14 | 15:43:05 |
| ------- | ----------- | --------------------- |
| 3 | 100 | 2015-04-12 | 01:59:34 |
I'd like to make it so that from these results, only rows with more than one occurrence in the id_customer column are selected - I'm assuming it'd look something like this:
| id_cart | id_customer | date_add | customer_count |
| ------- | ----------- | --------------------- | -------------- |
| 1 | 100 | 2017-07-24 | 10:48:00 | 2 |
| ------- | ----------- | --------------------- | -------------- |
| 3 | 100 | 2015-04-12 | 01:59:34 | 2 |
The issue I'm facing is most questions I've seen from others are looking for the count of multiple value occurrences, whereas I'm only interested in seeing the results - not the count. Also by using a joined table I'm unsure on how to refer back to it as a variable/alias, instead of just copy & pasting a horribly long query over and over again.
For MySQL I would do this:
select *
from (select stuff from bigjoin) a1
inner join
(
select id_customer
from (select stuff from bigjoin)
group by id_customer
having count(*) > 1
) x1
on x1.id_Customer = a1.id_customer

Database schema - Configurable fields?

I sell leads and charge my clients like so:
(Only one type of payment from the followings can be charged from a client)
Pay Per Lead:
$__ for the first __ leads per month
$__ for the next __ leads per month
$__ for the next __ leads per month
and so on...
Pay per Appointment:
$__ for the first __ leads per month
$__ for the next __ leads per month
$ __ for the next __ leads per month
and so on...
Pay per Percentage of Sale:
__% of the sale price (per sale)
My Question:
What are the best possible database design solutions in such cases?
What i have tried:
+---------+
| clients |
+---------+
| id |
| name |
+---------+
+---------------+
| deals |
+---------------+
| client_id |
| max_quantity |
| cost |
| unit_type |
+---------------+
So records for client with the id 1 might look like:
+-----------+--------------+---------------+-------------+
| client_id | max_quantity | cost_per_unit | unit_type |
+-----------+--------------+---------------+-------------+
| 1 | 10 | 10 | lead |
| 1 | 30 | 5 | lead |
| 1 | 100 | 2 | lead |
| 1 | 10 | 35 | appointment |
| 1 | 30 | 20 | appointment |
| 1 | 100 | 10 | appointment |
| 1 | 1000 | 5 | appointment |
| 1 | 0 | 50 | sale |
+-----------+--------------+---------------+-------------+
Now the above table means that:
$10 will be charged per lead upto 10 leads
$5 will be charged per lead upto 30 leads
$2 will be charged per lead upto 100 leads
$35 will be charged per appointment upto 10 leads
$20 will be charged per appointment upto 30 leads
$10 will be charged per appointment upto 100 leads
$5 will be charged per appointment upto 1000 leads
$50 will be charged per sale
Also i want to add x number of such rules (per lead, per appointment, per sale)
I personally don't think that my approach is one of the best solutions. Looking forward to hear for you cleaver folks! Thank you.
P.S. I know that unit_type can be further normalized but this is not the issue :)
Update
Maybe i can store serialized data?
Your proposed schema is a good start and has some merits. IMO the less elegant parts are the denormalized repetition of unit_type values and non-functional max_quantity value for sale.
Would suggest splitting deals into three tables rather than one. Would personally go with singular rather than plural table names** and begin with the same prefix so they are listed close to each other: Something like commission_lead, commission_appointment and commission_sale.
** [Lots of debate on this here]
Would also suggest including both lower and upper bands in each row. This does use more data than is strictly needed but think it is worth doing as it should make the table data more readable and simplify the calculation queries.
So the proposed new schema is:
+---------+
| client |
+---------+
| id |
| name |
+---------+
+-----------------+
| commission_lead |
+-----------------+
| client_id |
| min_quantity |
| max_quantity |
| cost_per_unit |
+-----------------+
+------------------------+
| commission_appointment |
+------------------------+
| client_id |
| min_quantity |
| max_quantity |
| cost_per_unit |
+------------------------+
+-----------------+
| commission_sale |
+-----------------+
| client_id |
| cost_per_unit |
+-----------------+
And the records for client_id = 1 are:
commission_lead
+-----------+--------------+--------------+---------------+
| client_id | min_quantity | max_quantity | cost_per_unit |
+-----------+--------------+--------------+---------------+
| 1 | 0 | 10 | 10 |
| 1 | 11 | 30 | 5 |
| 1 | 31 | 100 | 2 |
+-----------+--------------+--------------+---------------+
commission_appointment
+-----------+--------------+--------------+---------------+
| client_id | min_quantity | max_quantity | cost_per_unit |
+-----------+--------------+--------------+---------------+
| 1 | 0 | 10 | 35 |
| 1 | 11 | 30 | 20 |
| 1 | 31 | 100 | 10 |
| 1 | 101 | 1000 | 5 |
+-----------+--------------+--------------+---------------+
commission_sale
+-----------+---------------+
| client_id | cost_per_unit |
+-----------+---------------+
| 1 | 50 |
+-----------+---------------+
I make an assumption that the change is very rare (update/insert), most of the time you use select to calculate the cost, so I propose this design, the select to calculate cost is very simple
+-----------+--------------+---------------+---------------+--------------+------------+
| client_id | max_quantity | min_quantity | cost_per_unit | default_cost | unit_type |
+-----------+--------------+---------------+---------------+--------------+------------+
| 1 | 10 | 0 | 10 | 0 | lead|
| 1 | 40 | 10 | 5 | 100 | lead|
| 1 | 140 | 40 | 2 | 250 | lead|
| 1 | 10 | 0 | 35 | 0 | appointment|
| 1 | 40 | 10 | 20 | 350 | appointment|
| 1 | 140 | 40 | 10 | 950 | appointment|
| 1 | 1140 | 140 | 5 | 1950 | appointment|
| 1 | 0 | 0 | 50 | 0 | sale|
+-----------+--------------+---------------+---------------+--------------+------------+
select query looks like
select
default_cost + ($quantity - min_quantity) * cost_per_unit
from
table
where
unit_type = $unit_type
and (max_quantity >= $quantity or max_quantity = 0)
and $quantity >= min_quantity
IF you consider the cost calculations business logic that is likely to change in the future AND you dont need to filter/sort the table based on the calculation constants, I recommend having one column for rule_id, that pretty much works like your unit_type, and one varchar column called properties where all the specific values needed for that rule is stored with a separator.
You then retrieve the rules that apply for your client to your business logic and do your calculations there. If you need a new rule that suddenly takes 5 parameters, you don't need to change the database schema. Simply write code for a new rule_id in your business logic and you are good to go.
Of course, if you prefer to move calculation logic into stored procedures and/or need to filter or order by rule properties, I think you should go with separate columns for each rule parameter...