MySQL UNION ALL (Full Join) with conditional aggregation - mysql

I have three tables:
CREATE TABLE `Agreement` (
`AID` bigint(20) NOT NULL AUTO_INCREMENT,
`FLAGS` bigint(20) NOT NULL DEFAULT '0',
PRIMARY KEY (`AID`)
);
CREATE TABLE `Assessment` (
`ASMID` bigint(20) NOT NULL AUTO_INCREMENT,
`AID` bigint(20) NOT NULL DEFAULT '0',
`Amount` decimal(19,4) NOT NULL DEFAULT '0.0000',
`Description` text,
PRIMARY KEY (`ASMID`)
);
CREATE TABLE `Payment` (
`RID` bigint(20) NOT NULL AUTO_INCREMENT,
`AID` bigint(20) NOT NULL DEFAULT '0',
`ASMID` bigint(20) NOT NULL DEFAULT '0',
`Amount` decimal(19,4) NOT NULL DEFAULT '0.0000',
`Description` text,
PRIMARY KEY (`RID`)
);
And I'm inserting one Agreement, three Assessment, five Payment rows mentioned as below:
INSERT INTO Agreement(FLAGS) VALUES(0);
INSERT INTO Assessment(AID, Amount, Description) VALUES (1, 1200, "Rent");
INSERT INTO Assessment(AID, Amount, Description) VALUES (1, 20, "Damage - car break");
INSERT INTO Assessment(AID, Amount, Description) VALUES (1, 500, "Damage - vehicle");
INSERT INTO Payment(AID, ASMID, Amount, Description) VALUES(1, 1, 500, "Rent Fee");
INSERT INTO Payment(AID, ASMID, Amount, Description) VALUES(1, 1, 600, "Rent Fee");
INSERT INTO Payment(AID, ASMID, Amount, Description) VALUES(1, 2, 20, "Damage Fee");
INSERT INTO Payment(AID, Amount, Description) VALUES(1, 600, "Deposit Fee");
INSERT INTO Payment(AID, Amount, Description) VALUES(1, 50, "Application Fee");
and When I see the data, so it should look like this:
mysql> SELECT * FROM Agreement;
+-----+-------+
| AID | FLAGS |
+-----+-------+
| 1 | 0 |
+-----+-------+
1 row in set (0.00 sec)
mysql> SELECT * FROM Assessment;
+-------+-----+-----------+--------------------+
| ASMID | AID | Amount | Description |
+-------+-----+-----------+--------------------+
| 1 | 1 | 1200.0000 | Rent |
| 2 | 1 | 20.0000 | Damage - car break |
| 3 | 1 | 500.0000 | Damage - vehicle |
+-------+-----+-----------+--------------------+
3 rows in set (0.00 sec)
mysql> SELECT * FROM Payment;
+-----+-----+-------+----------+-----------------+
| RID | AID | ASMID | Amount | Description |
+-----+-----+-------+----------+-----------------+
| 1 | 1 | 1 | 500.0000 | Rent Fee |
| 2 | 1 | 1 | 600.0000 | Rent Fee |
| 3 | 1 | 2 | 20.0000 | Damage Fee |
| 4 | 1 | 0 | 600.0000 | Deposit Fee |
| 5 | 1 | 0 | 50.0000 | Application Fee |
+-----+-----+-------+----------+-----------------+
5 rows in set (0.00 sec)
So, any Agreement has multiple Assessments all need to be paid in near future. It may have multiple Payments which might be associated with Assessment (i.e., Rent Fee) or might be not (i.e., Application Fee).
Now, in reality, there are multiple Agreements which have multiple Assessments and multiple Payments.
Now I want the result that covers all of the rows from both tables Assessment and Payment associated with Agreement GROUPED BY first Agreement, second Assessment. Additionally, I need to aggregate AMOUNT as PaymentsApplied from the table Payment table for each Assessment so that we can compare it with Amount from the table Assessment as AmountDue. Plus if any Payment is not associated with any Assessment then don't do aggregation. The result would look like this:
+-----+-------+-----------+-----------------+--------------------+-----------------+
| AID | ASMID | AmountDue | PaymentsApplied | ASM-Descr | PMT-Description |
+-----+-------+-----------+-----------------+--------------------+-----------------+
| 1 | 1 | 1200.0000 | 1100.0000 | Rent | Rent Fee |
| 1 | 2 | 20.0000 | 20.0000 | Damage - car break | Damange Fee |
| 1 | 3 | 500.0000 | NULL | Damage - vehicle | NULL |
| 1 | 0 | NULL | 600.0000 | NULL | Deposit Fee |
| 1 | 0 | NULL | 50.0000 | NULL | Application Fee |
+-----+-------+-----------+-----------------+--------------------+-----------------+
5 Rows
I tried my best to explain the situation. Actually, in my application query has joins over 10 tables like Agreement!
Any Help would be most welcome!!
UPDATE 1
I've started from this query,
(SELECT DISTINCT
Payment.RID, Payment.Amount as PaymentsApplied, Payment.ASMID as PMT_ASMID, null as AmountDue, null AS ASMID
FROM Payment
LEFT JOIN Assessment ON Assessment.ASMID=Payment.ASMID)
UNION
(SELECT DISTINCT
null, null, null, Assessment.Amount, Assessment.ASMID
FROM Assessment
LEFT JOIN Payment ON Payment.ASMID=Assessment.ASMID)
ORDER BY ASMID, PMT_ASMID;
which gives me result,
+------+-----------------+-----------+-----------+-------+
| RID | PaymentsApplied | PMT_ASMID | AmountDue | ASMID |
+------+-----------------+-----------+-----------+-------+
| NULL | NULL | NULL | 1200.0000 | 1 |
| NULL | NULL | NULL | 20.0000 | 2 |
| NULL | NULL | NULL | 500.0000 | 3 |
| 1 | 500.0000 | 1 | NULL | NULL |
| 2 | 600.0000 | 1 | NULL | NULL |
| 3 | 20.0000 | 2 | NULL | NULL |
| 4 | 600.0000 | 0 | NULL | NULL |
| 5 | 50.0000 | 0 | NULL | NULL |
+------+-----------------+-----------+-----------+-------+
8 rows in set (0.01 sec)
Now, from this point, IDK how to aggregate Payment rows by Assessment ID (ASMID) and do join with Agreement table also?
UPDATE 2
I've made sqlfiddle link just in case someone wants to try it.
I've added conditional aggregation in my query,
(SELECT DISTINCT
null as AmountDue,
null AS ASMID,
null as ASM_Descr,
Payment.Description as PMT_Descr,
(CASE WHEN Payment.ASMID > 0 THEN SUM(Payment.Amount) ELSE Payment.Amount END) as PaymentsApplied,
(CASE WHEN Payment.ASMID > 0 THEN GROUP_CONCAT(Payment.RID) ELSE Payment.RID END) as PaymentList,
Payment.ASMID as PMT_ASMID
FROM Payment
LEFT JOIN Assessment ON Assessment.ASMID=Payment.ASMID
GROUP BY Assessment.ASMID)
UNION ALL
(SELECT DISTINCT
Assessment.Amount,
Assessment.ASMID,
Assessment.Description,
null,
null,
null,
null
FROM Assessment
LEFT JOIN Payment ON Payment.ASMID=Assessment.ASMID
GROUP BY Assessment.ASMID)
ORDER BY ASMID, PMT_ASMID;
which gives me,
+-----------+-------+--------------------+-------------+-----------------+-------------+-----------+
| AmountDue | ASMID | ASM_Descr | PMT_Descr | PaymentsApplied | PaymentList | PMT_ASMID |
+-----------+-------+--------------------+-------------+-----------------+-------------+-----------+
| NULL | NULL | NULL | Deposit Fee | 600.0000 | 4 | 0 |
| NULL | NULL | NULL | Rent Fee | 1100.0000 | 1,2 | 1 |
| NULL | NULL | NULL | Damage Fee | 20.0000 | 3 | 2 |
| 1200.0000 | 1 | Rent | NULL | NULL | NULL | NULL |
| 20.0000 | 2 | Damage - car break | NULL | NULL | NULL | NULL |
| 500.0000 | 3 | Damage - vehicle | NULL | NULL | NULL | NULL |
+-----------+-------+--------------------+-------------+-----------------+-------------+-----------+
But, this one is still missing one row from Payment row (RID: 5) and I'm not getting the expected result.

