Order by and group by - mysql

I am trying to show delivery charges for a shop I am building, there are three tables in the database 1 for the service ie Royal Mail, Carrier..., one for the band ie. UK, Europe, Worldwide1 etc.. and one for the charges (qty = weight)
I have a database of three tables that, when joined form the following
+------------------+-----+-----------+-------+---------+---------------+----------+-------+-------------+
| name | qty | serviceID | basis | bandID | initial_charge | chargeID | price | total_price |
+------------------+-----+-----------+-------+---------+---------------+----------+-------+-------------+
| Collect in store | 0 | 3 | | 1 | 3 | 0.00 | 2 | 0.00 |
| Royal mail | 0 | 1 | 2 | 4 | 2.00 | 3 | 0.00 | 2.00 |
| Royal mail | 1 | 1 | 2 | 4 | 2.00 | 4 | 1.00 | 3.00 |
| APC | 0 | 2 | 1 | 1 | 0.00 | 6 | 5.95 | 5.95 |
+------------------+-----+-----------+-------+---------+---------------+----------+-------+-------------+
Basically what I want to do is (as you can see) Royal Mail has two entries as there are more than one entry in the joined table. What I would like to do is show the highest of the two royal mail entries (I was initially trying to group by service_id) whilst also maintaining the two other services with different service id's
Any assistance would be great as this is driving me mad. I feel like I have tried every combination going!
In the example below the qty (weight) of the items is 3kg
SELECT
`service`.`name`,
`charge`.`qty`,
`service`.`serviceID`,
`band`.`bandID`,
`band`.`initial_charge`,
`charge`.`chargeID`,
`charge`.`price`,
`band`.`initial_charge` + `charge`.`price` AS `total_price`
FROM
`delivery_band` AS `band`
LEFT JOIN
`delivery_charge` AS `charge`
ON
`charge`.`bandID` = `band`.`bandID`
AND
`charge`.`qty` < '3'
LEFT JOIN
`delivery_service` AS `service`
ON
`service`.`serviceID` = `band`.`serviceID`
WHERE
FIND_IN_SET( '225', `band`.`accepted_countries` )
AND
(
`band`.`min_qty` >= '3'
OR
`band`.`min_qty` = '0'
)
AND
(
`band`.`max_qty` <= '3'
OR
`band`.`max_qty` = '0'
)
delivery_service
+-----------+------------------+
| serviceID | name |
+-----------+------------------+
| 1 | Royal mail |
| 2 | APC |
| 3 | Collect in store |
+-----------+------------------+
delivery_band
+--------+-----------+-----------------+----------------+---------+---------+-------------------------------------------------------+
| bandID | serviceID | name | initial_charge | min_qty | max_qty | accepted_countries |
+--------+-----------+-----------------+----------------+---------+---------+-------------------------------------------------------+
| 1 | 2 | UK Mainland | 0.00 | 0 | 0 | 225 |
| 2 | 2 | UK Offshore | 14.00 | 0 | 0 | 240 |
| 3 | 3 | Bradford Store | 0.00 | 0 | 0 | 225 |
| 4 | 1 | UK | 2.00 | 0 | 0 | 225 |
| 5 | 2 | World wide | 15.00 | 0 | 0 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20... |
| 6 | 1 | World wide Mail | 5.00 | 0 | 0 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20... |
+--------+-----------+-----------------+----------------+---------+---------+-------------------------------------------------------+
delivery_charge
+----------+--------+-----+-------+
| chargeID | bandID | qty | price |
+----------+--------+-----+-------+
| 1 | 2 | 0 | 5.00 |
| 2 | 3 | 0 | 0.00 |
| 3 | 4 | 0 | 0.00 |
| 4 | 4 | 1 | 1.00 |
| 5 | 4 | 5 | 3.00 |
| 6 | 1 | 0 | 5.95 |
| 7 | 1 | 10 | 10.95 |
| 8 | 2 | 10 | 14.00 |
| 9 | 5 | 0 | 0.00 |
| 10 | 5 | 3 | 5.00 |
| 11 | 5 | 6 | 10.00 |
| 12 | 5 | 9 | 15.00 |
| 13 | 6 | 0 | 0.00 |
| 14 | 6 | 2 | 5.00 |
| 15 | 6 | 4 | 10.00 |
| 16 | 6 | 6 | 15.00 |
+----------+--------+-----+-------+
When I tried adding the charge table as a sub query and then limiting that query it gave me NULL's for all the charge table fields
If I try the following query:
SELECT
`service`.`name`,
`charge`.`qty`,
`service`.`serviceID`,
`band`.`bandID`,
`band`.`initial_charge`,
`charge`.`chargeID`,
MAX( `charge`.`price` ) AS `price`,
`band`.`initial_charge` + `charge`.`price` AS `total_price`
FROM
`delivery_band` AS `band`
LEFT JOIN
`delivery_charge` AS `charge`
ON
`charge`.`bandID` = `band`.`bandID`
AND
`charge`.`qty` < '3'
LEFT JOIN
`delivery_service` AS `service`
ON
`service`.`serviceID` = `band`.`serviceID`
WHERE
FIND_IN_SET( '225', `band`.`accepted_countries` )
AND
(
`band`.`min_qty` >= '3'
OR
`band`.`min_qty` = '0'
)
AND
(
`band`.`max_qty` <= '3'
OR
`band`.`max_qty` = '0'
)
GROUP BY
`service`.`serviceID`
I get this returned:
+------------------+-----+-----------+--------+----------------+----------+-------+-------------+
| name | qty | serviceID | bandID | initial_charge | chargeID | price | total_price |
+------------------+-----+-----------+--------+----------------+----------+-------+-------------+
| Royal mail | 0 | 1 | 4 | 2.00 | 3 | 1.00 | 2.00 |
| APC | 0 | 2 | 1 | 0.00 | 6 | 5.95 | 5.95 |
| Collect in store | 0 | 3 | 3 | 0.00 | 2 | 0.00 | 0.00 |
+------------------+-----+-----------+--------+----------------+----------+-------+-------------+
Which looks fine in principle until you realise that the chargeID = 3 has a price of 0.00 and yet the table is showing a price of 1.00 so the values seem to have become disassociated

