SELECT records using multiple critira - mysql

I have a table named all_data with fields date, value, and other fields.
I need to select last 7, 14, and 30 days from the database. There can be several entries with the same date like
date value
10-25-16 30
10-25-16 24
10-26-16 42
Here is the SELECT Statment I have that works for less than 30 days
$sql = "SELECT type_entry, COUNT(value) as val_count, FLOOR(SUM(value)) as sum_glu FROM all_data
WHERE DATEDIFF(NOW(), date) < 30 AND type_entry = 'Glucose'
ORDER BY date";
I want the same query 2 more times but changing the DATEDIFF statement with <14 and <7 so I end up with val_count1, val_count2, val_count3, sum_glu1, sum_glu2, sumglu3 I will use those to calculate the average of each.
I do not know enough SQL to figure out how to do it.
Here is a sample of readings
10/17/2016 116
10/17/2016 277
10/17/2016 145
10/18/2016 150
10/18/2016 125
10/19/2016 200
I need the number of records from 10/19/2016 for 7 days then I can do a weekly average of the readings.

You can use conditional aggregation here:
SELECT type_entry,
SUM(CASE WHEN DATEDIFF(NOW(), date) < 30 THEN 1 ELSE 0 END) AS val_count_30,
FLOOR(SUM(CASE WHEN DATEDIFF(NOW(), date) < 30 THEN value ELSE 0 END)) AS sum_glu_30,
SUM(CASE WHEN DATEDIFF(NOW(), date) < 14 THEN 1 ELSE 0 END) AS val_count_14,
FLOOR(SUM(CASE WHEN DATEDIFF(NOW(), date) < 14 THEN value ELSE 0 END)) AS sum_glu_14,
SUM(CASE WHEN DATEDIFF(NOW(), date) < 7 THEN 1 ELSE 0 END) AS val_count_7,
FLOOR(SUM(CASE WHEN DATEDIFF(NOW(), date) < 7 THEN value ELSE 0 END)) AS sum_glu_7
FROM all_data
WHERE type_entry = 'Glucose'
ORDER BY date
If you just want the average of value for a 7 day period starting on 10/19/2016 for 7 days then you can just use a WHERE clause:
SELECT type_entry,
AVG(value) AS avgValue
FROM all_data
WHERE date BETWEEN '2016-10-19' AND DATE_ADD('2016-10-19', INTERVAL 7 DAY) AND
type_entry = 'Glucose'
GROUP BY type_entry

Basically you can retrieve all rows with criteria < 30 and all your following criterias contain within this one. You could add a CASE expression within aggregate functions to only perform aggregation within rows that match your criteria. All I did was changed the < X for every pair of columns:
SELECT
type_entry,
COUNT(CASE WHEN DATEDIFF(NOW(), date) < 30 THEN value END) as val_count1,
FLOOR(SUM(CASE WHEN DATEDIFF(NOW(), date) < 30 THEN value END)) as sum_glu1,
COUNT(CASE WHEN DATEDIFF(NOW(), date) < 14 THEN value END) as val_count2,
FLOOR(SUM(CASE WHEN DATEDIFF(NOW(), date) < 14 THEN value END)) as sum_glu2,
COUNT(CASE WHEN DATEDIFF(NOW(), date) < 7 THEN value END) as val_count3,
FLOOR(SUM(CASE WHEN DATEDIFF(NOW(), date) < 7 THEN value END)) as sum_glu3
FROM all_data
WHERE DATEDIFF(NOW(), date) < 30 AND type_entry = 'Glucose'
GROUP BY type_entry
ORDER BY date
Also, note that I added the missing GROUP BY clause. Even though MySQL allows it, many different databases would yield an error. The recommended way is not to rely on MySQL mechanism in this matter.

Related

how to get weeklytotal and yesterday record in mysql in one table

