Fifo sorting a sql table - mysql

I have a table called Transactions which looks like this:
Id | date | type | price | quantity | buysell
1 | 0001 | 1 | 1.00 | 3 | buy
2 | 0002 | 2 | 5.00 | 1 | buy
3 | 0003 | 3 | 0.30 | 2 | buy
4 | 0006 | 1 | 1.50 | 1 | sell
5 | 0007 | 4 | 7.00 | 12 | buy
6 | 0011 | 2 | 6.00 | 1 | sell
7 | 0015 | 3 | 0.50 | 2 | sell
8 | 0016 | 1 | 1.50 | 2 | sell
9 | 0017 | ....
I need to find a way how to match all sell transactions to related buy transactions. This should be done in the fifo principle: In the example above, transaction 4 should match to 1, 6 to 2, 7 to 3 and 8 to 1 (depending on the type). Also the quantity field has to be considered - transaction 1 for example has two fulfilling sell transactions which sum up to the buy quantity of 3.
Is there a way to achieve this with mysql or any other database system?
Edit
The desired resulset could for example be a table of all sell transactions and their related buy transactions:
Id | sellid | buyid
1 | 4 | 1
2 | 6 | 2
3 | 7 | 3
4 | 8 | 1
Based on this table, I can calculate margins or the time it takes to sell something (for example).

you may try the below model
select *,
(select sum(case when buysell='buy' then quantity else -quantity end) from yourTable a
where a.type1=b.type1 and a.date1<=b.date1) bls
from yourTable b order by type1,id

Related

Converting lump sums to transactions

I have a database that tracks the size of claims.
Each claim has fixed information that is stored in claim (such as claim_id and date_reported_to_insurer).
Each month, I get a report which is added to the table claim_month. This includes fields such as claim_id, month_id [101 is 31/01/2018, 102 is 28/02/2018, etc] and paid_to_date.
Since most claims don't change from month to month, I only add a record for claim_month when the figure has changed since last month. As such, a claim may have a June report and an August report, but not a July report. This would be because the amount paid to date increased in June and August, but not July.
The problem that I have now is that I want to be able to check the amount paid each month.
Consider the following example data:
+----------------+----------+----------------+--------------+
| claim_month_id | claim_id | month_id | paid_to_date |
+----------------+----------+----------------+--------------+
| 1 | 1 | 6 | 1000 |
+----------------+----------+----------------+--------------+
| 5 | 1 | 7 | 1200 |
+----------------+----------+----------------+--------------+
| 7 | 2 | 6 | 500 |
+----------------+----------+----------------+--------------+
| 12 | 1 | 9 | 1400 |
+----------------+----------+----------------+--------------+
| 18 | 2 | 8 | 600 |
+----------------+----------+----------------+--------------+
If we assume that this is all of the information regarding claim 1 and 2, then that would suggest that they are both claims that occurred during June 2018. Their transactions should look like the following:
+----------------+----------+----------------+------------+
| claim_month_id | claim_id | month_id | paid_month |
+----------------+----------+----------------+------------+
| 1 | 1 | 6 | 1000 |
+----------------+----------+----------------+------------+
| 5 | 1 | 7 | 200 |
+----------------+----------+----------------+------------+
| 7 | 2 | 6 | 500 |
+----------------+----------+----------------+------------+
| 12 | 1 | 9 | 200 |
+----------------+----------+----------------+------------+
| 18 | 2 | 8 | 100 |
+----------------+----------+----------------+------------+
The algorithm I'm using for this is
SELECT claim_month_id,
month_id,
claim_id,
new.paid_to_date - old.paid_to_date AS paid_to_date_change,
FROM claim_month AS new
LEFT JOIN claim_month AS old
ON new.claim_id = old.claim_id
AND ( new.month_id > old.month_id
OR old.month_id IS NULL )
GROUP BY new.claim_month_id
HAVING old.month_id = Max(old.month_id)
However this has two issues:
It seems really inefficient at dealing with claims with multiple
records. I haven't run any benchmarking, but it's pretty obvious.
It doesn't show new claims. In the above example, it would only show lines 2, 3 and 5.
Where am I going wrong with my algorithm, and is there a better logic to use to do this?
Use LAG function to get the next paid_to_date of each claim_id, and use the current paid_to_date minus the next paid_to_date.
SELECT
claim_month_id,
claim_id,
month_id,
paid_to_date - LAG(paid_to_date, 1, 0) OVER (PARTITION BY claim_id ORDER BY month_id) AS paid_month
FROM claim
The output table is:
+----------------+----------+----------+------------+
| claim_month_id | claim_id | month_id | paid_month |
+----------------+----------+----------+------------+
| 1 | 1 | 6 | 1000 |
| 5 | 1 | 7 | 200 |
| 12 | 1 | 9 | 200 |
| 7 | 2 | 6 | 500 |
| 18 | 2 | 8 | 100 |
+----------------+----------+----------+------------+

