I have two mysql tables: earning and redemption. Data in the earning table adds to a running balance and data in the redemption table subtracts from that balance. Each table has an hours column and an amount column.
(There are various reasons why these aren't negative and positive values in the same table, but even with these reasons I recognize now that this is likely a poor schema design, but... for now I'm stuck with it).
How can I get a current balance for both the hours and amounts fields? More specifically, how can I write a single query that will give me SUM(earning.hours) - SUM(redemption.hours) and SUM(earning.amount) - SUM(redemption.amount), grouped by a Common_ID?
Some sample data. Other fields exist in each table, but I'm not concerned with them at the moment.
Earning:
+----+-----------+-------+--------+
| id | common_id | hours | amount |
+----+-----------+-------+--------+
| 1 | 234 | 1.03 | 15.75 |
| 2 | 234 | 2.06 | 33.00 |
| 3 | 237 | 2.11 | 12.29 |
| 4 | 237 | 3.50 | 18.63 |
+----+-----------+-------+--------+
Redemption:
+----+-----------+-------+--------+
| id | common_id | hours | amount |
+----+-----------+-------+--------+
| 1 | 234 | 2.50 | 30.00 |
| 2 | 234 | 0.50 | 5.68 |
| 3 | 237 | 1.00 | 8.00 |
+----+-----------+-------+--------+
Desired result:
+-----------+---------------+----------------+
| common_id | hours_balance | amount_balance |
+-----------+---------------+----------------+
| 234 | 0.09 | 13.07 |
| 237 | 4.61 | 22.92 |
+-----------+---------------+----------------+
You need to perform the grouping separately.
SELECT e.Common_ID,
SUM(e.hours) - MIN(g.SumHours),
SUM(e.amount) - MIN(g.SumAmount)
FROM earning e JOIN (
SELECT Common_ID, SUM(hours) SumHours, SUM(amount) SumAmount
FROM redemption
GROUP BY Common_ID
) g ON e.Common_ID = g.Common_ID
GROUP BY Common_ID
Note: as Lamak pointed out, if you have an common_id that doesn't exists in both tables, you will need a LEFT JOIN and possibly another UNION with a RIGHT JOIN.
Related
Having a MySQL table with such data
code | amount | effective_from
a | 12000 | 7/1/2021
b | 12000 | 8/1/2021
d | 12000 | 7/1/2021
c | 9000 | 6/1/2021
b | 12000 | 15/1/2021
c | 1800 | 10/1/2021
b | 4500 | 21/1/2021
a | 4500 | 12/1/2021
c | 4500 | 20/1/2021
d | 4500 | 7/1/2021
a | 4500 | 17/1/2021
Now that I want to extract data with an additional column as below:
code | amount | effective_from |effective_to
a | 12000 | 7/1/2021 | 12/1/2021
a | 4500 | 12/1/2021 | 17/1/2021
a | 4500 | 17/1/2021 | (null)
b | 12000 | 8/1/2021 | 15/1/2021
b | 12000 | 15/1/2021 | 21/1/2021
b | 4500 | 21/1/2021 | (null)
c | 9000 | 6/1/2021 | 10/1/2021
c | 1800 | 10/1/2021 | 20/1/2021
c | 4500 | 20/1/2021 | (null)
The column effective_to data should come from the effective_from of the immediate row with an effective from date greater than the current effective from if available else return null.
Is there any workaround or I restructure my database?
If I understand correctly, you want a cumulative min() with a range specification:
select t.*,
min(effective_from) over (order by effective_from
range between interval 1 day following and unbounded following
) as effective_to
from t;
Note: This assumes that effective_from is a date with no time component. If there is a time component, you might want interval 1 second instead.
Here is a db<>fiddle.
In most circumstances like this, there would be an "entity" column where you would want the next value for that entity. In that case, the typical logic is LEAD() rather than a cumulative minimum.
I am building a trading system where users need to know their running account balance by date for a specific user (uid) including how much they made from trading (results table) and how much they deposited or withdrew from their accounts (adjustments table).
Here is the sqlfiddle and tables: http://sqlfiddle.com/#!9/6bc9e4/1
Adjustments table:
+-------+-----+-----+--------+------------+
| adjid | aid | uid | amount | date |
+-------+-----+-----+--------+------------+
| 1 | 1 | 1 | 20 | 2019-08-18 |
| 2 | 1 | 1 | 50 | 2019-08-21 |
| 3 | 1 | 1 | 40 | 2019-08-21 |
| 4 | 1 | 1 | 10 | 2019-08-19 |
+-------+-----+-----+--------+------------+
Results table:
+-----+-----+-----+--------+-------+------------+
| tid | uid | aid | amount | taxes | date |
+-----+-----+-----+--------+-------+------------+
| 1 | 1 | 1 | 100 | 3 | 2019-08-19 |
| 2 | 1 | 1 | -50 | 1 | 2019-08-20 |
| 3 | 1 | 1 | 100 | 2 | 2019-08-21 |
| 4 | 1 | 1 | 100 | 2 | 2019-08-21 |
+-----+-----+-----+--------+-------+------------+
How do I get the below results for uid (1)
+--------------+------------+------------------+----------------+------------+
| ResultsTotal | TaxesTotal | AdjustmentsTotal | RunningBalance | Date |
+--------------+------------+------------------+----------------+------------+
| - | - | 20 | 20 | 2019-08-18 |
| 100 | 3 | 10 | 133 | 2019-08-19 |
| -50 | 1 | - | 84 | 2019-08-20 |
| 200 | 4 | 90 | 378 | 2019-08-21 |
+--------------+------------+------------------+----------------+------------+
Where RunningBalance is the current account balance for the particular user (uid).
Based on #Gabriel's answer, I came up with something like, but it gives me empty balance and duplicate records
SELECT SUM(ResultsTotal), SUM(TaxesTotal), SUM(AdjustmentsTotal), #runningtotal:= #runningtotal+SUM(ResultsTotal)+SUM(TaxesTotal)+SUM(AdjustmentsTotal) as Balance, date
FROM (
SELECT 0 AS ResultsTotal, 0 AS TaxesTotal, adjustments.amount AS AdjustmentsTotal, adjustments.date
FROM adjustments LEFT JOIN results ON (results.uid=adjustments.uid) WHERE adjustments.uid='1'
UNION ALL
SELECT results.amount AS ResultsTotal, taxes AS TaxesTotal, 0 as AdjustmentsTotal, results.date
FROM results LEFT JOIN adjustments ON (results.uid=adjustments.uid) WHERE results.uid='1'
) unionTable
GROUP BY DATE ORDER BY date
For what you are asking you would want to union then group the results from both tables, this should give the results you want. However, I recommend calculating the running balance outside of MySQL since this adds some complexity to our query.
Weird things could start to happen, for example, if someone already defined the #runningBalance variable as part of the queries scope.
SELECT aggregateTable.*, #runningBalance := ifNULL(#runningBalance, 0) + TOTAL
FROM (
SELECT SUM(ResultsTotal), SUM(TaxesTotal), SUM(AdjustmentsTotal)
, SUM(ResultsTotal) + SUM(TaxesTotal) + SUM(AdjustmentsTotal) as TOTAL
, date
FROM (
SELECT 0 AS ResultsTotal, 0 AS TaxesTotal, amount AS AdjustmentsTotal, date
FROM adjustments
UNION ALL
SELECT amount AS ResultsTotal, taxes AS TaxesTotal, 0 as AdjustmentsTotal, date
FROM results
) unionTable
GROUP BY date
) aggregateTable
I have two mysql table.
Table: bill
id | billtype | amount | advid | paydate |adjid | adjdate |
1 | electric | 10000 | 123 | 2017-01-01 | 50 | 2017-01-03 |
2 | Water | 5000 | 124 | 2017-02-01 | 0 | 0000-00-00 |
3 | Shirt | 500 | 125 | 2017-03-01 | 0 | 0000-00-00 |
Table: advance
id | advid | amount | balance | purpose |
1 | 123 | 50000 | 20000 | Bill |
2 | 124 | 70000 | 10000 | Bill |
3 | 125 | 55000 | 15000 | Uniform |
4 | 124 | 60000 | 10000 | Bill |
I want to create a drop down menu so that to select those 'advance' which are not adjusted yet (adjid=0 and adjdate=0000-00-00) in Table: bill and that drop down menu will also contain the total value of advance for same advance id (advid) like below:
<option>Bill-130000</option>
<option>Uniform-55000</option>
As total 130000 (70000+60000) advance is taken against advance id 124, so the Total amount of Advance in Option menu should be 130000 in case of Bill. But I am failed to calculate total amount of advance accurately:
SELECT sum(a.amount), purpose FROM bill as b, advance as a WHERE b.paydate!='0000-00-00' AND b.adjid!=0 AND a.advid=b.advid GROUP BY a.advid
Total amount in <option></option> is not coming actual.
What would be the right query for this purpose?
You could try
SELECT SUM(a.amount) AS amount,
MAX(purpose) AS purpose
FROM advance a
WHERE a.advid IN (
SELECT b.advid
FROM bill b
WHERE b.paydate = '0000-00-00'
AND b.adjid = 0)
GROUP BY a.advid
I have two tables called transdt and inv_sldtl.
The transdt table consists of the customer ID and the total price of the items that customers bought.
+----------+-----------+-----------+------------+------------+----------+
| BranchID | TransCode | ControlNo | CustomerID | Date | Amount |
+----------+-----------+-----------+------------+------------+----------+
| 1 | 48 | 469 | 1170 | 2017-05-01 | 295.00 |
| 1 | 48 | 469 | 1185 | 2017-05-01 | 44257.24 |
+----------+-----------+-----------+------------+------------+----------+
and the inv_sldtl contains the item id, unit cost, quantity and total cost.
+----------+-----------+--------+-----------+------------+----------+----------+-----------+
| BranchID | TransCode | ItemID | ControlNo | Date | Quantity | UnitCost | TotalCost |
+----------+-----------+--------+-----------+------------+----------+----------+-----------+
| 1 | 48 | 108 | 469 | 2017-05-01 | 1.00 | 45.00 | 45.00 |
| 1 | 48 | 736 | 469 | 2017-05-01 | 10.00 | 25.00 | 250.00 |
| 1 | 48 | 622 | 469 | 2017-05-01 | 4.00 | 280 | 1120.00 |
| 1 | 48 | 500 | 469 | 2017-05-01 | 4.00 | 10784.31 | 43137.24 |
| | | | | | | | |
+----------+-----------+--------+-----------+------------+----------+----------+-----------+
Given that the results of two tables are in same one transaction which is determined by the ControlNo.
Is there any way we can output like this?
+-----------+-----------+------------+--------+------------+
| ControlNo | TransCode | CustomerID | ItemID | Date |
+-----------+-----------+------------+--------+------------+
| 469 | 48 | 1170 | 108 | 2017-05-01 |
| 469 | 48 | 1170 | 736 | 2017-05-01 |
| 469 | 48 | 1185 | 622 | 2017-05-01 |
| 469 | 48 | 1185 | 500 | 2017-05-01 |
+-----------+-----------+------------+--------+------------+
I assume that we can determine it based on the Amount in transdt table and TotalCost in inv_sldtl table. But how?
EDIT: Since SQL seems doesn't have a solution for this, How can I achieve this using VB6 as my programming language as what #ThorstenKettner have told?
Thanks :)
You would have to build all item combinations in order to compare these with the customers' totals.
pack #1 | pack #2 | sums
-----------+------------+-----------------
item 1 | item 2+3+4 | 45.00 , 44507.24
item 1+2 | item 3+4 | ...
item 1+3 | item 2+4 | ...
item 1+4 | item 2+3 | ...
item 1+2+3 | item 4 | ...
item 2 | item 1+3+4 | ...
item 2+3 | item 1+4 | ...
...
This is no task for SQL. At least not for a DBMS that doesn't support recursive queries. So use a programming language instead. I.e. use Java, C#, whatever, read the data, then get your totals by calling a recursive function.
UPDATE: You have asked for further help on the algorithm. Here is one possible way:
You have a set of n customers, here (1170,1185).
You have a set of m items, here (108,500,622,736).
Take the customer with the lowest total, here customer 1170 with 295.00.
Find item combinations matching the price. You'd use a recursive function call and stop as soon as the price is too high. So you quickly end up with item 108 = 45.00, item 736 = 250.00 and items 108+736 = 295.00. Only the last combination is a match.
Remove the customer from the cusomer set. Remove the item(s) from the item set.
Continue with step 3, till you are done.
To make this a tad more complicated: You see that you can have multiple matches for a customer, e.g. both items A + B and items B + C + D match the customer total. So step 5 and following must be done for every match. Another recursion :-)
Do this slowly, step by step, so as not to get confused. Good luck :-)
Without knowing exactly what you want and if you have Primary or Foreign keys which if you don't should have your ControlNo in first table transdt the PK and FK in your inv_sldt1 so that you can use something like my code below. This way you can also link the tables together by some column. Anyways again without seeing everything here is what I came up with on the fly check it out and let me know.
SELECT CustomerID, Date, Quantity
FROM transdt
INNER JOIN inv_sldt1 ON transdt.ControlNo=inv_sldt1.ControlNo
WHERE ItemID = 108
I was reading about the right outer join from tutorialspoint. I know that when a right outer join is performed the first thing that happens is an inner join of the two tables and then any rows in the right table that are missing in the left table are given null values.
Example from tutorial:
Customers table:
+----+----------+-----+-----------+----------+
| ID | NAME | AGE | ADDRESS | SALARY |
+----+----------+-----+-----------+----------+
| 1 | Ramesh | 32 | Ahmedabad | 2000.00 |
| 2 | Khilan | 25 | Delhi | 1500.00 |
| 3 | kaushik | 23 | Kota | 2000.00 |
| 4 | Chaitali | 25 | Mumbai | 6500.00 |
| 5 | Hardik | 27 | Bhopal | 8500.00 |
| 6 | Komal | 22 | MP | 4500.00 |
| 7 | Muffy | 24 | Indore | 10000.00 |
+----+----------+-----+-----------+----------+
Orders table:
+-----+---------------------+-------------+--------+
|OID | DATE | CUSTOMER_ID | AMOUNT |
+-----+---------------------+-------------+--------+
| 102 | 2009-10-08 00:00:00 | 3 | 3000 |
| 100 | 2009-10-08 00:00:00 | 3 | 1500 |
| 101 | 2009-11-20 00:00:00 | 2 | 1560 |
| 103 | 2008-05-20 00:00:00 | 4 | 2060 |
+-----+---------------------+-------------+--------+
Query:
SELECT ID, NAME, AMOUNT, DATE
FROM CUSTOMERS
RIGHT JOIN ORDERS
ON CUSTOMERS.ID = ORDERS.CUSTOMER_ID;
Result:
+------+----------+--------+---------------------+
| ID | NAME | AMOUNT | DATE |
+------+----------+--------+---------------------+
| 3 | kaushik | 3000 | 2009-10-08 00:00:00 |
| 3 | kaushik | 1500 | 2009-10-08 00:00:00 |
| 2 | Khilan | 1560 | 2009-11-20 00:00:00 |
| 4 | Chaitali | 2060 | 2008-05-20 00:00:00 |
+------+----------+--------+---------------------+
Why is the result of the right outer join the same as the original right table? How is this right join in anyway useful? I see it as pointless.
This isn't a useful query for RIGHT JOIN. Since all orders should have a valid customer (in fact, there should be a foreign key relationship between the Order and Customers tables), there will never be an order with no matching customer, so you'll never get any null values added.
I think they included this query just to contrast it with the almost identical query on the LEFT JOIN and FULL JOIN pages of the tutorial. Those queries show all orders, as well as all customers that don't have any orders (they have NULL in the AMOUNT and DATE columns.
To get the equivalent result with RIGHT JOIN you can simply swap the order of the tables:
SELECT ID, NAME, AMOUNT, DATE
FROM ORDERS
RIGHT JOIN CUSTOMERS
ON CUSTOMERS.ID = ORDERS.CUSTOMER_ID;
Because LEFT JOIN and RIGHT JOIN are equivalent like this, most programmers just use LEFT JOIN.
This is not the right example in that case.To see something which is there in orders table but not in customers table the id should be something that is not present in customers,such as 8,9 etc.