Hi Everyone i am trying to implement query to get weekly and yesterday data in same table,
dummy output i have shared below, if yesterday not exist as per employee_id it should we showing 0 also as per my table week start from monday and end at sunday.please help me out how to query this get weekly_total and yesterday record and one table.
Table name-dailydata-
Sample data
employee_id
date
total
20
2022-04-25
10
20
2022-04-26
20
20
2022-04-27
20
20
2022-04-28
10
20
2022-04-29
20
20
2022-04-30
30
20
2022-04-31
40
20
2022-05-01
50
40
2022-04-26
20
expected output
employee_id
weekly_total
yesterday_record
20
200
40
40
20
0
mysql query to get weekly data
select employee_id,sum(total) as week_total from dailydata where date between '2022-04-25' and '2022-05-01'
You can try to use the condition aggregate function to make it.
We might add non-aggregate columns in the group by when we are using aggregate functions.
select employee_id,
SUM(total) as week_total,
SUM(CASE WHEN DATEDIFF('2022-05-01',date) = 1 THEN total ELSE 0 END) yesterday_record
from dailydata t1
where date between '2022-04-25' and '2022-05-01'
GROUP BY employee_id
You can use a case in the query to get yesterdays data, as long as the where does not exclude it, which is the case in the second query.
Once you have understood the principal you can define the date range so that it is calculated dynamically when you run the script if what you want is to see yesterday's figure and the last 7 days total.
You can also get yesterday using SUBDATE(NOW(),1) which is shorter.
select
employee_id,
sum(total) as week_total ,
sum(case when date = DATE_SUB(CURDATE(), INTERVAL 1 DAY)
then total else 0 end as yesterday
from dailydata
where date between
DATE_SUB(CURDATE(), INTERVAL 1 WEEK)
and DATE_SUB(CURDATE(), INTERVAL 1 DAY) ;
select
employee_id,
sum(total) as week_total ,
sum(case when date = DATE_SUB(CURDATE(), INTERVAL 1 DAY)
then total else 0 end as yesterday
from dailydata
where date between '2022-04-25' and '2022-05-01';
Hope this may help you, You just need to use the aggregate function in the case of IFNULL.
DBFiddle URL: Click Here
For the start of the week
SELECT SUBDATE(CURDATE(), weekday(CURDATE())); --Start of week
For the end of the week
SELECT DATE(CURDATE() + INTERVAL (6 - WEEKDAY(CURDATE())) DAY); --End of week
Hereby SQL query for getting employe wise total and yesterday total. If yesterday's total doesn't exist so for that Have used IFNULL. Just used SUBDATE for getting the start and end of the week date by passing current date.
SELECT employee_id,
IFNULL(SUM(total),0) AS total,
IFNULL(SUM(CASE date WHEN subdate(CURDATE(), 1) THEN total ELSE 0 END),0) AS yesterday_total
FROM dailydata
WHERE date BETWEEN
SUBDATE(CURDATE(), weekday(CURDATE())) AND (CURDATE() + INTERVAL (6 - WEEKDAY(CURDATE())) DAY)
GROUP BY employee_id
Start of this week:
SELECT DATEADD(wk, DATEDIFF(wk, 0, GETDATE()), 0)
End of this week:
SELECT DATEADD(wk, DATEDIFF(wk, 0, GETDATE()), 6)
Yesterday:
select GETDATE() -1
Your query:
select
employee_id,
sum(total) as week_total,
(select sum(total) as week_total
from dailydata b
where [date] = convert(date, getdate() -1 )
and a.employee_id = b.employee_id) as yesterday_record
from
dailydata a
where
[date] between dateadd(wk, datediff(wk, 0, getdate()), 0) and dateadd(wk, datediff(wk, 0, getdate()), 6)
group by
employee_id

Having a strange issue using right join in query

I have this MySQL query that is supposed to give me the amount in and out of all bank accounts in the database for the last 30 days. It even fills gaps with the use of a join statement with a calendar table.
However I ran into issues limiting the dataset only to include the statements belonging to a specific account, defined by a account_id field.
Here is the original query that fetches all entries in database for a specific account:
SELECT
COUNT(transactions.account_id) AS numTransactions,
calendar.datefield AS date,
SUM(CASE WHEN transactions.amount < 0 THEN transactions.amount ELSE 0 END) as negativeAmount,
SUM(CASE WHEN transactions.amount > 0 THEN transactions.amount ELSE 0 END) as positiveAmount
FROM
transactions
RIGHT JOIN
calendar
ON
DATE(transactions.accounting_date) = calendar.datefield
WHERE
calendar.datefield BETWEEN DATE_SUB(CURDATE(), INTERVAL 30 DAY) AND NOW()
AND
transactions.account_id = 1
GROUP BY
date
ORDER BY
date DESC;
This will return only the days with values for transactions.amount ignoring the RIGHT JOIN calendar clause.
What am I missing here?
I actually solved it fairly quickly.
My WHERE clause is on the left table, and not the right (?).
This worked:
SELECT
COUNT(transactions.account_id) AS numTransactions,
calendar.datefield AS date,
SUM(CASE WHEN transactions.amount < 0 THEN transactions.amount ELSE 0 END) as negativeAmount,
SUM(CASE WHEN transactions.amount > 0 THEN transactions.amount ELSE 0 END) as positiveAmount
FROM
transactions
RIGHT JOIN
calendar
ON
DATE(transactions.accounting_date) = calendar.datefield
AND
transactions.account_id = 1
WHERE
calendar.datefield BETWEEN DATE_SUB(CURDATE(), INTERVAL 30 DAY) AND NOW()
GROUP BY
date
ORDER BY
date DESC;

Join Multiple tables, then show data from one of the tables as separate columns based on CASE criteria

I have been able to join multiple tables from different databases but I am getting stuck with how to show data from one of the tables into separate columns using case criteria.
select p.ProductID as pid,
s.ItemCode as icode,
s.ItemDescription,
s.UOM,
s.AvgCost,
b.ST as stockAvl,
CASE WHEN c.saledate BETWEEN DATE_SUB(NOW(),INTERVAL 60 DAY) AND NOW() THEN SUM(c.qty) ELSE 0 END as s60,
CASE WHEN c.saledate BETWEEN DATE_SUB(NOW(),INTERVAL 90 DAY) AND NOW() THEN SUM(c.qty) ELSE 0 END as s90,
CASE WHEN c.saledate BETWEEN DATE_SUB(NOW(),INTERVAL 180 DAY) AND NOW() THEN SUM(c.qty) ELSE 0 END as s180,
CASE WHEN c.saledate BETWEEN DATE_SUB(NOW(),INTERVAL 365 DAY) AND NOW() THEN SUM(c.qty) ELSE 0 END as s365
FROM db_inventory.StockMain_T s
left outer join db_inventory.stkbalance_T b on s.ItemCode=b.itemid
left outer join db_main.SaleItems_T c on c.ItemID=s.ItemCode
left outer join db_inventory.ProductMaster_T p on p.ProductID=s.ProdID
group by icode
order by pid
The above query works but is inconsistent with results. The columns s60,s90,s180 and s365 do not show complete data. The result shows data for only a few items and not for all items whereas there is data recorded into the SaleItems_T table for those items too but the result shows 0 for those. Can't seem to understand what is it that i'm doing wrong in the query.
Here is the result of the query. For simplification, i have removed a few columns from the original query above and kept 5 columns.
The ttlsales shows sum of all sales in the database history since beginning.
s300 and s900 are blank except one item. s9000 is also the entire history when used as
NOW()-INTERVAL 9000 DAY
so it shows the entire history as well. Issues is with columns s300 and s900 which do not show data whereas data exists in the saleitems table.
Aggregate on the returned value of each CASE statement:
SUM(CASE WHEN c.saledate BETWEEN DATE_SUB(NOW(),INTERVAL 60 DAY) AND NOW() THEN c.qty ELSE 0 END) as s60,
SUM(CASE WHEN c.saledate BETWEEN DATE_SUB(NOW(),INTERVAL 90 DAY) AND NOW() THEN c.qty ELSE 0 END) as s90,
SUM(CASE WHEN c.saledate BETWEEN DATE_SUB(NOW(),INTERVAL 180 DAY) AND NOW() THEN c.qty ELSE 0 END) as s180,
SUM(CASE WHEN c.saledate BETWEEN DATE_SUB(NOW(),INTERVAL 365 DAY) AND NOW() THEN c.qty ELSE 0 END) as s365

Get records with difference on 2 different date ranges in single query

I have sales table and have two different date ranges.
i.e, I have total sales between (2016-12-21 - 2016-12-30) is 100 and for period (2016-12-11 - 2016-12-20) is 85.
Now the result I want is
100 (sales of 2016-12-21 - 2016-12-30), 85 (sales of 2016-12-11 - 2016-12-20), 15 (difference of both periods) through single query.
What I am thinking is
select *, (a.sales - b.sales) as diff
from (select id, sum(sales) as sales from salestable where date >= '2016-12-21' and date <= '2016-12-30') a
join (select id, sum(sales) as sales from salestable where date >= '2016-12-11' and date <= '2016-12-20') b
on a.id = b.id;
Is there any other better way to do this?
You can use conditional aggregation:
select sum(case when date >= '2016-12-21' and date <= '2016-12-30' then sales else 0
end) as sales_a,
sum(case when date >= '2016-12-11' and date <= '2016-12-20' then sales else 0
end) as sales_b,
sum(case when date >= '2016-12-21' and date <= '2016-12-30'
then sales else 0
when date >= '2016-12-11' and date <= '2016-12-20'
then -sales
else 0
end) as sales_diff
from salestable;
If you want the overall sum by id (as suggested by your inclusion of id), then add id to the select and add group by id.
You can use case to do a conditional sum like this:
select id,
sum_21_to_30,
sum_11_to_20,
sum_21_to_30 - sum_11_to_20 diff
from (select id,
sum(case when date >= '2016-12-21' and date <= '2016-12-30' then sales else 0 end) sum_21_to_30,
sum(case when date >= '2016-12-11' and date <= '2016-12-20' then sales else 0 end) sum_11_to_20
from table group by id) t;

Get results from two time intervals

There is a query which brings back sales data for the last 7 days.
How to get the sales of the last 30 days as well (to see the sales for the last 7 days AND the last 30 days in the results)?
SELECT
a.row_id,
MAX(ad.new_value) - MIN(ad.new_value) AS sales7days
FROM
_audit a
LEFT JOIN _audit_data ad
ON a.audit_id = ad.audit_id
WHERE ad.col = 'sales'
AND a.triggered_datetime > NOW() - INTERVAL 7 DAY
GROUP BY a.row_id
ORDER BY sales7days DESC;
Perhaps with a CASE expression:
SELECT a.row_id
, MAX(case when a.triggered_datetime > NOW() - INTERVAL 7 DAY
then ad.new_value else NULL end)
- MIN(case when a.triggered_datetime > NOW() - INTERVAL 7 DAY
then ad.new_value else NULL end) AS sales7days
, MAX(case when a.triggered_datetime > NOW() - INTERVAL 30 DAY
then ad.new_value else NULL end)
- MIN(case when a.triggered_datetime > NOW() - INTERVAL 30 DAY
then ad.new_value else NULL end) AS sales30days
FROM _audit a, _audit_data ad
WHERE a.audit_id = ad.audit_id AND ad.col = 'sales'
GROUP BY a.row_id;
SELECT
d7.row_id,
d7.salesdays, d30.salesdays
FROM
(
Select a.row_id, MAX(ad.new_value) - MIN(ad.new_value) AS salesdays
From _audit a
LEFT JOIN _audit_data ad ON a.audit_id = ad.audit_id
WHERE ad.col = 'sales' AND a.triggered_datetime > NOW() - INTERVAL 7 DAY
GROUP BY a.row_id
) d7,
(
Select a.row_id, MAX(ad.new_value) - MIN(ad.new_value) AS salesdays
From _audit a
LEFT JOIN _audit_data ad ON a.audit_id = ad.audit_id
WHERE ad.col = 'sales' AND a.triggered_datetime > NOW() - INTERVAL 30 DAY
GROUP BY a.row_id
) d30
where d7.row_id = d30.row_id
ORDER BY sales7days DESC;
assume you want the same row id for both - and either value to show, you may or may not want to make it inner or outer joined and/or COALESCE the value fields (don't know enough about the data).