MySQL: How to use MAX function in WHERE clause? - mysql

This is what my query generated.
LOANTYPE | TOTALBALANCE | STATUS |
--------------------|--------------|--------|
Conventional Loans | 52.84 | Active |
Conforming Loans | 45.55 | Active |
Non-Conforming Loans| 43.90 | Active |
Secured Loans | 42.73 | Active |
Unsecured Loans | 34.99 | Active |
Open-ended Loans | 11.99 | Active |
Close-ended Loans | 11.69 | Active |
The TOTALBALANCE column is the summation of both Active and Inactive accounts per LOANTYPE.
Here is my query
SELECT
product.LOANTYPE,
SUM(account.PRINCIPALBALANCE + account.INTERESTBALANCE) AS TOTALBALANCE,
IF(DATE_ADD(MAX(transaction.PAYMENTDATES),
INTERVAL 6 MONTH) > CURRENT_DATE(),
'Active',
'Innactive') AS LOANSTATUS
FROM
account
INNER JOIN
client ON account.ACCOUNTKEY = client.PRIMARYKEY
INNER JOIN
product ON account.PRODUCTKEY = product.PRIMARYKEY
INNER JOIN
transaction ON transaction.ACCOUNTKEY = loanaccount.PRIMARYKEY
WHERE
transaction.TYPE = 'REPAYMENT'
GROUP BY product.LOANTYPE
I would like to get the TOTALBALANCE of Active/Innactive accounts as well as how many are Active/Inactive per LOANTYPE like so.
LOANTYPE | ACTIVEBALANCE | ACTIVE# | INACTIVEBALANCE | INNACTIVE#
--------------------|---------------|---------|-----------------|------------
Conventional Loans | 35.23 | 2 | 17.61 | 1
Conforming Loans | 18.22 | 1 | 27.33 | 1
Non-Conforming Loans| 32.486 | 2 | 11.414 | 2
Secured Loans | 17.092 | 2 | 25.638 | 1
Unsecured Loans | 40.61 | 2 | 5.6112 | 1
Open-ended Loans | 7.194 | 1 | 4.796 | 1
Close-ended Loans | 6.4395 | 2 | 5.26 | 2
I added
AND DATE_ADD(MAX(transaction.PAYMENTDATES),INTERVAL 6 MONTH) > CURRENT_DATE() in WHERE Clause
to limit the results but it still give me error.
What would/should I modify in my query to make my idea happen.
Thank you for your time.

Use conditional aggregation. The basic idea below is that we use a CASE expression to conditionally take a sum of balance, or record count, depending on whether the records are classified as active or inactive.
SELECT
product.LOANTYPE,
SUM(CASE WHEN DATE_ADD(t.PAYMENTDATES, INTERVAL 6 MONTH) > CURRENT_DATE()
THEN account.PRINCIPALBALANCE + account.INTERESTBALANCE
ELSE 0 END) AS ACTIVEBALANCE,
COUNT(CASE WHEN DATE_ADD(t.PAYMENTDATES, INTERVAL 6 MONTH) > CURRENT_DATE()
THEN 1 END) AS ACTIVE_CNT,
SUM(CASE WHEN DATE_ADD(t.PAYMENTDATES, INTERVAL 6 MONTH) <= CURRENT_DATE()
THEN account.PRINCIPALBALANCE + account.INTERESTBALANCE
ELSE 0 END) AS INACTIVEBALANCE,
COUNT(CASE WHEN DATE_ADD(t.PAYMENTDATES, INTERVAL 6 MONTH) <= CURRENT_DATE()
THEN 1 END) AS INACTIVE_CNT
FROM account a
INNER JOIN client c
ON a.ACCOUNTKEY = c.PRIMARYKEY
INNER JOIN product p
ON a.PRODUCTKEY = p.PRIMARYKEY
INNER JOIN
(
SELECT ACCOUNTKEY, MAX(PAYMENTDATES) AS PAYMENTDATES
FROM transaction
WHERE TYPE = 'REPAYMENT'
GROUP BY ACCOUNTKEY
) t
ON t.ACCOUNTKEY = a.PRIMARYKEY
GROUP BY
p.LOANTYPE;
Note that in your question you refer to a loanaccount table, but this table appears nowhere in the actual query. I have assumed that you intended this to refer to the account table.

