I have a table structured like this:
ID NAME SURNAME EXTRA TYPE
______________________________________________
1 MARIO ROSSI RED 10
2 MARCO VERDI YELLOW 10
3 GIANNI BLU TEACHER 20
4 LUCA BLU STUDENT 20
5 LUCA ROSSI GREEN 10
6 MARIA GIALLA 10/08/05 30
7 MARTA ROSA 11/01/79 30
8 FRANCO NERO BARMAN 20
9 MARY NERI 05/09/88 30
10 MAX BLU 06/08/98 30
Now I need to order the query by name, surname and (extra[DESC] where type=30), so with this 3rd condition I mean that I would arrange all extra data where type is 30 in descending order:
....
6 MARIA GIALLA 10/08/05 30
10 MAX BLU 06/08/98 30
9 MARY NERI 05/09/88 30
7 MARTA ROSA 11/01/79 30
....
So I've tried this:
select * from my_table order by name, surname, if(my_table.type=30, extra desc, extra asc)
but it gives me an Sql Error.
Thanks for your support.
Thanks to other programmers, and in according to my data, I solved in this way: ORDER BY name, surname, if(TYPE=30, STR_TO_DATE(EXTRA,'%d/%m/%Y %H.%i'), null) DESC
Try this query :-
ORDER BY
IF(type='30', extra, 0) DESC,
IF(type !='30', extra, 0) ASC
More usefully, consider the following:
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,NAME VARCHAR(12) NOT NULL
,SURNAME VARCHAR(12) NOT NULL
,EXTRA VARCHAR(12) NOT NULL
,TYPE INT NOT NULL
);
INSERT INTO my_table VALUES
( 1,'MARIO','ROSSI','RED','10'),
( 2,'MARCO','VERDI','YELLOW','10'),
( 3,'GIANNI','BLU','TEACHER','20'),
( 4,'LUCA','BLU','STUDENT','20'),
( 5,'LUCA','ROSSI','GREEN','10'),
( 6,'MARIA','GIALLA','2005/08/10','30'),
( 7,'MARTA','ROSA','1979/01/11','30'),
( 8,'FRANCO','NERO','BARMAN','20'),
( 9,'MARY','NERI','1988/09/05','30'),
(10,'MAX','BLU','1998/08/06','30'),
(11,'MARIO','ROSSI','PLUMBER','20'),
(12,'MARCO','VERDI','TAILOR','20'),
(13,'GIANNI','BLU','YELLOW','10'),
(14,'LUCA','BLU','BLUE','10'),
(15,'LUCA','ROSSI','BAKER','20'),
(16,'MARIO','ROSSI','2004/08/10','30'),
(17,'MARCO','VERDI','1978/01/11','30'),
(18,'FRANCO','NERO','RED','10'),
(19,'FRANCO','NERO','1987/09/05','30'),
(20,'MARIA','GIALLA','1995/08/06','30');
SELECT name
, surname
, MAX(CASE WHEN type = 10 THEN extra END) colour
, MAX(CASE WHEN type = 20 THEN extra END) occupation
, MAX(CASE WHEN type = 30 THEN extra END) date
FROM my_table
GROUP
BY name
, surname
ORDER
BY name
, surname
, extra
, type
, date DESC;
+--------+---------+--------+------------+------------+
| name | surname | colour | occupation | date |
+--------+---------+--------+------------+------------+
| FRANCO | NERO | RED | BARMAN | 1987/09/05 |
| GIANNI | BLU | YELLOW | TEACHER | NULL |
| LUCA | BLU | BLUE | STUDENT | NULL |
| LUCA | ROSSI | GREEN | BAKER | NULL |
| MARCO | VERDI | YELLOW | TAILOR | 1978/01/11 |
| MARIA | GIALLA | NULL | NULL | 2005/08/10 |
| MARIO | ROSSI | RED | PLUMBER | 2004/08/10 |
| MARTA | ROSA | NULL | NULL | 1979/01/11 |
| MARY | NERI | NULL | NULL | 1988/09/05 |
| MAX | BLU | NULL | NULL | 1998/08/06 |
+--------+---------+--------+------------+------------+
You can join a result like this (or actually a much simpler alternative - but I'll stick with this for now) back onto the original table to order the results as you'd like. I've added in the STR_TO_DATE function just for reference. Obviously it's unnecessary here, and very slightly different from how it would need to be in your version...
SELECT x.*
, y.date
FROM my_table x
LEFT
JOIN
( SELECT name
, surname
, MAX(CASE WHEN type = 30 THEN extra END) date
FROM my_table
GROUP
BY name
, surname
) y
ON y.name = x.name
AND y.surname = x.surname
ORDER
BY x.name
, x.surname
, STR_TO_DATE(y.date,'%Y/%m/%d') DESC;
+----+--------+---------+------------+------+------------+
| ID | NAME | SURNAME | EXTRA | TYPE | date |
+----+--------+---------+------------+------+------------+
| 18 | FRANCO | NERO | RED | 10 | 1987/09/05 |
| 19 | FRANCO | NERO | 1987/09/05 | 30 | 1987/09/05 |
| 8 | FRANCO | NERO | BARMAN | 20 | 1987/09/05 |
| 13 | GIANNI | BLU | YELLOW | 10 | NULL |
| 3 | GIANNI | BLU | TEACHER | 20 | NULL |
| 14 | LUCA | BLU | BLUE | 10 | NULL |
| 4 | LUCA | BLU | STUDENT | 20 | NULL |
| 15 | LUCA | ROSSI | BAKER | 20 | NULL |
| 5 | LUCA | ROSSI | GREEN | 10 | NULL |
| 17 | MARCO | VERDI | 1978/01/11 | 30 | 1978/01/11 |
| 2 | MARCO | VERDI | YELLOW | 10 | 1978/01/11 |
| 12 | MARCO | VERDI | TAILOR | 20 | 1978/01/11 |
| 6 | MARIA | GIALLA | 2005/08/10 | 30 | 2005/08/10 |
| 20 | MARIA | GIALLA | 1995/08/06 | 30 | 2005/08/10 |
| 11 | MARIO | ROSSI | PLUMBER | 20 | 2004/08/10 |
| 16 | MARIO | ROSSI | 2004/08/10 | 30 | 2004/08/10 |
| 1 | MARIO | ROSSI | RED | 10 | 2004/08/10 |
| 7 | MARTA | ROSA | 1979/01/11 | 30 | 1979/01/11 |
| 9 | MARY | NERI | 1988/09/05 | 30 | 1988/09/05 |
| 10 | MAX | BLU | 1998/08/06 | 30 | 1998/08/06 |
+----+--------+---------+------------+------+------------+
It should be like this:
SELECT *
FROM Tabletest
order by
CASE WHEN type <> 30 THEN 1 ELSE 2 END,
CASE WHEN type <> 30 THEN Name END,
CASE WHEN type <> 30 THEN Surname END,
CASE WHEN type = 30 THEN STR_TO_DATE(extra,'%m/%d/%Y') ELSE 0 END DESC
this will order the results as:
Related
I am stuck in this situation where I need to use Row Number and Column Number values from table's columns to derive the output mentioned below. I have tried everything - if/else, case when/then but not helping.
Any help/suggestions are really appreciated!
Here is a mocked up sample data present in db table -
+--------+--------+--------+----------+-------------+
| Record | ColNbr | RowNbr | ColTitle | CellContent |
+--------+--------+--------+----------+-------------+
| 1 | 1 | 1 | Unit | sqf |
| 1 | 1 | 2 | Unit | cm |
| 1 | 2 | 1 | Desc | roof |
| 1 | 2 | 2 | Desc | rod |
| 1 | 3 | 1 | Material | concrete |
| 1 | 3 | 2 | Material | steel |
| 1 | 4 | 1 | Quantity | 100 |
| 1 | 4 | 2 | Quantity | 12 |
| 1 | 1 | 1 | Unit | liter |
| 1 | 1 | 2 | Unit | ml |
| 1 | 2 | 1 | Desc | bowl |
| 1 | 2 | 2 | Desc | plate |
| 1 | 3 | 1 | Material | plastic |
| 1 | 3 | 2 | Material | glass |
| 1 | 4 | 1 | Quantity | 2 |
| 1 | 4 | 2 | Quantity | 250 |
+--------+--------+--------+----------+-------------+
Expected Output -
+--------+--------+--------+----------+-------------+
| Record | Unit | Desc | Material | Quantity |
+--------+--------+--------+----------+-------------+
| 1 | sqf | roof | concrete | 100 |
| 1 | cm | rod | steel | 12 |
| 2 | liter | bowl | plastic | 2 |
| 2 | ml | plate | glass | 250 |
+--------+--------+--------+----------+-------------+
If your actual data is like that, I suggest that you consider to separate the data to; for example, 4 different tables (unit,description,material & a table to store all that ids+quantity). The former 3 tables will store the prerequisite info that get minor updates throughout time and the last table will store all the quantity records. Let's say your tables will look something like this:
CREATE TABLE `Unit` (
unit_id INT,
unit_name VARCHAR(50));
+---------+-----------+
| unit_id | unit_name |
+---------+-----------+
| 1 | sqf |
| 2 | cm |
| 3 | liter |
| 4 | ml |
+---------+-----------+
CREATE TABLE `Description` (
desc_id INT,
desc_name VARCHAR(50));
+---------+-----------+
| desc_id | desc_name |
+---------+-----------+
| 1 | roof |
| 2 | rod |
| 3 | bowl |
| 4 | plate |
+---------+-----------+
CREATE TABLE `Material` (
mat_id INT,
mat_name VARCHAR(50));
+--------+----------+
| mat_id | mat_name |
+--------+----------+
| 1 | concrete |
| 2 | steel |
| 3 | plastic |
| 4 | glass |
+--------+----------+
CREATE TABLE `Records` (
unit_id INT,
desc_id INT,
mat_id INT,
quantity DECIMAL(14,4));
+---------+---------+--------+----------+
| unit_id | desc_id | mat_id | Quantity |
+---------+---------+--------+----------+
| 1 | 1 | 1 | 100 |
| 2 | 2 | 2 | 12 |
| 3 | 3 | 3 | 2 |
| 4 | 4 | 4 | 250 |
+---------+---------+--------+----------+
Then you insert the data accordingly.
Anyhow, for your existing data example, it could be done but there are some concern over whether the unit+desc+material+quantity matching are correct. The only way I can maybe at least think that it's correctly matched is by giving all of the query a similar ORDER BY clause. Hence, the following:
SELECT A.record,A.unit,B.Desc,C.Material,D.Quantity FROM
(SELECT #rn:=#rn+1 AS record,CASE WHEN coltitle='unit' THEN cellcontent END AS Unit
FROM yourtable, (SELECT #rn :=0 ) v
HAVING unit IS NOT NULL
ORDER BY colnbr) A LEFT JOIN
(SELECT #rn1:=#rn1+1 AS record,CASE WHEN coltitle='Desc' THEN cellcontent END AS `Desc`
FROM yourtable, (SELECT #rn1 :=0 ) v
HAVING `Desc` IS NOT NULL
ORDER BY colnbr) B ON a.record=b.record LEFT JOIN
(SELECT #rn2:=#rn2+1 AS record,CASE WHEN coltitle='material' THEN cellcontent END AS Material
FROM yourtable, (SELECT #rn2:=0 ) v
HAVING Material IS NOT NULL
ORDER BY colnbr) C ON a.record=c.record LEFT JOIN
(SELECT #rn3:=#rn3+1 AS record,CASE WHEN coltitle='Quantity' THEN cellcontent END AS Quantity
FROM yourtable, (SELECT #rn3:=0 ) v
HAVING Quantity IS NOT NULL
ORDER BY colnbr) D ON a.record=d.record;
The idea here is to make a sub-query based on COLTITLE then assign a numbering/ranking (#rn,#rn1,#rn2,#rn3) variable to each of the sub-query and join them up using LEFT JOIN. Now, this experiment works to exactly return the output that you need but its not a definite answer because there are some part that is questionable especially on matching the combination correctly. Hopefully, this will give you some idea.
invoice table
SELECT id, fname, gtotal, `date` FROM invoice WHERE id = 1;
| id | fname | gtotal | date |
|----|---------|--------|-----------------------|
| 1 | Brandon | 860 | May, 11 2016 00:00:00 |
invoice_contents table,
SELECT * FROM invoice_contents WHERE invoice_id = 1;
| id | invoice_id | item | price | quantity | discount | total |
|----|------------|------------|-------|----------|----------|-------|
| 1 | 1 | Dextrose | 10 | 10 | 5 | 95 |
| 2 | 1 | Nescaine | 20 | 30 | 10 | 540 |
| 3 | 1 | Anticavity | 30 | 10 | 25 | 225 |
This JOIN query
SELECT invoice.id, invoice.fname, invoice_contents.item,
invoice_contents.price, invoice_contents.quantit,
invoice_contents.discount, invoice_contents.total,
invoice.gtotal
FROM invoice_contents
INNER JOIN invoice ON invoice_contents.invoice_id=1 AND invoice.id=1;
gives this result.
| id | fname | item | price | quantity | discount | total | gtotal |
|----|---------|------------|-------|----------|----------|-------|--------|
| 1 | Brandon | Dextrose | 10 | 10 | 5 | 95 | 860 |
| 1 | Brandon | Nescaine | 20 | 30 | 10 | 540 | 860 |
| 1 | Brandon | Anticavity | 30 | 10 | 25 | 225 | 860 |
I need this result.
| id | fname | item | price | quantity | discount | total | gtotal |
|----|---------|------------|-------|----------|----------|-------|--------|
| 1 | Brandon | Dextrose | 10 | 10 | 5 | 95 | 860 |
| | | Nescaine | 20 | 30 | 10 | 540 | |
| | | Anticavity | 30 | 10 | 25 | 225 | |
I am just a beginner in MySQL. I have been trying from this morning to get this kind of output by experimenting on different combinations please help me out.
#Rex, Your select is correct. You should make desired output using some script e.g. PHP.
try this in SQL:
in this Query i save everytime fname in a variable is not equal and at the next row i compare it and return a empty string is it equal. and the same for gtotal.
the cross join is only to initialize the variables.
in this case it is important that the rows are order by fname to ensure that the same name is behind each other
SELECT
invoice.id,
IF(#last_fname = invoice.fname, '', (#last_fname:=invoice.fname)) as fname,
invoice_contents.item,
invoice_contents.price,
invoice_contents.quantity,
invoice_contents.discount,
IF(#last_gtotal = invoice.gtotal, '', (#last_gtotal:=invoice.gtotal)) as gtotal
FROM invoice_contents
INNER JOIN invoice ON invoice_contents.invoice_id=1 AND invoice.id=1
CROSS JOIN ( select #last_fname := '' , #last_gtotal := '' ) AS parameter
ORDER BY invoice.fname;
Sample
MariaDB [bb]> SELECT
-> invoice.id,
-> IF(#last_fname = invoice.fname, '', (#last_fname:=invoice.fname)) AS fname,
-> invoice_contents.item,
-> invoice_contents.price,
-> invoice_contents.quantity,
-> invoice_contents.discount,
-> IF(#last_gtotal = invoice.gtotal, '', (#last_gtotal:=invoice.gtotal)) AS gtotal
-> FROM invoice_contents
-> INNER JOIN invoice ON invoice_contents.invoice_id=1 AND invoice.id=1
-> CROSS JOIN ( SELECT #last_fname:='' , #last_gtotal:='' ) AS parameter
-> ORDER BY invoice.fname;
+----+---------+------------+-------+----------+----------+--------+
| id | fname | item | price | quantity | discount | gtotal |
+----+---------+------------+-------+----------+----------+--------+
| 1 | Brandon | Dextrose | 10.00 | 10 | 5.00 | 860.00 |
| 1 | | Nescaine | 20.00 | 30 | 10.00 | |
| 1 | | Anticavity | 30.00 | 10 | 25.00 | |
+----+---------+------------+-------+----------+----------+--------+
3 rows in set, 1 warning (0.00 sec)
MariaDB [bb]>
I want to display orders item that have the collect_id = 2
And I want to display all the fields related to each order_item as columns with values.
These are the tables and the result :
+-------------------------------+
| order_item |
+-------------------------------+
| oi_id oi_price oi_collect_id |
| 1 100 2 |
| 2 30 2 |
| 3 55 3 |
| 4 70 4 |
| 5 220 2 |
| 6 300 4 |
+-------------------------------+
+-----------------------------------+
| field_value |
+-----------------------------------+
| v_value v_fk_field_id oi_fk_id |
| Peter 1 1 |
| Lagaf 2 1 |
| Football 3 1 |
| Male 4 1 |
| 12345678 5 1 |
| Frank 1 2 |
| Loran 2 2 |
| Tennis 3 2 |
| Male 4 2 |
| 11223658 5 2 |
| Nathali 1 5 |
| Waton 2 5 |
| Reading 3 5 |
+-----------------------------------+
oi_fk_id : foreign key ref(order_item.oi_id)
v_fk_field_id : foreign key ref(field.f_id)
+--------------------+
| field |
+--------------------+
| f_id f_label |
| 1 surname |
| 2 name |
| 3 hobbies |
| 4 sex |
| 5 phone |
+--------------------+
+-----------------------------------------------------------------------------+
| Result |
+-----------------------------------------------------------------------------+
| oi_id oi_price oi_collect_id surname name hobbies sex phone |
| 1 100 2 Peter Lagaf Football Male 12345678 |
| 2 30 2 Frank Loran Tennis Male 11223658 |
| 5 220 2 Nathali Waton Reading null null |
+-----------------------------------------------------------------------------+
Important : The table field does not contain only these 5 fields (name, surname, hobbies, sex, phone), but it can contain many others, that the developper may not know, same thing for the correspondant value on the table 'field_value'.
PS : I didn't make field labels as columns in a table because they are dynamic and not limited, and in the front end application, the user can add new fields as he want.
You can take advantage of dynamic pivoting to get the results:
SELECT GROUP_CONCAT(t.line)
FROM (
SELECT CONCAT('MAX(IF(t.l=''', f.f_label, ''',t.v,NULL)) AS ', f.f_label) AS line
FROM field f
) AS t
INTO #dynamic;
SELECT CONCAT('SELECT t.oi_id, t.oi_price, t.oi_collect_id,',
#dynamic,
' FROM ( SELECT oi.*, f.f_label AS l, fv.v_value AS v FROM order_item oi JOIN field_value fv ON fv.oi_fk_id = oi.oi_id JOIN field f ON f.f_id = fv.v_fk_field_id WHERE oi.oi_collect_id = 2 ) AS t GROUP BY t.oi_id;')
INTO #sql;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
However it is limited by GROUP_CONCAT function:
The result is truncated to the maximum length that is given by the group_concat_max_len system variable, which has a default value of 1024. The value can be set higher, although the effective maximum length of the return value is constrained by the value of max_allowed_packet.
EDIT - Why MAX function is required?
To solve the given problem, we are creating a dynamic query, based on the content of the field table.
For the given exemplary data, the query without use of MAX function would be:
SELECT t.oi_id,
t.oi_price,
t.oi_collect_id,
IF(t.l='surname', t.v, NULL) AS surname,
IF(t.l='name', t.v, NULL) AS name,
IF(t.l='hobbies', t.v, NULL) AS hobbies,
IF(t.l='sex', t.v, NULL) AS sex,
IF(t.l='phone', t.v, NULL) AS phone
FROM (
SELECT oi.*,
f.f_label AS l,
fv.v_value as V
FROM order_item oi
JOIN field_value fv
ON fv.oi_fk_id = oi.oi_id
JOIN field f
ON f.f_id = fv.v_fk_field_id
WHERE oi.oi_collect_id = 2
) AS t;
Which wouldd result in:
+-------+----------+---------------+---------+-------+----------+------+----------+
| oi_id | oi_price | oi_collect_id | surname | name | hobbies | sex | phone |
+-------+----------+---------------+---------+-------+----------+------+----------+
| 1 | 100 | 2 | Peter | NULL | NULL | NULL | NULL |
| 2 | 30 | 2 | Frank | NULL | NULL | NULL | NULL |
| 5 | 220 | 2 | Nathali | NULL | NULL | NULL | NULL |
| 1 | 100 | 2 | NULL | Lagaf | NULL | NULL | NULL |
| 2 | 30 | 2 | NULL | Loran | NULL | NULL | NULL |
| 5 | 220 | 2 | NULL | Waton | NULL | NULL | NULL |
| 1 | 100 | 2 | NULL | NULL | Football | NULL | NULL |
| 2 | 30 | 2 | NULL | NULL | Tennis | NULL | NULL |
| 5 | 220 | 2 | NULL | NULL | Reading | NULL | NULL |
| 1 | 100 | 2 | NULL | NULL | NULL | Male | NULL |
| 2 | 30 | 2 | NULL | NULL | NULL | Male | NULL |
| 1 | 100 | 2 | NULL | NULL | NULL | NULL | 12345678 |
| 2 | 30 | 2 | NULL | NULL | NULL | NULL | 11223658 |
+-------+----------+---------------+---------+-------+----------+------+----------+
This is an intermediate result, where each row consists of value for one field and NULL for the others. The MAX function together with a GROUP BY clause is used to combine multiple rows concerning one order item in such a way, that it chooses non null values. It could be replaced by MIN function, which will also favor existing values over null.
MySQL
Table Messages
| fromuser | toUser | message | time
| 1 | 2 | Hi? | +1
| 2 | 1 | Hello! | +2
| 1 | 3 | There? | +3
| 3 | 1 | Yes | +4
| 2 | 3 | Hey | +5
| 3 | 2 | Sup? | +6
| 1 | 2 | :) | +7
| 1 | 4 | thanks |
| 4 | 1 | welcome |
I need a query for latest message
Closest I got is
SELECT * FROM (SELECT * FROM Messages WHERE fromUser=1 OR toUser=1 ORDER BY time DESC) AS msg GROUP BY fromUser,toUser
Result:
| fromuser | toUser | message | time
| 2 | 1 | Hello! | +2
| 1 | 3 | There? | +3
| 3 | 1 | Yes | +4
| 1 | 2 | :) | +7
| 1 | 4 | thanks |
| 4 | 1 | welcome |
it returns the latest from and latest to.
What I want is
| fromuser | toUser | message | time
| 3 | 1 | Yes | +4
| 1 | 2 | :) | +7
| 4 | 1 | welcome |
Latest of from and to of user1, like how a list of chats work.
The OP is looking for the latest message from each dialogue within which a given user participates.
There's a probably a more elegant solution, but here's one way...
Note that my dataset has a surrogate PK...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(fromuser INT NOT NULL
,toUser INT NOT NULL
,message VARCHAR(20) NOT NULL
,message_id INT NOT NULL PRIMARY KEY
);
INSERT INTO my_table VALUES
(1,2,'Hi?',1),
(2,1,'Hello!',2),
(1,3,'There?',3),
(3,1,'Yes',4),
(2,3,'Hey',5),
(3,2,'Sup?',6),
(1,2,':)',7),
(1,4,'thanks',8),
(4,1,'welcome',9);
SELECT a.* FROM my_table a
JOIN
(
SELECT MAX(message_id) message_id FROM
(
SELECT toUser correspondent,message,message_id FROM my_table WHERE fromuser = 1
UNION
SELECT fromuser,message,message_id FROM my_table WHERE touser = 1
) x
GROUP BY correspondent
) b
ON b.message_id = a.message_id;
+----------+--------+---------+------------+
| fromuser | toUser | message | message_id |
+----------+--------+---------+------------+
| 1 | 2 | :) | 7 |
| 3 | 1 | Yes | 4 |
| 4 | 1 | welcome | 9 |
+----------+--------+---------+------------+
What I have until now: http://sqlfiddle.com/#!2/bbfec/6
I want to get the quanity of shares for a given stock, that a given company has available to sell - grouped by price. For example, for company number 9 and stock number 1, I want the data like this:
| id | name | price | date | quantity | total |
------------------------------------------------------------------
| 3 | ALTR | 2.240 | 2015-05-12 04:29:29 | 50 | 112.00 |
| 7 | ALTR | 2.449 | 2014-06-10 18:21:02 | 50 | 122.45 |
Because company 9 bought 200 stocks on 2015-05-12 04:29:29, sold 100 on 2014-06-10 15:50:17, more 50 on 2014-06-10 17:06:18 and bought 50 on 2014-06-10 18:21:02.
I don't want the total of all shares, because they have different prices when a company acquires them. The price and the date are the purchasing price and date but the quantity is what is left from a certain purchasing.
Thanks in advance.
Strawberry, the desired result:
| id | price | date | quantity |
-----------------------------------------------
| 3 | 2.240 | 12-05-2015 | 50 |
| 7 | 2.449 | 10-06-2014 | 50 |
Start with:
select id_acao, id_empresa, ifnull(bought,0) - ifnull(sold,0) as stock
from
(
select id_acao, id_empresa,
(select sum(quantidade) from acao_trans where tipo='C' and id_acao=a.id_acao and id_empresa=a.id_empresa) as bought,
(select sum(quantidade) from acao_trans where tipo='V' and id_acao=a.id_acao and id_empresa=a.id_empresa) as sold
from acao_trans a group by id_acao,id_empresa
) x
;
+---------+------------+-------+
| id_acao | id_empresa | stock |
+---------+------------+-------+
| 1 | 4 | 1500 |
| 1 | 9 | 100 |
| 8 | 9 | 3500 |
| 13 | 9 | 5000 |
+---------+------------+-------+
And join this query to your base acao and empresa tables.
Remark: For statistics etc. it would be easier to use negative quantities for selling transactions instead of a transaction type 'C' and 'V'.
For ease of (my) comprehension, I translated and adjusted your stock table slightly...
SELECT a.stock_id
, a.company_id
, a.transaction_date
, a.price
, COALESCE(a.quantity - SUM(b.quantity),a.quantity) quantity
, COALESCE(a.quantity - SUM(b.quantity),a.quantity) * a.price subtotal
FROM stock_company a
LEFT
JOIN
( SELECT x.stock_id
, x.company_id
, MAX(x.transaction_date) min_transaction_date
, y.quantity
FROM stock_company x
JOIN stock_company y
ON y.stock_id = x.stock_id
AND y.company_id = x.company_id
AND y.transaction_date <= x.transaction_date
AND y.transaction_type <> x.transaction_type
WHERE y.transaction_type = 'SELL'
GROUP
BY x.stock_id
, x.company_id
, y.quantity
) b
ON b.stock_id = a.stock_id
AND b.company_id = a.company_id
AND b.min_transaction_date = a.transaction_date
WHERE a.stock_id = 1
AND a.company_id = 9
AND a.transaction_type = 'BUY'
GROUP
BY stock_id
, company_id
, transaction_date;
+----------+------------+---------------------+-------+----------+----------+
| stock_id | company_id | transaction_date | price | quantity | subtotal |
+----------+------------+---------------------+-------+----------+----------+
| 1 | 9 | 2014-06-10 18:21:02 | 2.449 | 50 | 122.450 |
| 1 | 9 | 2015-05-12 04:29:29 | 2.240 | 50 | 112.000 |
+----------+------------+---------------------+-------+----------+----------+
http://www.sqlfiddle.com/#!2/cfa4d/1
Note that this hasn't been tested extensively so there may be a flaw (or perhaps several flaws!) in my logic, but it seems to work well enough on the data set provided.
EDIT: I made a slight adjustment - still not sure if it's enough. Let me know.
Maybe now I've understood it. How about this:
select c.id,c.id_empresa,c.id_acao,c.data as c_data,c.quantidade as c_quantidade,v.preco,v.id as v_id,v.data as v_data,ifnull(v.quantidade,0) as v_quantidade, c.preco*v.quantidade as bought, v.preco*v.quantidade as sold
from acao_trans c
left join acao_trans v
on c.id=v.parent
order by id_empresa, id_acao,c_data,v_data
which results in
+----+------------+---------+---------------------+--------------+-------+------+---------------------+--------------+----------+----------+
| id | id_empresa | id_acao | c_data | c_quantidade | preco | v_id | v_data | v_quantidade | bought | sold |
+----+------------+---------+---------------------+--------------+-------+------+---------------------+--------------+----------+----------+
| 4 | 4 | 1 | 2014-06-10 08:59:09 | 2000 | 2.385 | 8 | 2014-06-11 10:39:48 | 500 | 1184.000 | 1192.500 |
| 8 | 4 | 1 | 2014-06-11 10:39:48 | 500 | NULL | NULL | NULL | 0 | NULL | NULL |
| 5 | 9 | 1 | 2014-06-10 15:50:17 | 100 | NULL | NULL | NULL | 0 | NULL | NULL |
| 6 | 9 | 1 | 2014-06-10 17:06:18 | 50 | NULL | NULL | NULL | 0 | NULL | NULL |
| 7 | 9 | 1 | 2014-06-10 18:21:02 | 50 | NULL | NULL | NULL | 0 | NULL | NULL |
| 3 | 9 | 1 | 2015-05-12 04:29:29 | 200 | 2.430 | 5 | 2014-06-10 15:50:17 | 100 | 224.000 | 243.000 |
| 3 | 9 | 1 | 2015-05-12 04:29:29 | 200 | 2.449 | 6 | 2014-06-10 17:06:18 | 50 | 112.000 | 122.450 |
| 2 | 9 | 8 | 2015-05-12 04:27:56 | 3500 | NULL | NULL | NULL | 0 | NULL | NULL |
| 1 | 9 | 13 | 2015-05-12 04:25:52 | 5000 | NULL | NULL | NULL | 0 | NULL | NULL |
+----+------------+---------+---------------------+--------------+-------+------+---------------------+--------------+----------+----------+
Can you go on from there?
Ok, now I think I got it.
Here's the query resulting what I wanted:
SELECT p.id
, a.nome
, p.preco
, date_format(p.`data`,'%m/%d/%Y') AS `data`
, COALESCE(p.quantidade-SUM(f.quantidade), p.quantidade) AS quantidade
, p.preco*COALESCE(p.quantidade-SUM(f.quantidade), p.quantidade) AS total
FROM acao_trans p
LEFT JOIN acao_trans f
ON p.id=f.parent
INNER JOIN acao a
ON p.id_acao=a.id
WHERE p.parent IS NULL
AND p.id_acao=1
AND p.id_empresa=9
GROUP BY p.id
Fiddle: http://sqlfiddle.com/#!2/bbfec/64.
What I did: I joined the table that matters (acao_trans "p") with itself ("f") and I used Sum function to aggregate all the quantities of second argument, giving me the total of all sold shares. If there are records on "f" I want to subtract that total of the quantity of purchasing shares ("p"). If there is not a correspondence it will show null fields and I show purchased quantity. After it's done it's kind of simple. Quantity was what really mattered here, with that I was able to reach other things easily.