What I would like to do is show the highest of the two royal mail entries
You can use MAX to obtain the maximum of a given column, e.g.
SELECT … MAX(charge.price) … FROM …
If you absolutely need the other columns (like charge.chargeID) to match, things will become a lot more complicated. So make sure you actually need that. For details on the general idea behind this kind of query, have a closer look at Select one value from a group based on order from other columns. Adapting this answer by #RichardTheKiwi, I came up with the following query:
SELECT s.name,
c.qty,
s.serviceID,
b.bandID,
b.initial_charge,
c.chargeID,
c.price,
b.initial_charge + c.price AS total_price
FROM delivery_band AS b,
delivery_service AS s,
(SELECT chargeID, price, qty,
#rowctr := IF(bandId = #lastBand, #rowctr+1, 1) AS rowNumber,
#lastBand := bandId AS bandId
FROM (SELECT #rowctr:=0, #lastBand:=null) init,
delivery_charge
WHERE qty < 3
ORDER BY bandId, price DESC
) AS c
WHERE FIND_IN_SET(225, b.accepted_countries)
AND (b.min_qty >= 3 OR B.min_qty = 0)
AND (b.max_qty <= 3 OR B.max_qty = 0)
AND s.serviceID = b.serviceID
AND c.bandID = b.bandID
AND c.rowNumber = 1
See this fiddle for the corresponding output. Note that I only do inner queries, not left queries, since that seems sufficient for the query in question, and keeps things a lot more readable so you can concentrate on the important parts, i.e. those involving rowNumber. The idea is that the subquery generates row numbers for the items of the same band, resetting them for the next band. When you select only rows with rowNumber being 1, you only get the highest price, with all other columns associated with that.

Related

How to sum values of two tables and group by date

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

How to write mysql innerquery when get the records count from same table

Query to get results from 2 tables:
SELECT path.* FROM (SELECT tbc.course_name
slp.course_id,slp.student_type,slp.stu_reference_id,
count(slp.course_id) as counttotalWatchedStudents
from tbl_student_learning_path slp LEFT JOIN tbl_courses tbc
on tbc.course_pid = slp.course_id WHERE slp.stu_reference_id =34
and slp.student_type='institute' GROUP BY slp.course_id ) as path
Query result table:
| course_id | totalCollegeStudents | fullwatchedStudentsCount | counttotalWatchedStudents | sumOfWatchPointsForWatchedStudents | totalStudentsAvg | fullwatchedAvg |
|-------------------------------|----------------------|--------------------------|---------------------------|------------------------------------|------------------|----------------|
| Number Systems | 9 | 0 | 3 | 60 | 20.0000 | 0 |
| Percentages | 9 | 0 | 3 | 30 | 10.0000 | 0 |
| Blood Relations | 9 | 3 | 3 | 300 | 100.0000 | 300 |
| Calandar | 9 | 3 | 3 | 300 | 100.0000 | 300 |
| Percentages | 9 | 3 | 3 | 300 | 100.0000 | 300 |
| Permutation & Combination | 9 | 3 | 3 | 300 | 100.0000 | 300 |
| Probability | 9 | 0 | 3 | 90 | 30.0000 | 0 |
| Ratios | 9 | 0 | 3 | 120 | 40.0000 | 0 |
| Time and Work | 9 | 0 | 3 | 150 | 50.0000 | 0 |
| Time Speed & Distance | 9 | 1 | 3 | 140 | 46.6667 | 100 |
| Averages | 9 | 3 | 3 | 300 | 100.0000 | 300 |
| Coding and Decoding | 9 | 3 | 3 | 300 | 100.0000 | 300 |
From the above table, i want to add inner query to the main query
query should be something like this:
( select count(t1.watched_percentage) from tbl_student_learning_path t1
WHERE t1.stu_reference_id =34 and t1.student_type='institute'
AND t1.watched_percentage >= 90 group by t1.course_id )
fullwatchedStudentsCount,
And the result should come like this(this is nothing but if first table counttotalWatchedStudents value is 3 it means that there are 2 type of people
1.students watched full (watched_percentage>=90)
2.students still watching (watched_percentage 1-89)
)
| fullwatchedStudentsCount | fullwatchedStudentsSum |
|--------------------------|------------------------|
| 2 | 200 |
| 1 | 100 |
| 0 | 0 |
| 2 | 200 |
| 1 | 100 |
| 1 | 100 |
| 0 | 0 |
| 2 | 200 |
| 2 | 200 |
| 1 | 100 |
| 2 | 200 |
| 1 | 100 |
If I understand correctly you want sum up the students which saw more than 90% in another column:
I'd do it like this:
SELECT tbc.course_name,
slp.course_id,
slp.student_type,
slp.stu_reference_id,
count(slp.course_id) AS counttotalWatchedStudents,
sum(if(slp.watched_percentage>=90, 1, 0)) AS fullwatchedStudentsCount
sum(if(slp.watched_percentage>=90, watched_percentage, 0)) AS fullwatchedStudentsSum
FROM tbl_student_learning_path slp
LEFT JOIN tbl_courses tbc ON tbc.course_pid = slp.course_id
WHERE slp.stu_reference_id =34
AND slp.student_type='institute'
GROUP BY slp.course_id
enter code here
Hope this helps
So you want to count students with a watched_percentage >= 90? Use conditional aggregation for this:
SELECT
tbc.course_name,
slp.course_id,
slp.student_type,
slp.stu_reference_id,
COUNT(*) as counttotalWatchedStudents,
SUM(slp.watched_percentage >= 90) as fullwatchedStudentsCount,
SUM(slp.watched_percentage < 90) as stillwatchedStudentsCount
FROM tbl_student_learning_path slp
LEFT JOIN tbl_courses tbc ON tbc.course_pid = slp.course_id
WHERE slp.stu_reference_id = 34
AND slp.student_type= 'institute'
GROUP BY slp.course_id;
MySQL treats true = 1 and false = 0, so you can simply sum the trues :-)