How about this
SELECT product.loantype,
active.totalbalance AS ACTIVEBALANCE,
active.cnt AS ACTIVE,
incative.totalbalance AS INACTIVEBALANCE,
inactive.cnt AS INACTIVE
FROM product
left join (SELECT product.loantype,
SUM(account.principalbalance
+ account.interestbalance) AS TOTALBALANCE,
COUNT(1) AS cnt
FROM account
inner join client
ON account.accountkey = client.primarykey
inner join product
ON account.productkey = product.primarykey
WHERE TRANSACTION.TYPE = 'REPAYMENT'
AND Current_date() > (SELECT DATE_ADD(MAX(
TRANSACTION.paymentdates),
interval 6 month)
FROM TRANSACTION
WHERE TYPE = 'REPAYMENT'
AND TRANSACTION.accountkey
=
account.accountkey)
GROUP BY product.loantype) AS active
ON( product.loantype = active.loantype )
left join (SELECT product.loantype,
SUM(account.principalbalance
+ account.interestbalance) AS TOTALBALANCE,
COUNT(1) AS cnt
FROM account
inner join client
ON account.accountkey = client.primarykey
inner join product
ON account.productkey = product.primarykey
WHERE TRANSACTION.TYPE = 'REPAYMENT'
AND Current_date() < (SELECT DATE_ADD(MAX(
TRANSACTION.paymentdates),
interval 6 month)
FROM TRANSACTION
WHERE TYPE = 'REPAYMENT'
AND TRANSACTION.accountkey
=
account.accountkey)
GROUP BY product.loantype) AS inactive
ON( product.loantype = inactive.loantype )

Related

Conditional subquery in where clause