I'd first collect all assessments, left join them to the payments and then union all payments without assessments:
# Assessments with payments
SELECT asm.AID,
asm.ASMID,
min(asm.Amount) AS AmountDue,
SUM(pam.Amount) AS PaymentsApplied,
asm.Description AS `ASM-Descr`,
pam.Description AS `PMT-Descr`,
agr.FLAGS
FROM Assessment asm
LEFT JOIN Payment pam ON pam.ASMID = asm.ASMID
JOIN Agreement agr ON agr.AID = asm.AID
GROUP BY asm.AID,
asm.ASMID
UNION # Payments without assessments
SELECT pam.AID,
pam.ASMID,
NULL AS AmountDue,
SUM(pam.Amount) AS PaymentsApplied,
NULL AS `ASM-Descr`,
pam.Description AS `PMT-Descr`,
agr.FLAGS
FROM Payment pam
LEFT JOIN Assessment asm ON pam.ASMID = asm.ASMID
JOIN Agreement agr ON agr.AID = pam.AID
WHERE asm.ASMID IS NULL
GROUP BY pam.AID, pam.RID;
If you want to add more information you could wrap this result, give it a name and join more tables to the temporary result:
SELECT payment_overview.*,
p.name
FROM
( # Assessments with payments
SELECT asm.AID,
asm.ASMID,
min(asm.Amount) AS AmountDue,
SUM(pam.Amount) AS PaymentsApplied,
asm.Description AS `ASM-Descr`,
pam.Description AS `PMT-Descr`,
agr.FLAGS
FROM Assessment asm
LEFT JOIN Payment pam ON pam.ASMID = asm.ASMID
JOIN Agreement agr ON agr.AID = asm.AID
GROUP BY asm.AID,
asm.ASMID
UNION # Payments without assessments
SELECT pam.AID,
pam.ASMID,
NULL AS AmountDue,
SUM(pam.Amount) AS PaymentsApplied,
NULL AS `ASM-Descr`,
pam.Description AS `PMT-Descr`,
agr.FLAGS
FROM Payment pam
LEFT JOIN Assessment asm ON pam.ASMID = asm.ASMID
JOIN Agreement agr ON agr.AID = pam.AID
WHERE asm.ASMID IS NULL
GROUP BY pam.AID,
pam.RID ) AS payment_overview
JOIN Payor p ON p.AID = payment_overview.AID ;

