mysql left join take rows together - mysql

I have 3 tables: invoice, person and payement.
I want to have a list of invoices with the client name (from person) and the sum of payements and dates of payements(from payement).
First I made these statement
SELECT V.id, V.datum, V.amount, P.name AS 'client',
(SELECT SUM(B.amount) FROM payement AS B WHERE B.invoiceId = V.id) AS 'payed',
(SELECT GROUP_CONCAT(B.datum SEPARATOR ',') FROM payement AS B WHERE B.invoiceId = V.id) AS 'date payement'
FROM invoice AS V
JOIN person AS P ON (V.clientId = P.id)
WHERE YEAR(V.datum) = '2015'
ORDER BY V.datum;
This give what I want (p.e. a transaction of 1000 on 4 sept and one of 2400 on 10 sept), but works very slow when I have a lot of invoices.
+------+-----------+--------+--------+-------+---------------------+
| id | datum | amount | client | payed | date payement |
+------+-----------+--------+--------+-------+---------------------+
| 75 |2015-09-10 | 3400 |Sommers | 3400 |2015-09-04,2015-09-10|
+------+-----------+--------+--------+-------+---------------------+
So I tried another statement.
SELECT V.id, V.datum, V.amount, P.name AS 'client', B.amount AS 'payed', B.datum 'date payement'
FROM invoice AS V
JOIN person AS P ON (V.clientId = P.id)
LEFT JOIN payement AS B ON B.invoiceId = V.id
WHERE YEAR(V.datum) = '2015'
ORDER BY V.datum;
But this give me 2 rows for 1 invoice, when it is payed with 2 transactions.
Can I solve it with SQL, or is it better to solve it in my application (in Java)?

When an invoice has been paid with 2 payments, which details do you wish to use? the first payment or the 2nd?
Assuming that you want the total payment amount and the latest payment date:-
SELECT V.id,
V.datum,
V.amount,
P.name AS 'client',
SUM(B.amount) AS 'payed',
MAX(B.datum) AS 'date payement'
FROM invoice AS V
JOIN person AS P ON (V.clientId = P.id)
LEFT OUTER JOIN payement AS B ON B.invoiceId = V.id
WHERE YEAR(V.datum) = '2015'
GROUP BY V.id,
V.datum,
V.amount,
P.name
ORDER BY V.datum
I don't use phpmyadmin.
mysql> EXPLAIN SELECT V.factuurnr,
-> V.datum,
-> V.somexcl,
-> P.naam AS 'client',
-> SUM(B.bedrag) AS 'payed',
-> GROUP_CONCAT(DATE_FORMAT(B.datum,'%d/%m/%y') SEPARATOR ',') AS 'date payement'
-> FROM verkoop AS V
-> JOIN persoon AS P ON (V.klantId = P.id)
-> LEFT JOIN betaling AS B ON B.docId = V.id
-> WHERE YEAR(V.datum) = '2015' and month(V.datum)=9
-> GROUP BY V.factuurnr,
-> V.datum,
-> V.somexcl,
-> P.naam
-> ORDER BY factuurnr;
+----+-------------+-------+--------+---------------+---------+---------+----------------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+----------------+------+----------------------------------------------+
| 1 | SIMPLE | V | ALL | NULL | NULL | NULL | NULL | 1576 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | P | eq_ref | PRIMARY | PRIMARY | 4 | meta.V.klantId | 1 | Using where |
| 1 | SIMPLE | B | ALL | NULL | NULL | NULL | NULL | 3291 | |
+----+-------------+-------+--------+---------------+---------+---------+----------------+------+----------------------------------------------+
3 rows in set (0.00 sec)

Related

Select latest value of another column based on a comparison between unique keys