I'm trying to create a query with conditional logic where I only calculate revenue for the most recent records by each month using a datetime column (start_date), but only if there are multiple records in that month from the same account_id.
Here's a basic example of the schema after I join two tables (full schema in sqlfiddle link).
| account_id | plan_id | start_date | plan_interval | price |
|------------|---------|----------------------|---------------|-------|
| 1 | 1 | 2018-01-03T14:52:13Z | month | 39 |
| 1 | 3 | 2018-02-07T11:10:17Z | year | 999 |
| 1 | 2 | 2018-02-07T11:11:17Z | month | 99 |
In the above example, I would only like to include rows 1 and 3 in my output, as it's the one record from account_id 1 in January and the most recent of two records for account_id 1 in February.
SELECT
MONTH(start_date) AS month,
SUM(CASE WHEN plan_interval = 'month'
THEN price * .01
ELSE (price * .01)/12 END) AS mrr
FROM subscriptions
JOIN plans
ON plans.id = subscriptions.plan_id
WHERE Year(start_date) = 2018 AND
CASE WHEN (account_id = account_id
AND MONTH(start_date) = MONTH(start_date))
THEN (SELECT MAX(start_date) FROM subscriptions)
ELSE (SELECT start_date FROM subscriptions)
END
GROUP BY month
ORDER BY month ASC;
The case statement in the subquery above does not seem to work in doing this. It returns the data without filtering out records when the first condition is met.
Here is an example: sqlfiddle
This query returns the rows that you are asking for in the question:
SELECT s.*, p.plan_interval, p.price,
(CASE WHEN p.plan_interval = 'month'
THEN p.price * 0.01
ELSE (p.price * 0.01)/12
END) AS mrr
FROM subscriptions s JOIN
plans p
ON p.id = s.plan_id
WHERE YEAR(s.start_date) = 2018 AND
s.start_date = (SELECT MAX(s2.start_date)
FROM subscriptions s2
WHERE s2.account_id = s.account_id AND
EXTRACT(YEAR_MONTH FROM s2.start_date) = EXTRACT(YEAR_MONTH FROM s.start_date)
)
ORDER BY s.start_date ASC;
This uses a subquery to get the most recent record for a subscription for each month.
You can then aggregate this however you wish.
Notes about the query:
Table aliases make the query easier to write and to read.
The subquery uses the handy YEAR_MONTH option of EXTRACT(), so it handles both years and months.
For numeric constants between -1 and 1, I always prepend with a 0, so 0.12 rather than .12. If find that this makes the decimal point more obvious.
First work out the last entry by account and month (sub query a) join to subscriptions to get the plan_id and then get the plan
SELECT S.ACCOUNT_id,s.plan_id,s.start_date,p.Price,p.plan_interval,
case when p.plan_interval = 'month' then p.price * .01 /12 else p.price * .01 end as rev
from subscriptions s
join (select s.account_id,month(s.start_date), max(s.start_date) start_date
from subscriptions s
group by account_id,month(start_date)) a on a.account_id = s.account_id and a.start_date = s.start_date
join plans p on p.id = s.plan_id;
+------------+---------+---------------------+----------+---------------+--------------+
| ACCOUNT_id | plan_id | start_date | Price | plan_interval | rev |
+------------+---------+---------------------+----------+---------------+--------------+
| 1 | 1 | 2018-01-03 14:52:13 | 3900.00 | month | 3.25000000 |
| 1 | 2 | 2018-02-07 11:11:17 | 9900.00 | month | 8.25000000 |
| 2 | 3 | 2018-01-03 17:40:05 | 99900.00 | year | 999.00000000 |
+------------+---------+---------------------+----------+---------------+--------------+
In your case, the WHERE statement does not work because the CASE statement will always return a boolean.
CASE WHEN (account_id = account_id
AND MONTH(start_date) = MONTH(start_date))
THEN (SELECT MAX(start_date) FROM subscriptions)
ELSE (SELECT start_date FROM subscriptions)
END
Another approach to what you are building would involve using a subquery to order the columns the way you want within the groups.
SELECT
account_id,
month,
CASE WHEN plan_interval = 'month'
THEN price * .01
ELSE (price * .01)/12
END AS mrr
FROM (
SELECT *, MONTH(start_date) AS month
FROM subscriptions
INNER JOIN plans ON plans.id = subscriptions.plan_id
ORDER BY account_id, start_date DESC
) sq
GROUP BY account_id, month
This works because selecting columns in a GROUP BY will automatically take the first row that is returned by the subquery for a given group of columns.

MYSQL: How to get the current entry of multipe tables