It seems you want a result row per AID + ASMID + PMT-Description. So:
Select from payments and aggregate.
Select from assessments.
Full outer join the two, as there can be payments without accessments and accessments without payments.
MySQL lacks FULL OUTER JOIN. So write the same query twice, once with LEFT OUTER JOIN, once with RIGHT OUTER JOIN, and use UNION on the two result sets.
select
p.aid,
p.asmid,
a.amount as amount_due,
p.payments_applied,
a.description as asm_description,
p.description as pmt_description
from
(
select aid, asmid, description, sum(amount) as payments_applied
from payment
group by aid, asmid, description
) p
left join assessment a on a.aid = p.aid and a.asmid = p.asmid
union
select
p.aid,
p.asmid,
a.amount as amount_due,
p.payments_applied,
a.description as asm_description,
p.description as pmt_description
from
(
select aid, asmid, description, sum(amount) as payments_applied
from payment
group by aid, asmid, description
) p
right join assessment a on a.aid = p.aid and a.asmid = p.asmid
order by aid, asmid, pmt_description;
Once MySQL features FULL OUTER JOIN you can cut this query in half.

Related

How to organize my query with so many ANDs

My query looks like:
SELECT SUM(ct_product_store_quantity.quantity) as quantity, `ct_product`.*
FROM `ct_product`
LEFT JOIN `ct_productLang` ON `ct_product`.`id` = `ct_productLang`.`product_id`
LEFT JOIN `ct_product_store_quantity` ON `ct_product`.`id` = `ct_product_store_quantity`.`product_id`
LEFT JOIN `ct_product_attribute` as cpa ON ct_product.id=cpa.product_id
WHERE cpa.attribute_id=10
AND cpa.attribute_value_id=36
AND cpa.attribute_id=2
AND cpa.attribute_value_id=5
AND cpa.attribute_id=7
AND cpa.attribute_value_id=31
AND cpa.attribute_id=9
AND cpa.attribute_value_id=28
AND cpa.attribute_id=8
AND cpa.attribute_value_id=25
GROUP BY `ct_product`.`id`
HAVING quantity > 0
ORDER BY `id` DESC
In simple words - each of the AND condtitions evaluate to true. If I execute them one by one it is OK. But when I try to execute it like what I posted above - no results are returned. I am sure am not doing right the multiple AND conditions part. The ct_product_attribute table:
+--------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| product_id | int(11) | YES | MUL | NULL | |
| attribute_set_id | int(11) | YES | MUL | NULL | |
| attribute_id | int(11) | YES | MUL | NULL | |
| attribute_value_id | int(11) | YES | MUL | NULL | |
| value | varchar(255) | YES | | NULL | |
+--------------------+--------------+------+-----+---------+----------------+
Will post the other tables if needed. Just trying to not flood the post. Thank you!
EDIT
In ct_product I got products like ( just for example ):
id
1
2
3
In ct_product_attribute each product can have more than one attribute-attr.value pairs. Some of the pairs are same.( will show only the columns that I need )
id product_id attribute_id attribute_value_id
1 1 1 1
2 2 1 1
3 1 2 1
4 2 3 1
5 3 1 1
6 3 2 1
The values that I get from the request are:
attribute_id=1
attribute_value_id=1
attribute_id=2
attribute_value_id=1
And now I have to retrieve only the product with id=1. If I use OR it is retrieving both products id=1 and id=2. Not sure if it gets more clear now.
I'm pretty sure those are supposed to be ORs because you can't have all those IDs at the same time. With that in mind, you should be able to use IN.
WHERE cpa.attribute_id IN (10,2,7,9,8)
AND cpa.attribute_value_id IN (36,5,31,28,25)
I really don't know what you are trying to accomplish but you should/could use WHERE IN, as everyone pointed in the comments you are looking for a field with multiple values...
But, as for the AND question, you could/should use IN, as in;
SELECT SUM(ct_product_store_quantity.quantity) as quantity, `ct_product`.*
FROM `ct_product`
LEFT JOIN `ct_productLang` ON `ct_product`.`id` = `ct_productLang`.`product_id`
LEFT JOIN `ct_product_store_quantity` ON `ct_product`.`id` = `ct_product_store_quantity`.`product_id`
LEFT JOIN `ct_product_attribute` as cpa ON ct_product.id=cpa.product_id
WHERE cpa.attribute_id IN (10, 2, 7, 9, 8)
AND cpa.attribute_value_id IN (36, 5, 31, 28, 25)
GROUP BY `ct_product`.`id`
HAVING quantity > 0
ORDER BY `id` DESC
You can try using (cpa.attribute_id,cpa.attribute_value_id) in ((10,36),(2,5),(7,31),(9,28),(8,25))
SELECT SUM(ct_product_store_quantity.quantity) as quantity, `ct_product`.*
FROM `ct_product`
LEFT JOIN `ct_productLang` ON `ct_product`.`id` = `ct_productLang`.`product_id`
LEFT JOIN `ct_product_store_quantity` ON `ct_product`.`id` = `ct_product_store_quantity`.`product_id`
LEFT JOIN `ct_product_attribute` as cpa ON ct_product.id=cpa.product_id
WHERE (cpa.attribute_id,cpa.attribute_value_id) in ((10,36),(2,5),(7,31),(9,28),(8,25)) and `ct_product`.`id`=1
GROUP BY `ct_product`.`id`
HAVING quantity > 0
ORDER BY `id` DESC

