Reputation: 17
I'm working on a simple stock market portfolio manager and have a question which, I believe, relates to the order of the records in my database table.
Here's my table:
CREATE TABLE 'transactions' (
'positionID' int(11) DEFAULT NULL,
'date' date DEFAULT NULL,
'mode' varchar(100) DEFAULT NULL,
'company' varchar(50) DEFAULT NULL,
'units' int(11) DEFAULT NULL,
'price' float DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Just to summarize, my table of transactions contains a record for my initial purchase, and subsequent dividend reinvestments.
Here's my data:
15,2018-02-28,Reinvestment,Ford,877,13.25
15,2017-11-30,Reinvestment,Ford,26,10.47
15,2016-09-13,Purchase,Ford,20,13.07
15,2019-08-30,Reinvestment,Ford,25,12.64
44,2015-12-05,Purchase,Oracle,87,52.18
44,2017-07-13,Reinvestment,Oracle,449,60.64
44,2018-03-29,Reinvestment,Oracle,84,54.01
44,2019-05-07,Reinvestment,Oracle,59,67.24
21,2016-10-13,Purchase,Ford,10,6.54
21,2018-03-30,Reinvestment,Ford,439,6.63
21,2017-12-30,Reinvestment,Ford,13,5.24
21,2019-09-29,Reinvestment,Ford,13,6.32
I would like to list all my positions grouped by positionid and accumulated units. With each positionid, I would like to list initial purchase date (mode = 'Purchase') and duration of holding since initial purchase. This is what I wrote:
SELECT
t.positionid,
t.company,
IF(t.mode = 'Purchase', t.date, '') AS purchaseDate,
IF(t.mode = 'Purchase', DATEDIFF(CURDATE(), t.date), '') AS duration,
ROUND(SUM(t.units), 3) AS currentHolding,
IF(t.mode = 'Purchase', ROUND(t.price, 3), '') AS cost
FROM transactions t
GROUP BY t.positionid
ORDER BY t.company, t.date
Here's the result I got:
21,Ford,2016-10-13,1117,475.000,6.540
15,Ford,,,948.000,
44,Oracle,2015-12-05,1430,679.000,52.180
My problem is ... for position 15, I don't see the purchaseDate and duration. I believe this is because the records for position=15 are not in chronological order. If the 'Purchase' record were the first, I could have gotten what I wanted.
How do I fix this?
Upvotes: 0
Views: 50
Reputation: 164174
The problem is that you use GROUP BY
without aggregating the columns in the SELECT
list and you don't use in the GROUP BY
clause the column t.company
which you select.
This may be allowed by MySql but sometimes leads to unexpected results.
Change to this:
SELECT
t.positionid,
t.company,
MAX(IF(t.mode = 'Purchase', t.date, '')) AS purchaseDate,
MAX(IF(t.mode = 'Purchase', DATEDIFF(CURDATE(), t.date), '')) AS duration,
ROUND(SUM(t.units), 3) AS currentHolding,
MAX(IF(t.mode = 'Purchase', ROUND(t.price, 3), '')) AS cost
FROM transactions t
GROUP BY t.positionid, t.company
ORDER BY t.company, purchaseDate
See the demo.
Results:
| positionid | company | purchaseDate | duration | currentHolding | cost |
| ---------- | ------- | ------------------- | -------- | -------------- | ------ |
| 15 | Ford | 2016-09-13 00:00:00 | 1147 | 948 | 13.070 |
| 21 | Ford | 2016-10-13 00:00:00 | 1117 | 475 | 6.540 |
| 44 | Oracle | 2015-12-05 00:00:00 | 1430 | 679 | 52.180 |
Upvotes: 1
Reputation: 136
One way to do this is by using a simple join
SELECT
t.positionid,
t.company,
pt.date AS purchaseDate,
DATEDIFF(CURDATE(), pt.date) AS duration,
ROUND(SUM(t.units), 3) AS currentHolding,
ROUND(pt.price, 3) AS cost
FROM transactions t
LEFT JOIN transactions pt ON t.positionid = pt.positionid
WHERE pt.mode = 'Purchase'
GROUP BY t.positionid
ORDER BY t.company, t.date
Upvotes: 0