What I'm trying to achieve is to fetch the latest date of another column based on the same msisdn (if there exists more than one msisdn that is linked to other imsis). (You can assume imsi is more of a unique column)
(Tables are simplified for demonstration purposes)
I've two tables like the following:
operator table
+--------+--------+---------------------+
| imsi | msisdn | last_accessed |
+--------+--------+---------------------+
| 74583 | 004442 | 2018-04-05 16:20:32 |
+--------+--------+---------------------+
| 94210 | 023945 | 2017-02-13 11:27:14 |
+--------+--------+---------------------+
| 59123 | 004442 | 2018-07-15 05:24:55 |
+--------+--------+---------------------+
| 61234 | 089923 | 2018-07-21 16:13:29 |
+--------+--------+---------------------+
customer table
+--------+--------------+---------------------+
| imsi | company_id | business_plan |
+--------+--------------+---------------------+
| 74583 | FEX | yearly |
+--------+--------------+---------------------+
| 94210 | AOH | trial |
+--------+--------------+---------------------+
| 59123 | BIOI | monthly |
+--------+--------------+---------------------+
| 61234 | OOX | simple |
+--------+--------------+---------------------+
The following result is what I aim for. If I search for 74583 it should return 2018-07-15 05:24:55.
+--------+--------------+---------------------+----------------------+
| imsi | company_id | business_plan | last_accessed_date |
+--------+--------------+---------------------+----------------------+
| 74583 | FEX | yearly | 2018-07-15 05:24:55 |
+--------+--------------+---------------------+----------------------+
The following query returns almost what I try to achieve but does not return the latest date according to the table above.
SELECT
cust.imsi,
cust.company_id,
cust.business_plan,
CASE
WHEN
(
SELECT MAX(subop.last_accessed)
FROM operator subop
WHERE subop.msisdn = op.msisdn
GROUP BY subop.msisdn
HAVING COUNT(*) > 1
)
THEN
op.last_accessed
ELSE
'Never'
END
AS last_accessed_date
FROM customer cust
INNER JOIN operator op
ON cust.imsi = op.imsi
WHERE cust.imsi = '74583';
We can try doing this using a correlated subquery in the select clause:
SELECT
c.imsi,
c.company_id,
c.business_plan,
(SELECT MAX(t.last_accessed) FROM operator t
WHERE t.msisdn = o.msisdn) last_accessed_date
FROM customer c
INNER JOIN operator o
ON c.imsi = o.imsi
WHERE c.imsi = '74583';
Follow the link below for a SQLFiddle demo.
Demo
This query will return the last_accessed_date for every imsi:
select
o1.imsi,
o1.msisdn,
max(o2.last_accessed) as last_accessed_date
from
operator o1 inner join operator o2
on o1.msisdn = o2.msisdn
group by
o1.imsi,
o1.msisdn
(I am joining the operators table with itself to get the last accessed date based on the msisdn column). Then you can join this query with the customer table:
select
c.imsi,
c.company_id,
c.business_plan,
coalesce(l.last_accessed_date, 'Never') as last_accessed_date
from
customer c left join (
select
o1.imsi,
o1.msisdn,
max(o2.last_accessed) as last_accessed_date
from
operator o1 inner join operator o2
on o1.msisdn = o2.msisdn
group by
o1.imsi,
o1.msisdn
) l on c.imsi = l.imsi
it can then be written in some different ways, but I think this is the easier to understand.
Please see a fiddle here http://sqlfiddle.com/#!9/0f080c/1
Try this
SELECT
cust.imsi,
cust.company_id,
cust.business_plan,
(
SELECT MAX(last_accessed) FROM operator AS a WHERE a.msisdn = op.msisdn
) AS last_accessed_date
FROM customer cust
INNER JOIN operator op
ON cust.imsi = op.imsi
WHERE cust.imsi = '74583'

Count (Select * Join) mysql query

