Kind of lost on anything more than selects and joins and need help with this. I have a table that maintains attributes of products that are created. There are currently 110k rows in that table. I'm looking for a way to query that data and return data related to changes in the attributes of each product.
+-----------+---------+--------+--------+--------+
| attrib_id | prod_id | height | weight | length |
+-----------+---------+--------+--------+--------+
| 1 | 120 | 20 | 3 | 5 |
| 2 | 101 | 5 | 10 | 20 |
| 3 | 101 | 5 | 10 | 20 |
| 4 | 101 | 5 | 10 | 20 |
| 5 | 120 | 20 | 3 | 5 |
| 6 | 101 | 8 | 10 | 20 |
| 7 | 120 | 20 | 3 | 5 |
| 8 | 101 | 8 | 15 | 30 |
| 9 | 101 | 16 | 15 | 20 |
| 10 | 120 | 20 | 10 | 3 |
+-----------+---------+--------+--------+--------+
I would like to see something like this as an output when ever a product attributes change:
+-----------+---------+-------------+------------+------------+-------------+------------+------------+-------------+------------+------------+
| attrib_id | prod_id | orig_height | new_height | chg_height | orig_weight | new_weight | chg_weight | orig_length | new_length | chg_length |
+-----------+---------+-------------+------------+------------+-------------+------------+------------+-------------+------------+------------+
| 6 | 101 | 5 | 8 | 3 | 10 | | | 20 | | |
| 10 | 120 | 20 | | | 3 | 10 | 7 | 5 | 3 | -2 |
+-----------+---------+-------------+------------+------------+-------------+------------+------------+-------------+------------+------------+
Your expected output is bit incorrect.
You want to find min and max attrib_id and then use aggregation to find the required values:
select attrib_id,
prod_id,
original_height,
case when original_height = new_height then null else new_height end new_height,
nullif(new_height - original_height, 0) chg_height,
original_weight,
case when original_weight = new_weight then null else new_weight end new_weight,
nullif(new_weight - original_weight, 0) chg_weight,
original_length,
case when original_length = new_length then null else new_length end new_length,
nullif(new_length - original_length, 0) chg_length
from (
select t2.max_id attrib_id,
t.prod_id,
max(case when t.attrib_id = t2.min_id then t.height end) original_height,
max(case when t.attrib_id = t2.max_id then t.height end) new_height,
max(case when t.attrib_id = t2.min_id then t.weight end) original_weight,
max(case when t.attrib_id = t2.max_id then t.weight end) new_weight,
max(case when t.attrib_id = t2.min_id then t.length end) original_length,
max(case when t.attrib_id = t2.max_id then t.length end) new_length
from t
join (
select prod_id,
min(attrib_id) min_id,
max(attrib_id) max_id
from t
group by prod_id
) t2 on t.prod_id = t2.prod_id
and t.attrib_id in (t2.min_id, t2.max_id)
group by t.prod_id
) t;
Demo
OK, I will suggest a completely different approach. I thought about it reading the words “when ever a product attributes change” in your question. Other answers recompute all joins and aggregates every time, while your table t is essentially a history log which is bound to grow and grow and your query will be slower and slower. My approach is to create a table report and keep it in sync by means of a trigger. You have to start with two empty tables
DROP TABLE IF EXISTS t;
CREATE TABLE t (attrib_id INT, prod_id INT, height INT, weight INT, length INT);
DROP TABLE IF EXISTS report;
CREATE TABLE report (
attrib_id INT, prod_id INT,
orig_height INT, new_height INT, chg_height INT,
orig_weight INT, new_weight INT, chg_weight INT,
orig_length INT, new_length INT, chg_length INT
);
and then, define the trigger:
DROP TRIGGER IF EXISTS trig;
DELIMITER $$
CREATE TRIGGER trig AFTER INSERT ON t FOR EACH ROW BEGIN
DECLARE old_prod_id, old_height, old_weight, old_length INT;
SELECT prod_id, new_height, new_weight, new_length
INTO old_prod_id, old_height, old_weight, old_length
FROM report
WHERE prod_id = NEW.prod_id;
IF ISNULL(old_prod_id) THEN
INSERT INTO report(attrib_id, prod_id, orig_height, orig_weight, orig_length)
VALUES (NEW.attrib_id, NEW.prod_id, NEW.height, NEW.weight, NEW.length);
ELSEIF old_height != NEW.height OR old_weight != NEW.weight OR old_length != NEW.length
OR ISNULL(old_height) -- First change: I suppose checking one field is enough
THEN
UPDATE report SET
attrib_id = NEW.attrib_id,
new_height = NEW.height, chg_height = NEW.height - orig_height,
new_weight = NEW.weight, chg_weight = NEW.weight - orig_weight,
new_length = NEW.length, chg_length = NEW.length - orig_length
WHERE prod_id = NEW.prod_id;
END IF;
END$$
DELIMITER ;
When you fill t with the values you’ve given us, you get:
> SELECT * FROM report;
+-----------+---------+-------------+------------+------------+-------------+------------+------------+-------------+------------+------------+
| attrib_id | prod_id | orig_height | new_height | chg_height | orig_weight | new_weight | chg_weight | orig_length | new_length | chg_length |
+-----------+---------+-------------+------------+------------+-------------+------------+------------+-------------+------------+------------+
| 10 | 120 | 20 | 20 | 0 | 3 | 10 | 7 | 5 | 3 | -2 |
| 9 | 101 | 5 | 16 | 11 | 10 | 15 | 5 | 20 | 20 | 0 |
+-----------+---------+-------------+------------+------------+-------------+------------+------------+-------------+------------+------------+
and you have a much more flexible situation, where you can easily fine-tune your SELECT ... FROM report query as you like it.
I'm not sure if this is what are you looking for but it tracks all changes:
select attrib_id, prod_id,
height as ori_height, nheight as new_height, (nheight - height) as chg_height,
weight as ori_weight, nweight as new_weight, (nweight - weight) as chg_weight,
length as ori_length, nlength as new_length, (nlength - length) as chg_length
from (
select attr1.attrib_id, attr1.prod_id, attr1.height, attr1.weight, attr1.length
,(select attr2.height from attr attr2
where attr2.prod_id = attr1.prod_id and attr2.attrib_id > attr1.attrib_id limit 1) nheight
,(select attr2.weight from attr attr2
where attr2.prod_id = attr1.prod_id and attr2.attrib_id > attr1.attrib_id limit 1) nweight
,(select attr2.length from attr attr2
where attr2.prod_id = attr1.prod_id and attr2.attrib_id > attr1.attrib_id limit 1) nlength
from attr attr1
order by attr1.prod_id, attr1.attrib_id
) calc
;
It returns all changes, row by row:
+-----------+---------+------------+------------+------------+------------+------------+------------+------------+------------+------------+
| attrib_id | prod_id | ori_height | new_height | chg_height | ori_weight | new_weight | chg_weight | ori_length | new_length | chg_length |
+-----------+---------+------------+------------+------------+------------+------------+------------+------------+------------+------------+
| 2 | 101 | 5 | 5 | 0 | 10 | 10 | 0 | 20 | 20 | 0 |
| 3 | 101 | 5 | 5 | 0 | 10 | 10 | 0 | 20 | 20 | 0 |
| 4 | 101 | 5 | 8 | 3 | 10 | 10 | 0 | 20 | 20 | 0 |
| 6 | 101 | 8 | 8 | 0 | 10 | 15 | 5 | 20 | 30 | 10 |
| 8 | 101 | 8 | 16 | 8 | 15 | 15 | 0 | 30 | 20 | -10 |
| 9 | 101 | 16 | NULL | NULL | 15 | NULL | NULL | 20 | NULL | NULL |
+-----------+---------+------------+------------+------------+------------+------------+------------+------------+------------+------------+
| 1 | 120 | 20 | 20 | 0 | 3 | 3 | 0 | 5 | 5 | 0 |
| 5 | 120 | 20 | 20 | 0 | 3 | 3 | 0 | 5 | 5 | 0 |
| 7 | 120 | 20 | 20 | 0 | 3 | 10 | 7 | 5 | 3 | -2 |
| 10 | 120 | 20 | NULL | NULL | 10 | NULL | NULL | 3 | NULL | NULL |
+-----------+---------+------------+------------+------------+------------+------------+------------+------------+------------+------------+
Related
From the following game table of sports matches:
+-----+-----------+---------------+----------+-------+-------+---------+---------+
| id_ | date_time | tournament_id | round_id | p1_id | p2_id | p1_stat | p2_stat |
+-----+-----------+---------------+----------+-------+-------+---------+---------+
| 1 | NULL | 1 | 4 | 1 | 3 | 2 | 3 |
| 2 | NULL | 1 | 5 | 1 | 4 | 4 | 6 |
| 3 | NULL | 1 | 9 | 1 | 5 | 6 | 9 |
| 4 | NULL | 1 | 10 | 2 | 1 | 8 | 12 |
| 5 | NULL | 2 | 4 | 1 | 2 | 10 | 15 |
| 6 | NULL | 2 | 5 | 4 | 1 | 12 | 18 |
+-----+-----------+---------------+----------+-------+-------+---------+---------+
I'm trying to get the stats for each player for their previous match. The output should look like this:
+-----+--------------+--------------+
| id_ | prev_p1_stat | prev_p2_stat |
+-----+--------------+--------------+
| 1 | NULL | NULL |
| 2 | 2 | NULL |
| 3 | 4 | NULL |
| 4 | NULL | 6 |
| 5 | 12 | 8 |
| 6 | 6 | 10 |
+-----+--------------+--------------+
However, the date_time column is quite often blank and id_ is not date sequential. The tournament table does have a date_time column which is always populated:
+-----+------------+
| id_ | date_time |
+-----+------------+
| 1 | 1997-01-01 |
| 2 | 1997-01-06 |
+-----+------------+
This means the tournament date_time can be used in conjunction with game round_id to determine the previous match.
I've found the following answers here and here but they both focus on a single table and don't have the added complexity of having to determine whether the p1_stat or the p2_stat should be selected.
I've got as far as this query:
SELECT
g.id_ AS game_id,
CASE
WHEN g.p1_id = sq_p1.p1_id THEN sq_p1.p1_stat
ELSE sq_p1.p2_stat
END AS prev_p1_stat,
CASE
WHEN g.p1_id = sq_p2.p1_id THEN sq_p2.p1_stat
ELSE sq_p2.p2_stat
END AS prev_p2_stat
FROM
test.game AS g
JOIN
test.tournament AS t ON t.id_ = g.tournament_id
LEFT OUTER JOIN
(SELECT
g.id_ AS match_id,
t.date_time AS tournament_date,
g.round_id,
g.p1_id,
g.p2_id,
g.p1_stat,
g.p2_stat
FROM
test.game AS g
JOIN test.tournament AS t ON t.id_ = g.tournament_id) AS sq_p1 ON (sq_p1.p1_id = g.p1_id
OR sq_p1.p2_id = g.p1_id)
AND (sq_p1.tournament_date = t.date_time
AND sq_p1.round_id < g.round_id
OR sq_p1.tournament_date < t.date_time)
LEFT OUTER JOIN
(SELECT
g.id_ AS match_id,
t.date_time AS tournament_date,
g.round_id,
g.p1_id,
g.p2_id,
g.p1_stat,
g.p2_stat
FROM
test.game AS g
JOIN test.tournament AS t ON t.id_ = g.tournament_id) AS sq_p2 ON (sq_p2.p1_id = g.p1_id
OR sq_p2.p2_id = g.p1_id)
AND (sq_p2.tournament_date = t.date_time
AND sq_p2.round_id < g.round_id
OR sq_p2.tournament_date < t.date_time)
ORDER BY t.date_time , g.round_id
But this isn't even close to what I'm looking for :(
I've created a dbfiddle.
One other thing that's perhaps worth mentioning... I intend to use a couple of versions of this query in a union query such that the final result (including all columns for reference) will look like this:
+-----+------------+-----------+-------------+-------------+---------------+------------------+--------------------+
| id_ | player_num | player_id | opponent_id | player_stat | opponent_stat | player_prev_stat | opponent_prev_stat |
+-----+------------+-----------+-------------+-------------+---------------+------------------+--------------------+
| 1 | 1 | 1 | 3 | 2 | 3 | NULL | NULL |
| 1 | 2 | 3 | 1 | 3 | 2 | NULL | NULL |
| 2 | 1 | 1 | 4 | 4 | 6 | 2 | NULL |
| 2 | 2 | 4 | 1 | 6 | 4 | NULL | 2 |
| 3 | 1 | 1 | 5 | 6 | 9 | 4 | NULL |
| 3 | 2 | 5 | 1 | 9 | 6 | NULL | 4 |
| 4 | 1 | 2 | 1 | 8 | 12 | NULL | 6 |
| 4 | 2 | 1 | 2 | 12 | 8 | 6 | NULL |
| 5 | 1 | 1 | 2 | 10 | 15 | 12 | 8 |
| 5 | 2 | 2 | 1 | 15 | 10 | 8 | 12 |
| 6 | 1 | 4 | 1 | 12 | 18 | 6 | 10 |
| 6 | 2 | 1 | 4 | 18 | 12 | 10 | 6 |
+-----+------------+-----------+-------------+-------------+---------------+------------------+--------------------+
Perhaps it makes more sense to do a union and then engineer the previous stats?
For some final info, the actual game table has about 1.5m rows and the actual tournament table has about 30k rows. I'm using MySQL 8.0.26.
Kudos to #Barmer for the direction - here's the query I created using LAG():
WITH union_matches AS (
SELECT
g.id_ AS match_id,
t.date_time AS tournament_date,
g.round_id AS round_id,
1 AS player_num,
g.p1_id AS player_id,
g.p2_id AS opponent_id,
g.p1_stat AS player_stat,
g.p2_stat AS opponent_stat
FROM
game AS g
JOIN
tournament AS t ON t.id_ = g.tournament_id
UNION SELECT
g.id_ AS match_id,
t.date_time AS tournament_date,
g.round_id AS round_id,
2 AS player_num,
g.p2_id AS player_id,
g.p1_id AS opponent_id,
g.p2_stat AS player_stat,
g.p1_stat AS opponent_stat
FROM
game AS g
JOIN
tournament AS t ON t.id_ = g.tournament_id
)
SELECT
match_id,
player_num,
player_id,
opponent_id,
player_stat,
opponent_stat,
LAG(player_stat, 1) OVER (PARTITION BY player_id ORDER BY tournament_date, round_id) AS wrong_player_prev_stat,
LAG(opponent_stat, 1) OVER (PARTITION BY opponent_id ORDER BY tournament_date, round_id) AS wrong_opponent_prev_stat
FROM
union_matches
ORDER BY
tournament_date, round_id, player_num
And a link to the dbfiddle.
I am using this table from the Northwind dataset (can be generated from query below)
+-----------+-----------+
| NumOrders | CustCount |
+-----------+-----------+
| 1 | 1 |
| 2 | 2 |
| 3 | 7 |
| 4 | 6 |
| 5 | 10 |
| 6 | 8 |
| 7 | 7 |
| 8 | 4 |
| 9 | 5 |
| 10 | 11 |
| 11 | 4 |
| 12 | 3 |
| 13 | 3 |
| 14 | 6 |
| 15 | 3 |
| 17 | 1 |
| 18 | 3 |
| 19 | 2 |
| 28 | 1 |
| 30 | 1 |
| 31 | 1 |
+-----------+-----------+`
And I want to write a query to provide a histogram of the number of x people who made y number of orders
select
case
when NumOrders > 0 and NumOrders <= 5 then '0 - 5'
when NumOrders > 5 and NumOrders <=10 then '6 - 10'
else '10+'
end as Bucket,
CustomerCount = sum(CustCount)
from (
select
NumOrders,
CustCount = count(*)
from (
select *
from (
select
CustomerID,
count(*) as NumOrders
from orders
group by CustomerID
) c
) b
group by NumOrders
)a
group by
(
case
when NumOrders > 0 and NumOrders <= 5 then '0 - 5'
when NumOrders > 5 and NumOrders <=10 then '6 - 10'
else '10+'
end
)
From the query above I am getting this output, which is ordered incorrectly.
+--------+---------------+
| Bucket | CustomerCount |
+--------+---------------+
| 0 - 5 | 26 |
| 10+ | 28 |
| 6 - 10 | 35 |
+--------+---------------+
I would like it to be ordered as
+--------+---------------+
| Bucket | CustomerCount |
+--------+---------------+
| 0 - 5 | 26 |
| 6 - 10 | 35 |
| 10+ | 28 |
+--------+---------------+
Can someone suggest how to order it correctly?
You just need
Order by NumOrders
at the very end of your query
I can't see what part of the problem this fails to solve...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(NumOrders SERIAL PRIMARY KEY
,CustCount INT NOT NULL
);
INSERT INTO my_table VALUES
(1 ,1),
(2 ,2),
(3 ,7),
(4 ,6),
(5 ,0),
(6 ,8),
(7 ,7),
(8 ,4),
(9 ,5),
(10,1),
(11,4),
(12,3),
(13,3),
(14,6),
(15,3),
(17,1),
(18,3),
(19,2),
(28,1),
(30,1),
(31,1);
SELECT CASE WHEN numorders BETWEEN 0 AND 5 THEN '0-5'
WHEN numorders BETWEEN 6 AND 10 THEN '6-10'
ELSE '+10' END bucket
, COUNT(*) total
FROM my_table
GROUP
BY bucket
ORDER
BY numorders;
+--------+-------+
| bucket | total |
+--------+-------+
| 0-5 | 5 |
| 6-10 | 5 |
| +10 | 11 |
+--------+-------+
I am setting up a virtual classroom in PHP and MySQL. This classroom consists of courses and each course contains different subjects or modules. The student has to examine each module and, finally, a summary (evaluation board) is made to know if the student has passed the course or not.
Having said that, I have a table in which I store the evaluations of each student, in which I keep inscripcion_id (student - inscription_id), modulo_id (module_id), fecha (date_of_examination), aciertos (number_of_right_answers), ultima_convocatoria (evaluation_last_convocatory) and estado (status).
SQL Fiddle -> here
Through some previously established rules that tell me if a student has passed a module or not, I get the following set of data:
+----------------+-----------+---------------------+----------+---------------------+--------+------------+
| inscripcion_id | modulo_id | fecha | aciertos | ultima_convocatoria | estado | ev |
+----------------+-----------+---------------------+----------+---------------------+--------+------------+
| 890 | 1 | 2018-01-24 22:26:09 | 8 | 2 | 1 | aprobado |
| 890 | 2 | 2018-01-24 22:36:58 | 3 | 3 | 0 | suspendido |
| 890 | 5 | 2018-01-24 22:38:50 | 3 | 1 | 0 | suspendido |
| 890 | 6 | 2018-01-24 22:44:20 | 7 | 3 | 0 | suspendido |
| 891 | 1 | 2018-01-25 09:24:42 | 8 | 1 | 1 | aprobado |
| 891 | 2 | 2018-01-25 10:01:55 | 4 | 8 | 0 | suspendido |
| 891 | 4 | 2018-01-25 10:51:49 | 5 | 3 | 1 | suspendido |
| 891 | 5 | 2018-01-25 10:23:45 | 9 | 1 | 1 | aprobado |
| 891 | 6 | 2018-01-25 11:21:20 | 7 | 3 | 0 | suspendido |
| 896 | 1 | 2018-01-25 11:55:48 | 1 | 1 | 1 | suspendido |
| 898 | 1 | 2018-01-25 14:01:51 | 6 | 1 | 1 | suspendido |
| 907 | 1 | 2018-03-25 16:06:18 | 3 | 1 | 0 | suspendido |
| 907 | 2 | 2018-03-25 16:07:34 | 3 | 1 | 0 | suspendido |
| 907 | 3 | 2018-03-25 16:09:04 | 3 | 1 | 0 | suspendido |
| 907 | 4 | 2018-03-25 16:08:13 | 3 | 1 | 0 | suspendido |
| 907 | 5 | 2018-03-25 16:10:37 | 2 | 1 | 0 | suspendido |
| 907 | 6 | 2018-03-25 16:08:44 | 3 | 1 | 0 | suspendido |
+----------------+-----------+---------------------+----------+---------------------+--------+------------+
This data is obtained through the following query:
SELECT e1.inscripcion_id,
e1.modulo_id,
e1.fecha,
e1.aciertos,
e1.convocatoria AS ultima_convocatoria,
e1.estado,
if (
( e1.modulo_id in (SELECT modulo.modulo_id FROM modulo WHERE modulo.curso_id = 1 AND modulo.categoria_id = 1)
AND e1.aciertos <= 7 )
OR ( e1.modulo_id = (SELECT modulo.modulo_id FROM modulo WHERE modulo.curso_id = 1 AND modulo.categoria_id = 2)
AND e1.aciertos <= 11 ),
"suspendido",
"aprobado"
) AS ev
FROM (
SELECT inscripcion_id,
modulo_id,
MAX(convocatoria) AS max_convocatoria
FROM `evaluacion`
GROUP BY inscripcion_id,
modulo_id
ORDER BY `inscripcion_id` ASC,
`modulo_id` ASC,
`convocatoria` ASC
) AS e2
INNER JOIN evaluacion AS e1
ON e1.inscripcion_id = e2.inscripcion_id
AND e1.modulo_id = e2.modulo_id
AND e1.convocatoria = e2.max_convocatoria
As you can see, the student 890, has made modules 1, 2, 5 and 6. What I want to achieve is that the modules that are still pending, I also get as a result in the previous data set. The exemplification:
+----------------+-----------+---------------------+----------+---------------------+--------+------------+
| inscripcion_id | modulo_id | fecha | aciertos | ultima_convocatoria | estado | ev |
+----------------+-----------+---------------------+----------+---------------------+--------+------------+
| 890 | 1 | 2018-01-24 22:26:09 | 8 | 2 | 1 | aprobado |
| 890 | 2 | 2018-01-24 22:36:58 | 3 | 3 | 0 | suspendido |
| 890 | 3 | NULL | NULL | NULL | NULL | pendiente |
| 890 | 4 | NULL | NULL | NULL | NULL | pendiente |
| 890 | 5 | 2018-01-24 22:38:50 | 3 | 1 | 0 | suspendido |
| 890 | 6 | 2018-01-24 22:44:20 | 7 | 3 | 0 | suspendido |
| 891 | 1 | 2018-01-25 09:24:42 | 8 | 1 | 1 | aprobado |
| 891 | 2 | 2018-01-25 10:01:55 | 4 | 8 | 0 | suspendido |
| 891 | 3 | NULL | NULL | NULL | NULL | pendiente |
| 891 | 4 | 2018-01-25 10:51:49 | 5 | 3 | 1 | suspendido |
| 891 | 5 | 2018-01-25 10:23:45 | 9 | 1 | 1 | aprobado |
| 891 | 6 | 2018-01-25 11:21:20 | 7 | 3 | 0 | suspendido |
| 896 | 1 | 2018-01-25 11:55:48 | 1 | 1 | 1 | suspendido |
| 896 | 2 | NULL | NULL | NULL | NULL | pendiente |
| 896 | 3 | NULL | NULL | NULL | NULL | pendiente |
| 896 | 4 | NULL | NULL | NULL | NULL | pendiente |
| 896 | 5 | NULL | NULL | NULL | NULL | pendiente |
| 896 | 6 | NULL | NULL | NULL | NULL | pendiente |
| ... | | | | | | |
+----------------+-----------+---------------------+----------+---------------------+--------+------------+
The result is that the modules that the student has not yet done have been added, with the new value "pending" for the ev column.
I have no idea how to do this ... I've tried, I searched the internet and nothing :(
What is the final objective? What I want is to obtain a final list with all those students who have the pending course (that is, they have some pending module/s), to send them a reminder email that they have to examine themselves of the remaining modules. To those who have approved or suspended, no email will be sent to them.
Can you help me?
SQL Fiddle -> here
THANK YOU VERY MUCH
Suppose you want to get students with no module in evaluation table as 'pending' like what you provided in the example. The way to get student joining all module is to do a full join on modulo and evaluacion to get full set of distinct inscripcion_id modulo_id. Then left join with your existing query will provide the result you want.
sqlfiddle
SELECT fs.inscripcion_id,
fs.modulo_id,
e3.fecha,
e3.aciertos,
e3.ultima_convocatoria,
e3.estado,
IF(e3.ev IS NULL, "pendiente", e3.ev) AS ev
FROM (SELECT m.modulo_id,
e.inscripcion_id
FROM modulo m,
evaluacion e
GROUP BY m.modulo_id,
e.inscripcion_id) AS fs
LEFT JOIN (SELECT
e1.inscripcion_id,
e1.modulo_id,
e1.fecha,
e1.aciertos,
e1.convocatoria
AS
ultima_convocatoria
,
e1.estado,
IF (( e1.modulo_id IN (SELECT modulo.modulo_id
FROM modulo
WHERE modulo.curso_id = 1
AND modulo.categoria_id =
1)
AND e1.aciertos <= 7 )
OR ( e1.modulo_id = (SELECT modulo.modulo_id
FROM modulo
WHERE
modulo.curso_id = 1
AND modulo.categoria_id = 2)
AND e1.aciertos <= 11 ), "suspendido",
"aprobado")
AS ev
FROM (SELECT inscripcion_id,
modulo_id,
Max(convocatoria) AS max_convocatoria
FROM `evaluacion`
GROUP BY inscripcion_id,
modulo_id
ORDER BY `inscripcion_id` ASC,
`modulo_id` ASC,
`convocatoria` ASC) AS e2
INNER JOIN evaluacion AS e1
ON e1.inscripcion_id = e2.inscripcion_id
AND e1.modulo_id = e2.modulo_id
AND e1.convocatoria = e2.max_convocatoria)
AS e3
ON fs.modulo_id = e3.modulo_id
AND fs.inscripcion_id = e3.inscripcion_id
ORDER BY fs.inscripcion_id,
fs.modulo_id;
For the further question,
You may want to use
SELECT inscripcion_id,
SUM(case when ev = 'aprobado' then 1 else 0 end) as approved_cnt,
SUM(case when ev = 'suspendido' then 1 else 0 end) as suspended_cnt,
SUM(case when ev = 'pendiente' then 1 else 0 end) as pending_cnt
From --the above query...
Group by inscripcion_id
to get the count of status for each student, and then do the logic using those count.
After reviewing the new sqlfiddle
I wrote the query below, I think it should cover what you want
Notice that you more than one evaluation per module, meaning that you'll get more than one status per module
To solve that, you can add a group statement (in comment now)
Or you the different evaluations to your needs...
SELECT
i.inscripcion_id,
c.curso_id,
c.titulo AS curso_titulo,
m.modulo_id,
m.titulo AS modulo_titulo,
IFNULL(ev.estado,0) AS estado,
m.*
FROM
inscripcion i
INNER JOIN curso c ON c.curso_id = i.curso_id
INNER JOIN modulo m ON m.curso_id = c.curso_id
LEFT JOIN evaluacion ev ON ev.modulo_id = m.modulo_id
WHERE
(ev.estado = 0 OR ev.estado IS NULL)
/*
GROUP BY
m.modulo_id
*/
;
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'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