MySql - Getting rowid / order in a complex query [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
Working on a project with students/grades/etc, I need to update the top 3 students every once in a while. I came up with the query below. However, I am having trouble getting their rank/order. I know how to do that in a simple query, but in a more complex one, it is not working.
I am getting all of the other columns correctly, and, with all the methods I tried to get the order by, I sometimes got 0 (like the current state of the code), sometimes values that are just wrong (1, 11, 10), etc.
NOTE: I have checked various questions (including the question below), but I just couldn't figure out how to place them in my query.
What is the best way to generate ranks in MYSQL?
Summary:
GOAL:
- Get sum of each students' marks from marks, divide that on the number of entries in the table (again marks). Students are from a given grade.
- Use sum(mark) to rank these students.
- Get the top three.
- Place the top three students from that grade in the TopStudents table, with their average marks (as sum) and their id's.
TABLES:
Students table contains info about student including id:
+-------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------------+------+-----+---------+----------------+
| id | int (20) unsigned | NO | PRI | NULL | auto_increment |
| name |varchar(20) unsigned | NO | | NULL | |
+-------------+---------------------+------+-----+---------+----------------+
Marks Table has marks of each student on each exam
+-------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------------+------+-----+---------+----------------+
| id |int (20) unsigned | NO | PRI | NULL | auto_increment |
| idStudent |int (20) unsigned | NO | FOR | NULL | |
| mark |tinyInt (3) unsigned | NO | | NULL | |
| idExam |int (20) unsigned | NO | FOR | NULL | |
+-------------+---------------------+------+-----+---------+----------------+
Grade Table has grade id and name:
+-------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------------+------+-----+---------+----------------+
| id | int (20) unsigned | NO | PRI | NULL | auto_increment |
| name |varchar(20) unsigned | NO | | NULL | |
+-------------+---------------------+------+-----+---------+----------------+
Class Table classes for each grade. References table
+-------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------------+------+-----+---------+----------------+
| id | int (20) unsigned | NO | PRI | NULL | auto_increment |
| name |varchar(20) unsigned | NO | | NULL | |
| idGrade | int (20) unsigned | NO | FOR | NULL | |
+-------------+---------------------+------+-----+---------+----------------+
and finally, the infamous TopStudents Table .
+-------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------------+------+-----+---------+----------------+
| id | int (20) unsigned | NO | PRI | NULL | auto_increment |
| idStudent | int (20) unsigned | NO | FOR | NULL | |
| sumMarks | int (20) unsigned | NO | | NULL | |
| rank |tinyInt (1) unsigned | NO | | NULL | |
| date |date unsigned | NO | | NULL | |
+-------------+---------------------+------+-----+---------+----------------+
ATTEMPTS:
Attempt 1: ERROR: all ranks are 0
INSERT INTO topStudents(`date`, idStudent, `sum`, `order`)
SELECT
'2018-10-10' AS DATE,
student.id AS idStudent,
AVG(marks.mark)
#n = #n + 1 AS `order`
FROM
marks
INNER JOIN student ON student.id = marks.idStudent
INNER JOIN class ON class.id = marks.idClass
INNER JOIN grade ON class.idGrade = grade.id
WHERE
grade.id = 2
GROUP BY
marks.idStudent
ORDER BY
SUM(mark)
DESC
LIMIT 3
Attempt 2: ranks returned: 1, 11, 10
SET #n := 0;
INSERT INTO topStudents(`date`, idStudent, `sum`, `rank`)
SELECT
'2018-10-10' AS DATE,
tbl.idStudent AS idStudent,
AVG(tbl.mark) AS `sum`,
rnk AS `rank`
FROM (SELECT student.id AS idStudent, SUM(mark) AS mark FROM
marks
INNER JOIN student ON student.id = marks.idStudent
INNER JOIN class ON class.id = marks.idClass
INNER JOIN grade ON class.idGrade = grade.id
WHERE
grade.id = 2
GROUP BY
marks.idStudent
ORDER BY
SUM(mark)
DESC
LIMIT 3) AS tbl, (SELECT #n = #n + 1) AS rnk
In more recent versions of MySQL, you need to use a derived table for the ordering, before assigning the ranks:
INSERT INTO topStudents (`date`, idStudent, `sum`, `order`)
SELECT date, idStudent, `sum`, (#n := #n + 1) AS `order`
FROM (SELECT '2018-10-10' AS DATE, s.id AS idStudent,
SUM(m.mark) / (SELECT COUNT(*) FROM marks m2 WHERE m2.idStudent = m.idStudent) AS `sum`
FROM marks m JOIN
student s
ON s.id = m.idStudent JOIN
class c
ON c.id = m.idClass JOIN
grade g
ON c.idGrade = g.id
WHERE g.id = 2
GROUP BY m.idStudent
ORDER BY SUM(mark) DESC
LIMIT 3
) sm CROSS JOIN
(SELECT #n := 0) params;
I am almost certain that the calculation for sum is incorrect, and that you really intend avg(mark). However, this is the logic you have in your question.

Select maximum value of each member from table [duplicate]

This question already has answers here:
Fetch the rows which have the Max value for a column for each distinct value of another column
(35 answers)
Closed 8 years ago.
I want to select the best result of each member from the mysql table, for a given discipline.
(if there are entries with the same value, the entries with the lowest event start date should be taken)
DDLs:
CREATE TABLE `results` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`discipline` int(11) NOT NULL,
`member` int(11) DEFAULT '0',
`event` int(11) DEFAULT '0',
`value` int(11) DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `member_2` (`member`,`discipline`,`event`)
);
INSERT INTO results VALUES
(1,1,2,4,10),
(2,1,1,4, 8),
(3,1,2,5, 9),
(4,2,3,5, 9),
(5,1,2,6,11),
(6,1,2,7,11),
(7,1,2,1,11),
(8,1,2,3, 7);
CREATE TABLE `events` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
`startDate` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO events VALUES
(1 ,'Not in scope','2012-05-23'),
(3 ,'Test 0', '2014-05-09'),
(4 ,'Test 1', '2014-05-10'),
(5 ,'Test 2', '2014-05-11'),
(6 ,'Test 3', '2014-05-12'),
(7 ,'Test 4', '2014-05-13');
SELECT * FROM results;
+----+------------+--------+-------+-------+
| id | discipline | member | event | value |
+----+------------+--------+-------+-------+
| 1 | 1 | 2 | 4 | 10 |
| 2 | 1 | 1 | 4 | 8 |
| 3 | 1 | 2 | 5 | 9 |
| 4 | 2 | 3 | 5 | 9 |
| 5 | 1 | 2 | 6 | 11 |
| 6 | 1 | 2 | 7 | 11 |
| 7 | 1 | 2 | 1 | 11 |
| 8 | 1 | 2 | 3 | 7 |
+----+------------+--------+-------+-------+
SELECT * FROM events;
+----+--------------+---------------------+
| id | name | startDate |
+----+--------------+---------------------+
| 1 | Not in scope | 2012-05-23 00:00:00 |
| 3 | Test 0 | 2014-05-09 00:00:00 |
| 4 | Test 1 | 2014-05-10 00:00:00 |
| 5 | Test 2 | 2014-05-11 00:00:00 |
| 6 | Test 3 | 2014-05-12 00:00:00 |
| 7 | Test 4 | 2014-05-13 00:00:00 |
+----+--------------+---------------------+
Result should be:
+---------+------------+--------+-------+-------+
| id | discipline | member | event | value |
+---------+------------+--------+-------+-------+
| 3 | 1 | 1 | 4 | 8 |
| 5 | 1 | 2 | 6 | 11 |
+---------+------------+--------+-------+-------+
My first approach was to group by member id, but it's not that easy. So I tried a lot of different approaches from the web and from my colleages.
The last one was:
select res.*
from `results` as res
join (select id, max(value)
from results
join events on results.event = events.id
where discipline = 1
events.name like 'Test%'
Group by id
Order by events.startDate ASC) as tmpRes
on res.id = tmpRes.id
group by member
order by value DESC
But the result in this example would be a random result id for member 2.
Should be correct now, but let me know if there's a mistake...
SELECT r.*
FROM events e
JOIN results r
ON r.event = e.id
JOIN
( SELECT r.member
, MIN(e.startdate) min_startdate
FROM events e
JOIN results r
ON r.event = e.id
JOIN
( SELECT member
, MAX(value) max_value
, discipline
FROM events e
JOIN results r
ON r.event = e.id
WHERE discipline = 1
AND name LIKE 'Test%'
GROUP
BY member
) x
ON x.member = r.member
AND x.max_value = r.value
AND x.discipline = r.discipline
AND e.name LIKE 'Test%'
GROUP
BY member
) y
ON y.member = r.member
AND y.min_startdate = e.startdate;
Although fast, because these queries can get rather complex and cumbersome, there's an undocumented hack that achieves the same result. It goes something like this...
SELECT *
FROM
( SELECT r.*
FROM events e
JOIN results r
ON r.event = e.id
WHERE discipline = 1
AND name LIKE 'Test%'
ORDER
BY member
, value DESC
, startdate
) x
GROUP
BY member;
If I understand your question correctly, you need to group on member in the sub-query. Try the following:
select res.*
from `results` as res
join (select member, min(event) AS minEvent, max(value) AS maxValue
from results
where discipline = 1
Group by member) as tmpRes
on res.member = tmpRes.member AND res.event=tmpRes.minEvent AND res.value=tmpRes.maxValue
order by res.value
EDIT (bast on most recent comment): If that's the case, you'll need to join on the Events table. Unless the startDate field is actually a temporal field, it's going to be a big mess.
It would have made things easier with all the requirements included in the original question.

count the sum of a column where data is contained in multiple columns

I have a debating competition where I want to find out which team has won and lost the most debates. The problem I am having is the id of the two teams competing in the debate is in two different columns (hostid, visitid).
I have the below so far which gives me what I want however it only shows the visitid data.
CREATE TABLE teams (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255)
) DEFAULT CHARACTER SET utf8 ENGINE=InnoDB;
CREATE TABLE debates (
debateid INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
debatedate DATE NOT NULL,
hostid INT,
visitid INT,
winnerid INT
) DEFAULT CHARACTER SET utf8 ENGINE=InnoDB;
INSERT INTO teams (id, name) VALUES
(1,'team one'),
(2,'team two'),
(3,'team three'),
(4,'team four'),
(5,'team five'),
(6,'team six');
INSERT INTO debates (debateid, debatedate,hostid, visitid, winnerid ) VALUES
(1,'2012-01-11', 1,2,1),
(2,'2012-01-11', 3,4,4),
(3,'2012-02-11', 5,6,5),
(4,'2012-02-11', 1,4,1),
(5,'2012-02-11', 2,5,5),
(6,'2012-02-11', 3,6,3),
(7,'2012-03-11', 6,1,1),
(8,'2012-03-11', 5,2,5),
(9,'2012-03-11', 3,4,4);
SELECT
visitid AS id,
t.name AS name,
sum(visitid= deb.winnerid) as w,
sum(visitid != deb.winnerid) as l
FROM debates AS deb
JOIN teams t ON t.id = deb.visitid
WHERE visitid != -1
AND debatedate < CURDATE( )
GROUP BY id
ORDER BY w DESC
RESULT
-----------------------------------------
| ID | NAME | W | L |
| 4 | team four | 2 | 1 |
| 5 | team five | 1 | 0 |
| 1 | team one | 1 | 0 |
| 6 | team six | 0 | 2 |
| 2 | team two | 0 | 2 |
-----------------------------------------
How can I combine these two columns, I am aware of union but I can't think of a way to implement this in this situation or what method I should use?
If I had it working as intended the result would be the below eg where hostid or visitid = winnerid
-----------------------------------------
| ID | NAME | W | L |
| 1 | team one | 3 | 0 |
| 5 | team five | 3 | 0 |
| 4 | team four | 2 | 1 |
| 3 | team three | 1 | 2 |
| 2 | team two | 0 | 3 |
| 6 | team six | 0 | 3 |
-----------------------------------------
See fiidle for example
SELECT t.id,
t.name,
SUM(t.id = d.winnerid) AS w,
SUM(t.id != d.winnerid) AS l
FROM debates AS d
JOIN teams AS t ON t.id IN (d.hostid, d.visitid)
WHERE d.visitid != -1 -- not sure what purpose this serves
AND d.debatedate < CURDATE()
GROUP BY t.id
ORDER BY w DESC
See it on sqlfiddle.

Select from rental table in mysql to display items currently rented by user

If you know about sakila sample database, then what is the statement to select items currently rented by a user.
If not here is a code explanation:
CREATE TABLE IF NOT EXISTS `rentals` (
`item_id` int(10) unsigned NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`last_change_date` date NOT NULL,
PRIMARY KEY (`item_id`,`user_id`,`last_change_date`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Notice there is no return date, this is because the last renter has indefinite rental time, until somebody requests this item and it's transferred to the new renter for indefinite time as well. A user can rent more than one item and there is only one piece of each item so an item_id cannot go to two users at the same time.
I would like to display currently rented items per user_id.
SELECT r1.*
FROM rentals r1
LEFT JOIN rentals AS r2
ON r1.item_id = r2.item_id
AND r1.last_change_date < r2.last_change_date
WHERE r2.last_change_date IS NULL
This is a classic sql question. The answer is explained here.
Using
INSERT INTO rentals (item_id, user_id, last_change_date) VALUES (1, 1, '2009-01-01'), (1, 3, '2009-11-10'), (3, 3, '2009-02-13'), (3, 5, '2010-05-11'), (5, 5, '2010-06-04'), (7, 7, '2010-06-04'), (9, 9, '2010-06-04');
as play-data, to understand the method, look at the output of
SELECT r1.*,r2.*
FROM rentals r1
LEFT JOIN rentals AS r2
ON r1.item_id = r2.item_id
AND r1.last_change_date < r2.last_change_date
+---------+---------+------------------+---------+---------+------------------+
| item_id | user_id | last_change_date | item_id | user_id | last_change_date |
+---------+---------+------------------+---------+---------+------------------+
| 1 | 1 | 2009-01-01 | 1 | 3 | 2009-11-10 |
| 1 | 3 | 2009-11-10 | NULL | NULL | NULL |
| 3 | 3 | 2009-02-13 | 3 | 5 | 2010-05-11 |
| 3 | 5 | 2010-05-11 | NULL | NULL | NULL |
| 5 | 5 | 2010-06-04 | NULL | NULL | NULL |
| 7 | 7 | 2010-06-04 | NULL | NULL | NULL |
| 9 | 9 | 2010-06-04 | NULL | NULL | NULL |
+---------+---------+------------------+---------+---------+------------------+
The first 3 columns refer to r1's columns, the last 3 refer to r2's.
As you can see, whenever there is no r2.last_change_date which is greater than r1.last_change_date, the value is NULL. Those are the rows where r1.last_change_date is greatest. So to find the rows you want, you use
the condition
WHERE r2.last_change_date IS NULL