I'm stuck on this for hours, I'm trying to COUNT how many subscribers are there in Group A, Group B, Group C for this particular query:
SELECT rh.id_subscriber, rh.bill_month, rh.bill_year,
(
SELECT tbl_gen_info.gen_data_03
FROM tbl_subscriber
LEFT JOIN tbl_gen_info ON tbl_subscriber.bill_area_code = tbl_gen_info.gen_data_01
WHERE rh.id_subscriber = tbl_subscriber.id_subscriber
) AS group_area
FROM tbl_reading_head AS rh
WHERE rh.id_soa_head IS NULL
AND rh.read_status <> 'Beginning'
AND rh.rec_status = 'active'
ORDER BY rh.id_subscriber
The sub-query gets the Group area gen_data_03 from tbl_gen_info
Tables contain this information:
tbl_gen_info
--------------------------------------------
| gen_category | gen_data_01 | gen_data_03 |
--------------------------------------------
| Area Code | Camacho St. | Group A |
--------------------------------------------
tbl_subscriber
----------------------------------
| id_subscriber | bill_area_code |
----------------------------------
| 1 | Camacho St. |
----------------------------------
tbl_reading_head
----------------------------------------------------------------------
| id_subscriber | id_soa_head | read_status | bill_month | bill_year |
----------------------------------------------------------------------
| 1 | NULL | Metered | 10 | 2017 |
----------------------------------------------------------------------
Notice that each id_subscriber has two (2) rows (one for electric, one for water). After grouping by id_subscriber:
GROUP BY rh.id_subscriber
I got this:
I tried adding COUNT before the sub-query making it:
COUNT(SELECT tbl_gen_info.gen_data_03 ...) AS group_area
but that doesn't work.
Use a subquery:
SELECT rh.group_area, COUNT(*)
FROM (SELECT rh.id_subscriber, rh.bill_month, rh.bill_year,
(SELECT tbl_gen_info.gen_data_03
FROM tbl_subscriber LEFT JOIN
tbl_gen_info
ON tbl_subscriber.bill_area_code = tbl_gen_info.gen_data_01
WHERE rh.id_subscriber = tbl_subscriber.id_subscriber
) as group_area
FROM tbl_reading_head rh
WHERE rh.id_soa_head IS NULL AND
rh.read_status <> 'Beginning' AND
rh.rec_status = 'active'
) rh
GROUP BY rh.group_area;

Can query be optimized: Get a records max date then join the max date's values