I got three tables with data for products:
One where the products are defined.
A second where is defined which store has what products with what priceing with a timestamp.
And a third with the stock in the store of the products that are assiged.
Table 1 - products:
prdoductID|Name
----------|-------
1 |banana
2 |apple
Table 2 - prices:
storeID | productID | price | timestamp
--------|-----------|-------|------------
1 | 1 | 5,90 | 2016-03-27 16:00:00
1 | 1 | 5,90 | 2016-03-27 17:00:00
2 | 1 | 5,00 | 2016-03-27 16:00:00
2 | 2 | 5,00 | 2016-03-27 16:00:00
2 | 2 | 5,90 | 2016-03-28 19:00:00
Table 3 - stocks:
storeID | productID | stock | timestamp
--------|-----------|-------|------------
1 | 1 | 50 | 2016-03-27 08:00:00
1 | 1 | 5 | 2016-03-27 17:00:00
2 | 1 | 60 | 2016-03-27 09:00:00
2 | 2 | 0 | 2016-03-27 16:00:00
2 | 2 | 55 | 2016-03-28 19:00:00
`
Now I want to get the current state of one store for example at time 2016-03-27 14:00:00.
My current query get's for me the data but for all timestamps. I just want to get the corresponding entrys with the recent timesamps for each product of this store with the storeID = x;
Note: The user is able to plan. So there could be entries with futre timestamps so just a MAX would not work.
Query:
SELECT price.*, prod.name, stock.timestamp as 'stockTimesamp',
stock.stock FROM prices price
LEFT OUTER JOIN products prod on price.productID = prod.productID and
price.timestamp=(SELECT MAX(price.timestamp) WHERE price.timestamp <= '2016-03-27 14:00:00' and price.productID = prod.productID)
LEFT OUTER JOIN stocks stock on price.storeID = best.storeID and price.productID = stock.productID and stock.timestamp=(SELECT MAX(best.timestamp) WHERE stock.timestamp <= '2016-03-27 14:00:00' and stock.productID = prod.productID and stock.productID = price.productID)
where price.storeID=21
ORDER BY stock.timestamp DESC
Hope you can help me out...
This is a troublesome data structure for this type of query. You can get the most recent timestamp per product for each table and then join back to get additional information:
select p.*,
(select max(timestamp)
from stocks s
where s.storeid = $storeid and s.productid = p.productid and
s.timestamp <= '2016-03-27 14:00:00'
) as stock_timestamp,
(select max(timestamp)
from prices pr
where pr.storeid = $storeid and pr.productid = p.productid and
pr.timestamp <= '2016-03-27 14:00:00'
) as stock_timestamp
from products p;
Then, join back to the original tables to get additional information:
select p.*, s.*, pr.*
from (select p.*,
(select max(timestamp)
from stocks s
where s.storeid = $storeid and s.productid = p.productid and
s.timestamp <= '2016-03-27 14:00:00'
) as stock_timestamp,
(select max(timestamp)
from prices pr
where pr.storeid = $storeid and pr.productid = p.productid and
pr.timestamp <= '2016-03-27 14:00:00'
) as price_timestamp
from products p
) p left join
stocks s
on s.storeid = $storeid and s.productid = p.productid and
s.timestamp = p.stock_timestamp left join
prices pr
on pr.storeid = $storeid and pr.productid = p.productid and
pr.timestamp = p.price_timestamp ;
I should note that your problem would be relatively trivial if you had an effective and end date on each record. That is a better way to store a slowly changing dimension.

Return a default value if no rows are found in UNION ALL query

Scenario
I have a query developed from this question, were part of the optimisation was to create a MySql view which is used for generating statistics for users and sales, the problem is that when there is no result for one of the SELECT rows it gets omitted from the resulting table.
Question
How can a tell MySql to set a default value (e.g 0) if no rows are found for any of the SELECT?
Code
This is the code for creating the view
CREATE OR REPLACE VIEW user_events AS
SELECT 'Complete profiles' AS type, created_at
FROM users
WHERE completed_registration = 1
UNION ALL SELECT 'Incomplete profiles', created_at
FROM users
WHERE completed_registration = 0 AND verified_email = 1
UNION ALL SELECT 'Unverified profiles', created_at
FROM users
WHERE verified_email = 0
UNION ALL SELECT 'Onsite Teachers', created_at
FROM onsite_teachers
UNION ALL SELECT 'Onsite Teachers hired', created_at
FROM purchases
INNER JOIN purchased_profiles
ON purchased_profiles.purchase_id = purchases.id
AND purchased_profiles.profile_type = 'onsite_teacher'
WHERE purchases.transaction_status = 'completed'
UNION ALL SELECT 'Translators', created_at
FROM translators
UNION ALL SELECT 'Translators hired', created_at
FROM purchases
INNER JOIN purchased_profiles
ON purchased_profiles.purchase_id = purchases.id
AND purchased_profiles.profile_type = 'translator'
WHERE purchases.transaction_status = 'completed'
UNION ALL SELECT 'Interpreters', created_at
FROM interpreters
UNION ALL SELECT 'Interpreters hired', created_at
FROM purchases
INNER JOIN purchased_profiles
ON purchased_profiles.purchase_id = purchases.id
AND purchased_profiles.profile_type = 'interpreter'
WHERE purchases.transaction_status = 'completed';
And this is the code for querying the totals for the last 6 months including the current month.
SELECT
type,
COUNT(CASE
WHEN created_at >= CAST(CURDATE() - INTERVAL (DAYOFMONTH(CURDATE()) - 1) DAY AS DATETIME)
THEN 1
END) AS 0_month_ago,
COUNT(CASE
WHEN created_at BETWEEN CAST(CURDATE() - INTERVAL (DAYOFMONTH(CURDATE()) - 1) DAY - INTERVAL 1 MONTH AS DATETIME)
AND CAST(LAST_DAY(CURDATE() - INTERVAL 1 MONTH) + INTERVAL 1 DAY AS DATETIME)
THEN 1
END) AS 1_month_ago,
COUNT(CASE
WHEN created_at BETWEEN CAST(CURDATE() - INTERVAL (DAYOFMONTH(CURDATE()) - 1) DAY - INTERVAL 2 MONTH AS DATETIME)
AND CAST(LAST_DAY(CURDATE() - INTERVAL 2 MONTH) + INTERVAL 1 DAY AS DATETIME)
THEN 1
END) AS 2_months_ago,
COUNT(CASE
WHEN created_at BETWEEN CAST(CURDATE() - INTERVAL (DAYOFMONTH(CURDATE()) - 1) DAY - INTERVAL 3 MONTH AS DATETIME)
AND CAST(LAST_DAY(CURDATE() - INTERVAL 3 MONTH) + INTERVAL 1 DAY AS DATETIME)
THEN 1
END) AS 3_months_ago,
COUNT(CASE
WHEN created_at BETWEEN CAST(CURDATE() - INTERVAL (DAYOFMONTH(CURDATE()) - 1) DAY - INTERVAL 4 MONTH AS DATETIME)
AND CAST(LAST_DAY(CURDATE() - INTERVAL 4 MONTH) + INTERVAL 1 DAY AS DATETIME)
THEN 1
END) AS 4_months_ago,
COUNT(CASE
WHEN created_at BETWEEN CAST(CURDATE() - INTERVAL (DAYOFMONTH(CURDATE()) - 1) DAY - INTERVAL 5 MONTH AS DATETIME)
AND CAST(LAST_DAY(CURDATE() - INTERVAL 5 MONTH) + INTERVAL 1 DAY AS DATETIME)
THEN 1
END) AS 5_months_ago
FROM
user_events
GROUP BY
type;
Current output
If you look closely there is no Interpreters hired and Translators hired rows, I want for this rows to be set and zeroed out if they return null
+=========================+===============+===============+===============+==============+===============+===============+
| type | 0_month_ago | 1_month_ago | 2_month_ago | 3_month_ago | 4_month_ago | 5_month_ago |
+=========================+===============+===============+===============+==============+===============+===============+
| Complete profiles | 7 | 20 | 14 | 25 | 30 | 7 |
+-------------------------+---------------+---------------+---------------+--------------+---------------+---------------+
| Incomplete profiles | 12 | 27 | 56 | 45 | 48 | 23 |
+-------------------------+---------------+---------------+---------------+--------------+---------------+---------------+
| Unverified profiles | 3 | 16 | 23 | 5 | 0 | 9 |
+-------------------------+---------------+---------------+---------------+--------------+---------------+---------------+
| Onsite Teachers | 11 | 36 | 8 | 15 | 46 | 12 |
+-------------------------+---------------+---------------+---------------+--------------+---------------+---------------+
| Onsite Teachers hired | 0 | 0 | 12 | 9 | 3 | 0 |
+-------------------------+---------------+---------------+---------------+--------------+---------------+---------------+
| Interpreters | 4 | 21 | 27 | 46 | 45 | 28 |
+-------------------------+---------------+---------------+---------------+--------------+---------------+---------------+
| Translators | 7 | 20 | 19 | 27 | 19 | 42 |
+-------------------------+---------------+---------------+---------------+--------------+---------------+---------------+
The user_events view is a kind of a log, which looks like
type | created_at
======================+==============
Interpreters hired | 2014-12-12
Interpreters hired | 2014-12-14
Interpreters hired | 2014-12-16
Interpreters hired | 2015-01-02
However, if no interpreter has ever been hired, then there will be no rows with type = 'Interpreters hired'. In that case, the counting query cannot possibly fabricate an Interpreters hired row out of thin air.
A solution is to ensure that an Interpreters hired row appears in the user_events view, no matter what. You could create such fictitious rows with no created_at date. That way, there will always be something to GROUP BY, but not necessarily anything to COUNT().
CREATE OR REPLACE VIEW user_events AS
SELECT 'Complete profiles' AS type, created_at
FROM users
WHERE completed_registration = 1
UNION ALL SELECT 'Incomplete profiles', created_at
FROM users
WHERE completed_registration = 0 AND verified_email = 1
UNION ALL SELECT 'Unverified profiles', created_at
FROM users
WHERE verified_email = 0
UNION ALL SELECT 'Onsite Teachers', created_at
FROM onsite_teachers
UNION ALL SELECT 'Onsite Teachers hired', created_at
FROM purchases
INNER JOIN purchased_profiles
ON purchased_profiles.purchase_id = purchases.id
AND purchased_profiles.profile_type = 'onsite_teacher'
WHERE purchases.transaction_status = 'completed'
UNION ALL SELECT 'Onsite Teachers hired', NULL
UNION ALL SELECT 'Translators', created_at
FROM translators
UNION ALL SELECT 'Translators hired', created_at
FROM purchases
INNER JOIN purchased_profiles
ON purchased_profiles.purchase_id = purchases.id
AND purchased_profiles.profile_type = 'translator'
WHERE purchases.transaction_status = 'completed'
UNION ALL SELECT 'Translators hired', NULL
UNION ALL SELECT 'Interpreters', created_at
FROM interpreters
UNION ALL SELECT 'Interpreters hired', created_at
FROM purchases
INNER JOIN purchased_profiles
ON purchased_profiles.purchase_id = purchases.id
AND purchased_profiles.profile_type = 'interpreter'
WHERE purchases.transaction_status = 'completed'
UNION ALL SELECT 'Interpreters hired', NULL;

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

MySQL Join and Subqueries

I currently have the following tables:
Case_Workflows
case_id (PK) | work_id (PK) | date
1 | 1 | 2011-12-12
1 | 4 | 2011-12-13
2 | 6 | 2011-12-18
Workflows
work_id (PK) | status_id
1 | 1
2 | 1
3 | 1
4 | 2
5 | 2
6 | 3
Statuses
status_id (PK) | title
1 | abc
2 | def
3 | ghi
What I am attempting to do is pull a count of the total number of cases with a specific status such as 'abc'. The snag is that each case can have multiple workflows and I only want the single most recent one for each case.
The end result should be:
Status: abc - Count: 2
This is what I have so far:
SELECT COUNT(cases.case_id) as countNum
FROM $this->_caseTable
JOIN case_workflows
ON cases.case_id = cases_workflows.case_id
JOIN workflows
ON cases_workflows.workflow_id = workflows.workflow_id
JOIN statuses
ON workflow.status_id = statuses.status_id
WHERE cases.date > '2011-12-12'
AND cases.date <= '2011-12-18'
What I am unsure on is how to first select the latest work_id for each case, and then grabbing its status_id to match it to a WHERE clause such as WHERE statuses.title = 'abc'
SELECT COUNT(*) as countNum
FROM $this->_caseTable
JOIN workflows
ON workflows.workflow_id =
( SELECT workflow_id
FROM cases_workflows AS mcwf
WHERE mcwf.case_id = cases.case_id
ORDER BY date DESC
LIMIT 1
)
JOIN statuses
ON workflow.status_id = statuses.status_id
WHERE cases.date > '2011-12-12'
AND cases.date <= '2011-12-18'
AND statuses.title = 'abc'
From what I'm understanding here, you need to add statuses.title to your SELECT clause, and then add a GROUP BY clause:
SELECT statuses.title, COUNT(cases.case_id) as countNum
FROM $this->_caseTable
JOIN (SELECT case_id, work_id, max(date)
FROM case_workflows
GROUP BY work_id
WHERE case_id = cases.case_id) cw
ON cases.case_id = cw.case_id
JOIN workflows
ON cw.workflow_id = workflows.workflow_id
JOIN statuses
ON workflow.status_id = statuses.status_id
GROUP BY statuses.title
WHERE cases.date > '2011-12-12'
AND cases.date <= '2011-12-18'