Mysql and Triggers usage

I wonder what's the best practice is when you have a table like this below with orders. I need to calculate the total price of each order. Should I use triggers to calculate the price or should I hardcode the calculation before insert into database?
ORDERS:
id | Article | Price | Quantity | Total price
---------------------------------------------
1 | TV | 5 | 1 | 5
2 | CD | 3 | 2 | 6
3 | Book | 2 | 3 | 6
4 | XBOX | 1 | 1 | 1

Complex SQL Counting Query

I have a Facility Booking System and I would like to create a CSV format report.
1. I want to count total booking records
2. I want to count how many user booked our facilities, based on that timeslot
3. I want to combine two queries into one as I need to put the result into the CSV
And here is the table structure:
fbid | memberid | fbdate | fbtimeslot | fname | fposition | fattendance
------------------------------------------------------------------------------
1 | C00001 | 2014-12-12 | 1 | computer | 1 | 1
2 | C00002 | 2014-12-12 | 1 | computer | 4 | 3
3 | C00003 | 2014-12-12 | 1 | computer | 6 | 1
4 | C00002 | 2014-12-12 | 1 | computer | 8 | 3
5 | C00004 | 2014-12-12 | 1 | computer | 4 | 0
6 | C00002 | 2014-12-12 | 1 | computer | 24 | 1
7 | C00001 | 2014-12-12 | 2 | computer | 1 | 0
8 | C00002 | 2014-12-12 | 3 | computer | 1 | 0
For task 1, I have found the solution:
(fattendance = 3 means the position has changed and I have to keep record but the CSV report doesn't this)
SELECT fbtimeslot, COUNT(*) FROM facilitybooking WHERE fname='computer' AND fattendance<>3 GROUP BY fbtimeslot
But for 2 and 3, I tried more than 20 different statements but I still couldn't get the result.
Well, hard tasks though.
I will be very appreciated if you can help me to solve this.
Extras: SQLFiddle Link: http://sqlfiddle.com/#!2/4800b8/3

MySql: Summing the latest amounts banked for each category and type

I have four MySql tables (simplified here):
Table 1: factions (just a list to reference)
id | name
1 | FactionName1
2 | FactionName2
Table 2: currencies (just a list to reference)
id | name
1 | Currency1
2 | Currency2
3 | Currency3
Table 3: events (just a list to reference)
id | name | date
1 | Evebt1 | 2013-10-16
2 | Event2 | 2013-10-18 (Note: date out of order)
3 | Event3 | 2013-10-17
Table 4: event_banking (data entered after each event, remaining amount of each currency for each group)
id | faction_id | currency_id | event_id | amount
1 | 1 | 1 | 1 | 10
2 | 1 | 1 | 2 | 20
3 | 1 | 1 | 3 | 30
4 | 1 | 2 | 1 | 40
5 | 1 | 2 | 2 | 50
6 | 1 | 2 | 3 | 60
7 | 1 | 3 | 1 | 70
8 | 1 | 3 | 2 | 80
9 | 1 | 3 | 3 | 90
10 | 2 | 1 | 1 | 100
11 | 2 | 1 | 2 | 110
12 | 2 | 1 | 3 | 120
13 | 2 | 2 | 1 | 130
14 | 2 | 2 | 2 | 140
15 | 2 | 2 | 3 | 150
16 | 2 | 3 | 1 | 160
17 | 2 | 3 | 3 | 170
Note: Faction 2 didn't bank Currency 3 for Event 2
What I'm looking to be able to do is to get, for each currency, the total of the last banked (date wise) for each faction. (ie How much of each currency is currently banked in total if all factions are merged)
So, I need a table looking something like:
currency_id | total
1 | 130 (eg 20 + 110)
2 | 190 (eg 50 + 140)
3 | 250 (eg 80 + 170) <- Uses Event 3 for Group 2 as Event 2 doesn't exist
I can do basic joins etc, but I'm struggling to be able to filter the results so that I get the latest results for each Faction x Currency x Event so I can then sum them together to get the final total amounts for each currency.
I've tried various permutations of LEFT OUTER JOINs, GROUP BYss & HAVING COUNTs, and had some interesting (but incorrect results), and a variety of different error codes, but nothing remotely close to what I need.
Can anyone help?
I guess you can go on with something like this:
select eb.currency_id, sum(amount) as total
from events e
inner join (
select faction_id, currency_id, max(date) as md
from event_banking eb
inner join events e
on eb.event_id = e.id
group by faction_id, currency_id
) a
on e.date = a.md
inner join event_banking eb
on e.id = eb.event_id
and a.faction_id = eb.faction_id
and a.currency_id = eb.currency_id
group by currency_id;
Here is SQL Fiddle

Need MySQL Query Guru (intersection of record data)

Say i have a table as shown:
id, auctionUser, auctionId, MinPrice, NumBids, PlacedBids
And then say i've got the following entries in above table that have the same auctionId:
1 | user1 | 99 | 10.25 | 20 | 0
2 | user2 | 99 | 10.50 | 50 | 0
Is there a way to write a query ( WHERE auctionId = 99 ) that would return a row for every 0.01 of MinPrice where the two rows would 'intersect' (don't know if that's the right word but it's the best i could come up with to describe it) based on the number of bids in NumBids? So for the data above, there would be an 'intersect' of the two users from 10.50 thru 10.75. I'd like to be able to create the flowing data to display like so , alternating bids between the users for the number of bids set in NumBids:
(bidAmount) | (auctionUser) | NumBids | PlacedBids
10.50 | user2 | 50 | 1
10.51 | user1 | 20 | 1
10.52 | user2 | 50 | 2
10.53 | user1 | 20 | 2
10.54 | user2 | 50 | 3
10.55 | user1 | 20 | 3
.
.
.
10.70 | user2 | 50 | 20
10.71 | user1 | 20 | 20 <-- ends here for user1 since 20 NumBids would be used up
10.72 | user2 | 50 | 21
I don't even know if this is possible via a sql query or not -- or even how to start such a query. I thought i'd throw it out there to see if any sql guru's had and idea. I figured if there was a way to do it, it would probably be much faster to produce it from a query that trying to use php to cycle through and produce the result...maybe not though.
As always, MUCHO THANKS for any time and advice you can spare on this!
I can't say I understand exactly what you want, but I think you need to generate rows. One way of generating rows is to use a Numbers table, which is basically a table of consecutive integers.
Have a look at my answer to this question. It is not related to your question, but there is code to generate such a numbers table.
So if you want to generate 1 row for each 0,01 difference, you would calculate nr of cents (or whatever the currency was) and join to the numbers table with a filter on n < nr_of_cents.
Edit:
Ok, I'll try. First, some sample data.
create table auctions(
auctionuser int
,auctionid int
,minprice decimal(5,2)
,numbids int
);
insert into auctions values(1, 1, 2.20, 2);
insert into auctions values(2, 1, 3.30, 4);
insert into auctions values(3, 1, 4.40, 6);
select *
from auctions
where auctionid = 1;
+-------------+-----------+----------+---------+
| auctionuser | auctionid | minprice | numbids |
+-------------+-----------+----------+---------+
| 1 | 1 | 2.20 | 2 |
| 2 | 1 | 3.30 | 4 |
| 3 | 1 | 4.40 | 6 |
+-------------+-----------+----------+---------+
3 rows in set (0.00 sec)
I think the following is close to what you want. Note that I have used the numbers table in the post I linked to.
select a.auctionuser
,n as user_bid
,minprice
,numbids
,a.minprice + (0.01 * (n-1)) as bid
from auctions a
,numbers
where numbers.n <= a.numbids
and a.auctionid = 1
order
by n
,a.minprice
,a.auctionuser;
+-------------+----------+----------+---------+------+
| auctionuser | user_bid | minprice | numbids | bid |
+-------------+----------+----------+---------+------+
| 1 | 1 | 2.20 | 2 | 2.20 |
| 2 | 1 | 3.30 | 4 | 3.30 |
| 3 | 1 | 4.40 | 6 | 4.40 |
| 1 | 2 | 2.20 | 2 | 2.21 |
| 2 | 2 | 3.30 | 4 | 3.31 |
| 3 | 2 | 4.40 | 6 | 4.41 |
| 2 | 3 | 3.30 | 4 | 3.32 |
| 3 | 3 | 4.40 | 6 | 4.42 |
| 2 | 4 | 3.30 | 4 | 3.33 |
| 3 | 4 | 4.40 | 6 | 4.43 |
| 3 | 5 | 4.40 | 6 | 4.44 |
| 3 | 6 | 4.40 | 6 | 4.45 |
+-------------+----------+----------+---------+------+
12 rows in set (0.00 sec)