I've created a query that returns the results I want but I feel there must be a better way to do this. Any guidance would be appreciated.
I am trying to get all items for a specific meeting and join their max meeting date < X and join the max date's committee acronym. X is the current meeting date.
I've tried a few different queries but none, other than the one below, returned the expected results all the time.
You can see this query in action by going to rextester.
DROP TABLE IF EXISTS `committees`;
CREATE TABLE committees
(`id` int, `acronym` varchar(4))
;
INSERT INTO committees
(`id`, `acronym`)
VALUES
(1, 'Com1'),
(2, 'Com2'),
(3, 'Com3')
;
DROP TABLE IF EXISTS `meetings`;
CREATE TABLE meetings
(`id` int, `date` datetime, `committee_id` int)
;
INSERT INTO meetings
(`id`, `date`, `committee_id`)
VALUES
(1, '2017-01-01 00:00:00', 1),
(2, '2017-02-02 00:00:00', 2),
(3, '2017-03-03 00:00:00', 2)
;
DROP TABLE IF EXISTS `agenda_items`;
CREATE TABLE agenda_items
(`id` int, `name` varchar(6))
;
INSERT INTO agenda_items
(`id`, `name`)
VALUES
(1, 'Item 1'),
(2, 'Item 2'),
(3, 'Item 3')
;
DROP TABLE IF EXISTS `join_agenda_items_meetings`;
CREATE TABLE join_agenda_items_meetings
(`id` int, `agenda_item_id` int, `meeting_id` int)
;
INSERT INTO join_agenda_items_meetings
(`id`, `agenda_item_id`, `meeting_id`)
VALUES
(1, 1, 1),
(2, 1, 2),
(3, 2, 1),
(4, 3, 2),
(5, 2, 1),
(6, 1, 3)
;
SELECT agenda_items.id,
meetings.id,
meetings.date,
sub_one.max_date,
sub_two.acronym
FROM agenda_items
LEFT JOIN (SELECT ai.id AS ai_id,
me.id AS me_id,
Max(me.date) AS max_date
FROM agenda_items AS ai
JOIN join_agenda_items_meetings AS jaim
ON jaim.agenda_item_id = ai.id
JOIN meetings AS me
ON me.id = jaim.meeting_id
WHERE me.date < '2017-02-02'
GROUP BY ai_id) sub_one
ON sub_one.ai_id = agenda_items.id
LEFT JOIN (SELECT agenda_items.id AS age_id,
meetings.date AS meet_date,
committees.acronym AS acronym
FROM agenda_items
JOIN join_agenda_items_meetings
ON join_agenda_items_meetings.agenda_item_id = agenda_items.id
JOIN meetings
ON meetings.id = join_agenda_items_meetings.meeting_id
JOIN committees
ON committees.id = meetings.committee_id
WHERE meetings.date) sub_two
ON sub_two.age_id = agenda_items.id
AND sub_one.max_date = sub_two.meet_date
JOIN join_agenda_items_meetings
ON agenda_items.id = join_agenda_items_meetings.agenda_item_id
JOIN meetings
ON meetings.id = join_agenda_items_meetings.meeting_id
WHERE meetings.id = 2;
REVIEW / TESTING OF ANSWERS (REVISED):*
I've revised the testing based on the comments made.
Since I put a bounty on this question I felt I should show how I'm evaluating the answers and give some feedback. Overall I'm very grateful to all how have helped out, thank you.
For testing, I reviewed the queries against:
the initial rextester
a modified version of the initial rextester with all 4 queries for 2 separate datasets
a larger data set from my actual database
My Original Query with EXPLAIN
+----+-------------+---------------------------+------+----------------------------------------------+
| id | select_type | table | rows | Extra |
+----+-------------+---------------------------+------+----------------------------------------------+
| 1 | PRIMARY | meetings | 1 | |
| 1 | PRIMARY | join_agenda_item_meetings | 1976 | Using where; Using index |
| 1 | PRIMARY | agenda_items | 1 | Using index |
| 1 | PRIMARY | <derived2> | 1087 | |
| 1 | PRIMARY | <derived3> | 2202 | |
| 3 | DERIVED | join_agenda_item_meetings | 1976 | Using index |
| 3 | DERIVED | meetings | 1 | Using where |
| 3 | DERIVED | committees | 1 | |
| 3 | DERIVED | agenda_items | 1 | Using index |
| 2 | DERIVED | jaim | 1976 | Using index; Using temporary; Using filesort |
| 2 | DERIVED | me | 1 | Using where |
| 2 | DERIVED | ai | 1 | Using index |
+----+-------------+---------------------------+------+----------------------------------------------+
12 rows in set (0.02 sec)
Paul Spiegel's answers.
The initial answer works and seems to be the most efficient option presented, much more than mine.
Paul Spiegel's first query pulls the fewest rows, is shorter and more readable than mine. It also doesn't need to reference a date which will be nicer when writing it as well.
+----+--------------------+-------+------+--------------------------+
| id | select_type | table | rows | Extra |
+----+--------------------+-------+------+--------------------------+
| 1 | PRIMARY | m1 | 1 | |
| 1 | PRIMARY | am1 | 1976 | Using where; Using index |
| 1 | PRIMARY | am2 | 1 | Using index |
| 1 | PRIMARY | m2 | 1 | |
| 2 | DEPENDENT SUBQUERY | am3 | 1 | Using index |
| 2 | DEPENDENT SUBQUERY | m3 | 1 | Using where |
| 2 | DEPENDENT SUBQUERY | c3 | 1 | Using where |
+----+--------------------+-------+------+--------------------------+
7 rows in set (0.00 sec)
This query also returns the correct results when adding DISTINCT to the select statement. This query does not perform as well as the first though (but it is close).
+----+-------------+------------++------+-------------------------+
| id | select_type | table | rows | Extra |
+----+-------------+------------++------+-------------------------+
| 1 | PRIMARY | <derived2> | 5 | Using temporary |
| 1 | PRIMARY | am | 1 | Using index |
| 1 | PRIMARY | m | 1 | |
| 1 | PRIMARY | c | 1 | Using where |
| 2 | DERIVED | m1 | 1 | |
| 2 | DERIVED | am1 | 1787 | Using where; Using index |
| 2 | DERIVED | am2 | 1 | Using index |
| 2 | DERIVED | m2 | 1 | |
+----+-------------+------------+------+--------------------------+
8 rows in set (0.00 sec)
Stefano Zanini's answer
This query does return the expected results using DISTINCT. When using EXPLAIN and the number of rows being pulled this query is more efficient when compared to my original one but Paul Spiegel's is just a bit better.
+----+-------------+------------+------+---------------------------------+
| id | select_type | table | rows | Extra |
+----+-------------+------------+------+---------------------------------+
| 1 | PRIMARY | me | 1 | Using temporary; Using filesort |
| 1 | PRIMARY | rel | 1787 | Using where; Using index |
| 1 | PRIMARY | <derived2> | 1087 | |
| 1 | PRIMARY | rel2 | 1 | Using index |
| 1 | PRIMARY | me2 | 1 | Using where |
| 1 | PRIMARY | co | 1 | |
| 2 | DERIVED | t1 | 1787 | Using index |
| 2 | DERIVED | t2 | 1 | Using where |
+----+-------------+------------+------+---------------------------------+
8 rows in set (0.00 sec)
EoinS' answer
As noted in the comments, this answer works if meetings are sequential, but they may not be unfortunately.
This one is a bit crazy.. Let's do it step by step:
The first step is a basic join
set #meeting_id = 2;
select am1.meeting_id,
am1.agenda_item_id,
m1.date as meeting_date
from meetings m1
join join_agenda_items_meetings am1 on am1.meeting_id = m1.id
where m1.id = #meeting_id;
We select the meeting (id = 2) and the corresponding agenda_item_ids. This will already return the rows we need with the first three columns.
Next step is to get the last meeting date for every agenda item. We need to join the first query with the join table and corresponding meetings (except of the one with id = 2 - am2.meeting_id <> am1.meeting_id). We only want meetings with a date before the actual meeting (m2.date < m1.date). From all those meetings we only want the latest date each agenda item. So we group by the agenda item and select max(m2.date):
select am1.meeting_id,
am1.agenda_item_id,
m1.date as meeting_date,
max(m2.date) as max_date
from meetings m1
join join_agenda_items_meetings am1 on am1.meeting_id = m1.id
left join join_agenda_items_meetings am2
on am2.agenda_item_id = am1.agenda_item_id
and am2.meeting_id <> am1.meeting_id
left join meetings m2
on m2.id = am2.meeting_id
and m2.date < m1.date
where m1.id = #meeting_id
group by m1.id, am1.agenda_item_id;
This way we get the fourth column (max_date).
Last step is to select the acronym of the meeting with the last date (max_date). And this is the crazy part - We can use a correlated subquery in the SELECT clause. And we can use max(m2.date) for the correlation:
select c3.acronym
from meetings m3
join join_agenda_items_meetings am3 on am3.meeting_id = m3.id
join committees c3 on c3.id = m3.committee_id
where am3.agenda_item_id = am2.agenda_item_id
and m3.date = max(m2.date)
The final query would be:
select am1.meeting_id,
am1.agenda_item_id,
m1.date as meeting_date,
max(m2.date) as max_date,
( select c3.acronym
from meetings m3
join join_agenda_items_meetings am3 on am3.meeting_id = m3.id
join committees c3 on c3.id = m3.committee_id
where am3.agenda_item_id = am2.agenda_item_id
and m3.date = max(m2.date)
) as acronym
from meetings m1
join join_agenda_items_meetings am1 on am1.meeting_id = m1.id
left join join_agenda_items_meetings am2
on am2.agenda_item_id = am1.agenda_item_id
and am2.meeting_id <> am1.meeting_id
left join meetings m2
on m2.id = am2.meeting_id
and m2.date < m1.date
where m1.id = #meeting_id
group by m1.id, am1.agenda_item_id;
http://rextester.com/JKK60222
To be true, i was surprised that you can use max(m2.date) in the subquery.
Another solution - Use the second query in a subquery (derived table). Join committees over meetings and the join table using max_date. Only keep rows with an acronym and rows without a max_date.
select t.*, c.acronym
from (
select am1.meeting_id,
am1.agenda_item_id,
m1.date as meeting_date,
max(m2.date) as max_date
from meetings m1
join join_agenda_items_meetings am1 on am1.meeting_id = m1.id
left join join_agenda_items_meetings am2
on am2.agenda_item_id = am1.agenda_item_id
and am2.meeting_id <> am1.meeting_id
left join meetings m2
on m2.id = am2.meeting_id
and m2.date < m1.date
where m1.id = #meeting_id
group by m1.id, am1.agenda_item_id
) t
left join join_agenda_items_meetings am
on am.agenda_item_id = t.agenda_item_id
and t.max_date is not null
left join meetings m
on m.id = am.meeting_id
and m.date = t.max_date
left join committees c on c.id = m.committee_id
where t.max_date is null or c.acronym is not null;
http://rextester.com/BBMDFL23101
Using your schema I used the below query, assuming that all meetings entries are sequential:
set #mymeeting = 2;
select j.agenda_item_id, m.id, m.date, mp.date, c.acronym
from meetings m
left join join_agenda_items_meetings j on j.meeting_id = m.id
left join join_agenda_items_meetings jp on jp.meeting_id = m.id -1 and jp.agenda_item_id = j.agenda_item_id
left join meetings mp on mp.id = jp.meeting_id
left join committees c on mp.committee_id = c.id
where m.id = #mymeeting;
I create a variable just to make it easy to change meetings on the fly.
Here is a functional example in Rextester
Thanks for making your schema so easy to reproduce!
I found this problem quite challenging, and the results I achieved are not jaw-dropping, but I managed to get rid of one of the sub-queries and maybe of a few joins, and this is result:
select distinct me.ID, me.DATE, rel.AGENDA_ITEM_ID, sub.MAX_DATE, co.ACRONYM
from MEETINGS me
join JOIN_AGENDA_ITEMS_MEETINGS rel /* Note 1*/
on me.ID = rel.MEETING_ID
left join (
select t1.AGENDA_ITEM_ID, max(t2.DATE) MAX_DATE
from JOIN_AGENDA_ITEMS_MEETINGS t1
join MEETINGS t2
on t2.ID = t1.MEETING_ID
where t2.DATE < '2017-02-02'
group by t1.AGENDA_ITEM_ID
) sub
on rel.AGENDA_ITEM_ID = sub.AGENDA_ITEM_ID /* Note 2 */
left join JOIN_AGENDA_ITEMS_MEETINGS rel2
on rel2.AGENDA_ITEM_ID = rel.AGENDA_ITEM_ID /* Note 3 */
left join MEETINGS me2
on rel2.MEETING_ID = me2.ID and
sub.MAX_DATE = me2.DATE /* Note 4 */
left join COMMITTEES co
on co.ID = me2.COMMITTEE_ID
where me.ID = 2 and
(sub.MAX_DATE is null or me2.DATE is not null) /* Note 5 */
order by rel.AGENDA_ITEM_ID, rel2.MEETING_ID;
Notes
you don't need the join with AGENDA_ITEMS, since the ID is already available in the relationship table
up to this point we have current meeting, its agenda items and their "calculated" max date
we get all meetings of each agenda item...
...so that we can pick the meeting whom date matches the max date we calculated previously
this condition is needed because all the joins from rel2 on have to be left (because some agenda item may have no previous meeting and hence MAX_DATE = null) but this way me2 would give some agenda items undesired meetings.