MySQL Join Query without duplicate values

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]>

How can I order a table from another table's column then run a query?

I'm building a website for our ball team for the fun of it and keeping track of stats using PHP and SQL for the database. I've learned both by reading the manuals and through forums. I'm working on building a query that will display the current longest hitting streak. I stumbled across a page about detecting runs and streaks and am trying to work with that. I'm really new to all this stuff, so maybe I've structured my tables incorrectly.
Table "games"
+--------+------------+------+
| GameID | Date | Time |
+--------+------------+------+
| 1 | 2015/08/19 | 6:30 |
| 2 | 2015/08/20 | 6:30 |
| 3 | 2015/08/22 | 6:30 |
| 4 | 2015/08/24 | 8:00 |
| 5 | 2015/08/24 | 6:30 |
| 6 | 2015/07/15 | 8:00 |
+--------+------------+------+
Table "player"
+--------+----+---+
| GameID | AB | H |
+--------+----+---+
| 1 | 3 | 1 |
| 2 | 4 | 2 |
| 3 | 2 | 0 |
| 4 | 3 | 0 |
| 5 | 2 | 1 |
| 6 | 3 | 0 |
+--------+----+---+
Code
SELECT games.GameID, GR.H,
(SELECT COUNT(*)
FROM player G
WHERE (CASE WHEN G.H > 0 THEN 1 ELSE 0 END) <> (CASE WHEN GR.H > 0 THEN 1 ELSE 0 END)
AND G.GameID <= GR.GameID) as RunGroup
FROM player GR
INNER JOIN games
ON GR.gameID = games.GameID
ORDER BY Date ASC, Time ASC
Basically in order to correctly get the hit streak right, I need to reorder the GameIDs on the "player" table based on the Date (ASC) and Time (ASC) on the "games" table before executing the RunGroup part of the code. Obviously by adding the ORDER BY, everything gets sorted only after the RunGroup has finished querying and results in incorrect data. I've been stuck here for a few days and now need some help.
The Result I currently get is:
+--------+---+----------+
| GameID | H | RunGroup |
+--------+---+----------+
| 6 | 0 | 3 |
| 1 | 1 | 0 |
| 2 | 2 | 0 |
| 3 | 0 | 2 |
| 5 | 1 | 2 |
| 4 | 0 | 2 |
+--------+---+----------+
This is what I'm trying to achieve:
+--------+---+----------+
| GameID | H | RunGroup |
+--------+---+----------+
| 6 | 0 | 0 |
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 0 | 2 |
| 5 | 1 | 2 |
| 4 | 0 | 3 |
+--------+---+----------+
Thanks
Consider the following:
DROP TABLE IF EXISTS games;
CREATE TABLE games
(game_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,date_played DATETIME NOT NULL
);
INSERT INTO games VALUES
(1,'2015/08/19 18:30:00'),
(2,'2015/08/20 18:30:00'),
(3,'2015/08/22 18:30:00'),
(4,'2015/08/24 20:00:00'),
(5,'2015/08/24 18:30:00'),
(6,'2015/07/15 20:00:00');
DROP TABLE IF EXISTS stats;
CREATE TABLE stats
(player_id INT NOT NULL
,game_id INT NOT NULL
,at_bat INT NOT NULL
,hits INT NOT NULL
,PRIMARY KEY(player_id,game_id)
);
INSERT INTO stats VALUES
(1,1,3,1),
(1,2,4,2),
(1,3,2,0),
(1,4,3,0),
(1,5,2,1),
(1,6,3,0),
(2,1,2,1),
(2,2,3,2),
(2,3,3,0),
(2,4,3,1),
(2,5,2,1),
(2,6,3,0);
SELECT x.*
, SUM(y.at_bat) runningAB
, SUM(y.hits) runningH
, SUM(y.hits)/SUM(y.at_bat) BA
FROM
(
SELECT s.*, g.date_played FROM stats s JOIN games g ON g.game_id = s.game_id
) x
JOIN
(
SELECT s.*, g.date_played FROM stats s JOIN games g ON g.game_id = s.game_id
) y
ON y.player_id = x.player_id
AND y.date_played <= x.date_played
GROUP
BY x.player_id
, x.date_played;
+-----------+---------+--------+------+---------------------+-----------+----------+--------+
| player_id | game_id | at_bat | hits | date_played | runningAB | runningH | BA |
+-----------+---------+--------+------+---------------------+-----------+----------+--------+
| 1 | 6 | 3 | 0 | 2015-07-15 20:00:00 | 3 | 0 | 0.0000 |
| 1 | 1 | 3 | 1 | 2015-08-19 18:30:00 | 6 | 1 | 0.1667 |
| 1 | 2 | 4 | 2 | 2015-08-20 18:30:00 | 10 | 3 | 0.3000 |
| 1 | 3 | 2 | 0 | 2015-08-22 18:30:00 | 12 | 3 | 0.2500 |
| 1 | 5 | 2 | 1 | 2015-08-24 18:30:00 | 14 | 4 | 0.2857 |
| 1 | 4 | 3 | 0 | 2015-08-24 20:00:00 | 17 | 4 | 0.2353 |
| 2 | 6 | 3 | 0 | 2015-07-15 20:00:00 | 3 | 0 | 0.0000 |
| 2 | 1 | 2 | 1 | 2015-08-19 18:30:00 | 5 | 1 | 0.2000 |
| 2 | 2 | 3 | 2 | 2015-08-20 18:30:00 | 8 | 3 | 0.3750 |
| 2 | 3 | 3 | 0 | 2015-08-22 18:30:00 | 11 | 3 | 0.2727 |
| 2 | 5 | 2 | 1 | 2015-08-24 18:30:00 | 13 | 4 | 0.3077 |
| 2 | 4 | 3 | 1 | 2015-08-24 20:00:00 | 16 | 5 | 0.3125 |
+-----------+---------+--------+------+---------------------+-----------+----------+--------+
I rebuilt my database to have only one table to contain the stats from all players. From there i was able to use this query to find my longest current hitting streak for a certain player.
SELECT *
FROM (SELECT (CASE WHEN h > 0 THEN 1 ELSE 0 END) As H, MIN(date_played) as StartDate,
MAX(date_played) as EndDate, COUNT(*) as Games
FROM (SELECT date_played, (CASE WHEN h > 0 THEN 1 ELSE 0 END) as H, (SELECT COUNT(*)
FROM stats G WHERE ((CASE WHEN G.h > 0 THEN 1 ELSE 0 END) <> (CASE WHEN GR.h > 0 THEN 1 ELSE 0 END))
AND G.date_played <= GR.date_played AND player_id = 13) as RunGroup
FROM stats GR
WHERE player_id = 13) A
GROUP BY H, RunGroup
ORDER BY Min(date_played)) A
WHERE H = 1
ORDER BY Games DESC
LIMIT 1

Get available stocks to sell

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.