Got three results extracted from three different tables.
Each table is a product: loans, credits and discounts.
loans and credits got the following data: clientid, type, productid, date & expiration (days to go).
discounts got: clientid, date and expiration.
The results are the number of times (count) for every client which product expires in 10 days (or less) and is registered among two dates.
Example (just for loans):
SELECT clientid, COUNT(*)
FROM loans
WHERE ((type LIKE 'TITULAR') AND(date BETWEEN 'ccyy-mm-dd' AND 'ccyy-mm-dd') AND (expires <= 10))
GROUP BY clientid
ORDER BY clientid;
Obviously, not all the clients got loans, credits or discounts at the same time, but I need to get a result that sums the number of times any client has any of the products expiring in 10 days or less among the limit dates.
So, in example, if client #200 got 3 loans, 2 credits and just one discount; all of them between date1 and date2, with expiration equal or less 10; the result should be 6.
So far I've tried:
SELECT loansr.clienteid, (loansr.count + creditsr.count + discountsr.count)
FROM
(SELECT clienteid, COUNT(*) AS "count"
FROM loans
WHERE (type LIKE 'TITULAR')
AND (date BETWEEN '2009-08-01' AND '2009-10-30')
AND (expires <= 10)
GROUP BY clienteid) loansr,
(SELECT clienteid, COUNT(*) AS "count"
FROM credits
WHERE (type LIKE 'TITULAR')
AND (date BETWEEN '2009-08-01' AND '2009-10-30')
AND (expires <= 10)
GROUP BY clienteid) creditsr,
(SELECT clienteid, COUNT(*) AS "count"
FROM discounts
WHERE (date BETWEEN '2009-08-01' AND '2009-10-30')
AND (expires <= 10)
GROUP BY clienteid) discountsr
WHERE
(loansr.clienteid = creditsr.clienteid = discountsr.clienteid)
ORDER BY loansr.clienteid;
Edit 18:25
I've think that if I use UNION ALL to mix the three results and then group by clienteid I will get what I'm looking for, won't I?
SELECT clienteid AS "CLIENTE", SUM(COUNT) AS "NUM_VECES_INCI_10_ACT_U3M" FROM
((SELECT clienteid, COUNT(*) AS "COUNT"
FROM loans
WHERE (titularidad_tipo LIKE 'TITULAR')
AND (date BETWEEN '2009-08-01' AND '2009-10-30')
AND (expires >= 11)
GROUP BY clienteid)
UNION ALL
(SELECT clienteid, COUNT(*) AS "COUNT"
FROM credits
WHERE (titularidad_tipo LIKE 'TITULAR')
AND (date BETWEEN '2009-08-01' AND '2009-10-30')
AND (expires >= 11)
GROUP BY clienteid)
UNION ALL
(SELECT clienteid, COUNT(*) AS "COUNT"
FROM discounts
WHERE (date BETWEEN '2009-08-01' AND '2009-10-30')
AND (expires >= 11)
GROUP BY clienteid)) orig
GROUP BY clienteid
ORDER BY clienteid;
I'd post it in the comment if I could :)
If you use UNION ALL, you should get the desired results. Although make sure to have proper indexes (I suggest titularidad_tipo, date, expires) for tables loansr, credits, and (date, expires) for discounts table. If you have proper indexation, your results will come quickly.
Related
I currently am trying to track the number of messages sent by month as well as the volume's percent change in comparison to one year prior.
Here is my current query:
Select
a.mo,
a.ye,
a.Messages,
((a.Messages - b.Messages) / b.Messages) as "% Change"
from(
select
MONTH(post_date) as mo,
count(*) as "Messages",
YEAR(post_date) as ye
from
pm_messages
WHERE
post_date > "2018-01-01 00:00:00"
group by
year(post_date),
month(post_date)
) a
left join (
select
MONTH(post_date) as mo,
YEAR(post_date) as ye,
count(*) as "Messages"
from
pm_messages
group by
year(post_date),
month(post_date)
) b on a.mo = b.mo
and a.ye -1 = b.ye
This works great, however, it places month and year in separate columns, which has been messing up the graphs I am working with. However, when I try to pull month and year into one columns as I've done in other queries from the same table, i.e. using:
SELECT DATE_FORMAT(`post_date`,'%M %Y')
My query does not work.
Does anyone know how I can combine my current query to still calculate the return from a year prior but have month and date come up as one column, as opposed to (Month | Year | Messages | % Change)
Thanks!!
you can use extract instead of separate year() and month() functions :
EXTRACT(YEAR_MONTH from post_date)
of course you have to group by this instead of year, month . for example :
select
EXTRACT(YEAR_MONTH from post_date) yearmonth,
count(*) as "Messages"
from
pm_messages
group by
EXTRACT(YEAR_MONTH from post_date)
If you have data for every month, you can use lag():
select year(post_date) as ye, month(post_date) as mo,
count(*) as Messages,
lag(count(*)) over (partition by month(post_date) order by year(post_date)) as prev_year
from pm_messages
where post_date >= '2018-01-01'
group by year(post_date), month(post_date)
I expect this query to give me the avg value from daily active users up to date and grouped by month (from Oct to December). But the result is 164K aprox when it should be 128K. Why avg is not working? Avg should be SUM of values / number of current month days up to today.
SELECT sq.month_year AS 'month_year', AVG(number)
FROM
(
SELECT CONCAT(MONTHNAME(date), "-", YEAR(DATE)) AS 'month_year', count(distinct id_user) AS number
FROM table1
WHERE date between '2020-10-01' and '2020-12-31 23:59:59'
GROUP BY EXTRACT(year_month FROM date)
) sq
GROUP BY 1
Ok guys thanks for your help. The problem was that on the subquery I was pulling the info by month and not by day. So I should pull the info by day there and group by month in the outer query. This finally worked:
SELECT sq.day_month, AVG(number)
FROM (SELECT date(date) AS day_month,
count(distinct id_user) AS number
FROM table_1
WHERE date >= '2020-10-01' AND
date < '2021-01-01'
GROUP BY 1
) sq
GROUP BY EXTRACT(year_month FROM day_month)
Do not use single quotes for column aliases!
SELECT sq.month_year, AVG(number)
FROM (SELECT CONCAT(MONTHNAME(date), '-', YEAR(DATE)) AS month_year,
count(distinct id_user) AS number
FROM table1
WHERE date >= '2020-10-01' AND
date < '2021-01-01'
GROUP BY month_year
) sq
GROUP BY 1;
Note the fixes to the query:
The GROUP BY uses the same columns as the SELECT. Your query should return an error (although it works in older versions of MySQL).
The date comparisons have been simplified.
No single quotes on column aliases.
Note that the outer query is not needed. I assume it is there just to illustrate the issue you are having.
I have a Ledger table with this schema:
LedgerId (int,not null)
Timestamp (datetime, not null)
CostCenter (int, not null)
Payee (varchar(50), not null)
Type (varchar(3),not null)
Category (varchar(24), not null)
Amount (decimal(8,2) not null)
Tag (varchar(30),null)
Memo (varchar(150), null)
where I record expense transactions for a small business.
At year's end I have to issue a form 1099 to the IRS for any contractor who received more than $600. I run the following query (thanks to StackExchange!) to get this:
SELECT Payee as Name, SUM(Amount)as Total FROM Ledger
where (convert(date,timestamp) < convert(date,'2019-01-01'))
and (convert(date,timestamp) > convert(date,'2017-12-31'))
and category like '%Contract%'
group by Payee having SUM(amount) > 600
order by Payee
This is great, and gives me a list of each contractor and the total amount for 2018.
What I would like now is a query that will give me the total amount I have spent for these contractors in 2018 (also for IRS, form 1096).
If I use this query as a subquery to obtain this total amount I get errors. How do I go about totaling all this contractor expense?
Are you saying this doesn't work?
select sum(total)
from (select Payee as Name, SUM(Amount) as Total
from Ledger
where timestamp < '2019-01-01' and
timestamp >= '2018-01-01' and
category like '%Contract%'
group by Payee
having sum(amount) > 600
) l;
You shouldn't need date conversions for the logic you want to implement.
I have a table of population that I want to compare population in two year.
My table structure:
id (auto increment), type (man,woman,child), population (1 to 10000), date
I want run two under query and show into one table result:
query1:
SELECT type,count(population) as count_of_year1
FROM population
where date between '2013-01-01' and '2013-01-24'
GROUP BY type
query2:
SELECT type, count(population) as count_of_year2
FROM population
where date between '2014-01-01' and '2014-01-24'
GROUP BY type
I need this result :
| Type | population in year2013| population in year 2014
How to do this?
Use case expressions to do conditional counting:
SELECT type,
count(case when date between '2013-01-01' and '2013-01-24' then population end) as count_of_year1,
count(case when date between '2014-01-01' and '2014-01-24' then population end) as count_of_year2
FROM population
GROUP BY type
Add this where clause to speed things up if needed:
where date between '2013-01-01' and '2013-01-24'
or date between '2014-01-01' and '2014-01-24'
As population can have a value from 1 to 10000, I assume you want SUM() here not COUNT().
I'd have a separate table for types:
population_type - id, title
population - id, type_id (references type.id), population, date
Then I prefer using JOINs here:
SELECT pt.title type,
COALESCE(y1.total_population,0) population_2013,
COALESCE(y2.total_population,0) population_2014
FROM population_type pt
LEFT JOIN (
SELECT type_id,
SUM(population) total_population,
FROM population
WHERE date >= '2013-01-01'
AND date < '2013-01-24' + INTERVAL 1 DAY
GROUP BY type
) y1
ON y1.type_id = pt.id
LEFT JOIN (
SELECT type_id,
SUM(population) total_population,
FROM population
WHERE date >= '2014-01-01'
AND date < '2014-01-24' + INTERVAL 1 DAY
GROUP BY type
) y2
ON y2.type_id = pt.id
This way you are only summing through what you need each time and the query is more modular.
I have 3 tables:
doctors (id, name) -> has_many:
patients (id, doctor_id, name) -> has_many:
health_conditions (id, patient_id, note, created_at)
Every day each patient gets added a health condition with a note from 1 to 10 where 10 is a good health (full recovery if you may).
What I want to extract is the following 3 statistics for the last 30 days (month):
- how many patients got better
- how many patients got worst
- how many patients remained the same
These statistics are global so I don't care right now of statistics per doctor which I could extract given the right query.
The trick is that the query needs to extract the current health_condition note and compare with the average of past days (this month without today) so one needs to extract today's note and an average of the other days excluding this one.
I don't think the query needs to define who went up/down/same since I can loop and decide that. Just today vs. rest of the month will be sufficient I guess.
Here's what I have so far which obv. doesn't work because it only returns one result due to the limit applied:
SELECT
p.id,
p.name,
hc.latest,
hcc.average
FROM
pacients p
INNER JOIN (
SELECT
id,
pacient_id,
note as LATEST
FROM
health_conditions
GROUP BY pacient_id, id
ORDER BY created_at DESC
LIMIT 1
) hc ON(hc.pacient_id=p.id)
INNER JOIN (
SELECT
id,
pacient_id,
avg(note) AS average
FROM
health_conditions
GROUP BY pacient_id, id
) hcc ON(hcc.pacient_id=p.id AND hcc.id!=hc.id)
WHERE
date_part('epoch',date_trunc('day', hcc.created_at))
BETWEEN
(date_part('epoch',date_trunc('day', hc.created_at)) - (30 * 86400))
AND
date_part('epoch',date_trunc('day', hc.created_at))
The query has all the logic it needs to distinguish between what is latest and average but that limit kills everything. I need that limit to extract the latest result which is used to compare with past results.
Something like this assuming created_at is of type date
select p.name,
hc.note as current_note,
av.avg_note
from patients p
join health_conditions hc on hc.patient_id = p.id
join (
select patient_id,
avg(note) as avg_note
from health_conditions hc2
where created_at between current_date - 30 and current_date - 1
group by patient_id
) avg on t.patient_id = hc.patient_id
where hc.created_at = current_date;
This is PostgreSQL syntax. I'm not sure if MySQL supports date arithmetics the same way.
Edit:
This should get you the most recent note for each patient, plus the average for the last 30 days:
select p.name,
hc.created_at as last_note_date
hc.note as current_note,
t.avg_note
from patients p
join health_conditions hc
on hc.patient_id = p.id
and hc.created_at = (select max(created_at)
from health_conditions hc2
where hc2.patient_id = hc.patient_id)
join (
select patient_id,
avg(note) as avg_note
from health_conditions hc3
where created_at between current_date - 30 and current_date - 1
group by patient_id
) t on t.patient_id = hc.patient_id
SELECT SUM(delta < 0) AS worsened,
SUM(delta = 0) AS no_change,
SUM(delta > 0) AS improved
FROM (
SELECT patient_id,
SUM(IF(DATE(created_at) = CURDATE(),note,NULL))
- AVG(IF(DATE(created_at) < CURDATE(),note,NULL)) AS delta
FROM health_conditions
WHERE DATE(created_at) BETWEEN CURDATE() - INTERVAL 1 MONTH AND CURDATE()
GROUP BY patient_id
) t