multiple tables select, Subquery returned more than 1 value. SQL

I have two tables:
Employees
id | fullName | birth | speciality
1 | A A A | 01/01/1980 | Manager
2 | B B B | 01/01/1980 | Developer
3 | C C C | 01/01/1980 | User
EmployeesStatus
ID | status | dateChange
1 | 1 | 01/01/2010
2 | 1 | 01/01/2013
3 | 1 | 01/01/2015
3 | 2 | 01/01/2016
and I want to seletect the following data
ID | Full name | Bith date | speciality | Date hired | Date fired
Result has to be:
ID | Full name | Bith date | speciality | Date hired | Date fired
1 | A A A | 01/01/1980 | Manager | 01/01/2010 | null
2 | B B B | 01/01/1980 | Developer | 01/01/2013 | null
3 | C C C | 01/01/1980 | User | 01/01/2015 | 01/01/2016
3 | C C C |01/01/1980 | User | 01/01/2017 | null
my code:
SELECT Employees.id , Employees.fullName, Employees.birth, Employees.speciality,
(SELECT dateChange FROM EmployeesStatus WHERE status=1 AND id=Employees.id) datehired,
(SELECT dateChange FROM EmployeesStatus WHERE status=2 AND id=Employees.id) datefired FROM Employees
hase as result the following message:
Msg 512, Level 16, State 1, Line 1 Subquery returned more than 1
value. This is not permitted when the subquery follows =, !=, <, <= ,
, >= or when the subquery is used as an expression.
Any thoughts?
You should use a join instead of an = based on subquery
SELECT
Employees.id
, Employees.fullName
, Employees.birth
, Employees.speciality
, e1.dateChange as datehired, e2.dateChange as datefired
FROM Employees
INNER JOIN EmployeesStatus es1 on e1.status=1 AND e1.id=Employees.id
LEFT JOIN EmployeesStatus es2 on e2.status=2 AND e2.id=Employees.id
Or you could use in clause instead of = on subquery
In addition to #scaisEdge answer:
SELECT
Employees.id,
Employees.fullName,
Employees.birth,
Employees.speciality
e1.dateChange as datehired,
MIN(e2.dateChange) as datefired
FROM Employees
INNER JOIN EmployeesStatus es1 on e1.status=1 AND e1.id=Employees.id
LEFT JOIN EmployeesStatus es2 on e2.status=2 AND e2.id=Employees.id
AND e2.dateChange > e1.dateChange
GROUP BY Employees.id,
Employees.fullName,
Employees.birth,
Employees.speciality,
e1.dateChange
Try following:
SELECT E.ID,E.FULLNAME,E.BIRTH,E.SPECIALITY,ED.DATE_HIRED,ED.DATE_FIRED
FROM EMPLOYEES E,
(SELECT ID,
MAX(CASE WHEN STATUS=1 THEN DATECHANGE ELSE NULL END)DATE_HIRED,
MAX(CASE WHEN STATUS=2 THEN DATECHANGE ELSE NULL END)DATE_FIRED
FROM EMPLOYEESSTATUS
GROUP BY ID)ED
WHERE E.ID=ED.ID
This query will be faster in term of performance and will give you same result.
try this
SELECT e.id ,
e.fullName,
e.birth,
e.speciality,
CASE WHEN t1.status = 1 then max(t1.dateChange) else null end as "Date Hired",
CASE WHEN t2.status = 2 then max(t2.dateChange) else null end as "Date Fired",
FROM Employees e
LEFT JOIN EmployeesStatus t1 on t1.id = e.id and t1.status = 1
LEFT JOIN EmployeesStatus t2 on t2.id = e.id and t1.status = 2
Hope this works..

sum two rows and order by date / total

need some help to build a query, this is my current scheme:
users:
+----+------------+
| id | username |
+----+------------+
| 1 | rob |
| 2 | john |
| 3 | jane | <--- jane never has donated
| 4 | mike |
+----+------------+
donations:
+--------------------+------------+
| uid | amount | date |
+---------+----------+------------+
| 1 | 20 | 2013-10-10 |
| 2 | 5 | 2013-10-03 |
| 2 | 50 | 2013-09-25 |
| 2 | 5 | 2013-10-01 |
| 4 | 100 | 2012-10-01 | <-- past year
+---------+----------+------------+
Result I want:
+---------+-------------+---------+-------------+---------------+----------+
| id | username | amount | monthly | totalamount | total |
+---------+-------------+---------+-------------+ --------------+----------+
| 1 | rob | 20 | 1 | 20 | 1 |
| 2 | john | 60 | 3 | 60 | 3 |
| 3 | jane | 0 | 0 | 0 | 0 |
| 4 | mike | 0 | 0 | 100 | 1 |
+---------+-------------+-----------------------+---------------+----------+
This is my query:
SELECT
u.*,
COALESCE(sum(d.amount), 0) amount,
COUNT(d.uid) monthly,
COUNT(d.amount) as Total, <-- need to get sum all time donations and number of times donated
FROM users u
LEFT JOIN donations d
ON u.id = d.uid
AND (month(d.date), year(d.date)) = (month(CURDATE()), year(CURDATE()))
GROUP BY u.id ORDER BY u.id ASC
So i need to add 2 different sums from same data.
EDIT: http://sqlfiddle.com/#!2/20a974/9 schema and data
How I can do this?
For this we need to filter the data on the select and not on the join.
Remove this condition:
AND (month(d.date), year(d.date)) = (month(CURDATE()), year(CURDATE()))
and add this to the select:
SUM (CASE WHEN (month(d.date), year(d.date)) = (month(CURDATE()), year(CURDATE())) THEN 1 ELSE 0 END) as monthly
Edit:
whole query:
SELECT users.id, users.username,
COALESCE(sum(CASE WHEN (month(donations.date), year(donations.date)) = (month(CURDATE()), year(CURDATE())) THEN donations.amount ELSE 0 END), 0) monthly_sum,
COALESCE(sum(CASE WHEN (month(donations.date), year(donations.date)) = (month(CURDATE()), year(CURDATE())) THEN 1 ELSE 0 END), 0) monthly_amount,
COALESCE(sum(donations.amount), 0) total_sum,
count(*) total_amount
from users
left join donations
on donations.uid = users.id
group by users.id, users.username
http://sqlfiddle.com/#!2/20a974/20/0
For me the easiest way to think about the separately grouped information is to put it into separate queries and then just join the results back together. This is not likely to be the most efficient, but it helps to get something working.
select auo.id, auo.username,
coalesce(monthly_count, 0), coalesce(monthly_total, 0),
coalesce(total, 0), coalesce(total_amount, 0)
from aaa_users auo
left join (
select au.id as id, count(adm.amount) as monthly_count, SUM(adm.amount) as monthly_total
from aaa_users au join aaa_donations adm on au.id = adm.uid and adm.donate_date > GETDATE()-30
group by au.id
) as monthly on monthly.id = auo.id
left join (
select au.id as id, count(ady.amount) total, SUM(ady.amount) as total_amount
from aaa_users au join aaa_donations ady on au.id = ady.uid and ady.donate_date > getDate()-450
group by au.id
) as yearly on yearly.id = auo.id
As #CompuChip said, it's cleaner to just join to the donations table twice, but I have something wrong in my join logic as the values for john are getting duplicated. I think there would need to be a donations.id column to prevent the monthly and total donations from being combined. Anyway, here's an example even though it isn't working correctly
select au.id, au.username,
count(adm.amount), SUM(adm.amount) as monthly_total,
count(ady.amount), SUM(ady.amount) as total_amount
from aaa_users au
left outer join aaa_donations adm on au.id = adm.uid and adm.donate_date > GETDATE()-60
left outer join aaa_donations ady on au.id = ady.uid and ady.donate_date > getDate()-450
group by au.id, au.username
order by au.id, au.username
You can do another join to donations, giving it a different alias: LEFT JOIN donations d2 on d2.uid = u.id. Then sum over d2.amount for the last two fields, e.g.
SELECT u.*,
COALESCE(sum(d.amount), 0) amount,
COUNT(d.uid) monthly,
COUNT(d.amount) as Total,
COALESCE(sum(d2.amount), 0) amountAll,
COUNT(d2.uid) monthlyAll,
COUNT(d2.amount) as TotalAll
FROM users u
LEFT JOIN donations d ON u.id = d.uid AND (month(d.date), year(d.date)) = (month(CURDATE()), year(CURDATE()))
LEFT JOIN donations d2 ON u.id = d2.uid
GROUP BY u.id ORDER BY u.id ASC