Having a strange issue using right join in query - mysql

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;

Related

sql for Group By and SUM with date range condition

Below is the Invoices table:
I am trying to make a sql query which gives me output based on due_date range with the sum of balance_amount group by company_id
My try:
select invoices.company_id,
SUM(invoices_cmonth.balance_amount) as cmonth,
SUM(invoices_1month.balance_amount) as 1month,
SUM(invoices_2month.balance_amount) as 2month
from `invoices`
LEFT JOIN invoices invoices_cmonth
ON (invoices.company_id = invoices_cmonth.company_id and invoices_cmonth.due_date >= '2021-11-10')
LEFT JOIN invoices invoices_1month
ON (invoices.company_id = invoices_1month.company_id and invoices_1month.due_date < '2021-11-10' and invoices_1month.due_date >= '2021-10-10')
LEFT JOIN invoices invoices_2month
ON (invoices.company_id = invoices_2month.company_id and invoices_2month.due_date < '2021-10-10' and invoices_2month.due_date >= '2021-9-10')
where invoices.`status` = 'ACTIVE'
and invoices.`balance_amount` > 0
and `invoices`.`deleted_at` is null
group by invoices.`company_id`
But it is giving me wrong figures in balance amount.
I suggest just making a single pass over the invoices table using conditional aggregation for the various time windows:
SELECT
company_id,
SUM(CASE WHEN due_date >= '2021-11-10' THEN balance_amount ELSE 0 END) AS cmonth,
SUM(CASE WHEN due_date >= '2021-10-10' AND due_date < '2021-11-10'
THEN balance_amount ELSE 0 END) AS 1month,
SUM(CASE WHEN due_date >= '2021-09-10' AND due_date < '2021-10-10'
THEN balance_amount ELSE 0 END) AS 2month
FROM invoices
WHERE
status = 'ACTIVE' AND balance_amount > 0 AND deleted_at IS NULL
GROUP BY
company_id;

How sum values in days intervals MySQL 5.7?

I have a server with MySQL 5.7.
I have two tables. First one t contains creating dates for each id. Second table t0 contains profit records day by day for each id.
I want to get columns with sums of profit for first and second 30 days for each id as well as for the first day.
SELECT t.created_at,
t.id,
sum(t1.profit) profit_1_week,
sum(t2.profit) profit_2_week,
sum(t3.profit) profit_1_day
FROM t
LEFT JOIN t0 t1 ON t.id = t.id
AND t1.inport_date BETWEEN t.created_at AND DATE_ADD(t.created_at, INTERVAL 30 DAY)
LEFT JOIN t0 t2 ON t.id = t.id
AND t2.inport_date BETWEEN DATE_ADD(t.created_at, INTERVAL 30 DAY) AND DATE_ADD(t.created_at, INTERVAL 60 DAY)
LEFT JOIN t0 t3 ON t.id = t.id
AND t3.inport_date BETWEEN t.created_at AND DATE_ADD(t.created_at, INTERVAL 1 DAY)
GROUP BY t.created_at,
t.id
ORDER BY t.created_at
This code runs but sums are wrong because sum of the first day much more then monthly. Where I'm wrong and how to fix it?
Your problem statement is not that clear, but based on your attempt, I suspect that you can do conditional aggregation:
select t.created_at, t.id,
sum(case when t0.inport_date >= t.created_at and t0.inport_date < t.created_at + interval 30 day then t0.profit else 0 end) profit_1,
sum(case when t0.inport_date >= t.created_at + interval 30 day and t0.inport_date < t.created_at + interval 60 day then t0.profit else 0 end) profit_2,
sum(case when t0.inport_date >= t.created_at and t0.inport_date < t.created_at + interval 1 day then t0.profit else 0 end) profit_3
from t
left join t0 on t0.id = t.id
group by t.created_at, t.id
order by t.created_at
The logic is to join just once, and then to use case expressions within the sum()s to choose which values should be taken into account.
I changed the date filtering logic to use half-open intervals rather than between, because it seems more relevant to me. You can adapt that as you wish according to your actual use case.

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

SQL query summary issue

I'm new to SQL and trying to create a total summary of a working SQL query. It's listing the total results from one month of data.
Now I need the total values of the outcome of the query.
So I created a 'query in a query' piece of SQL, but it ain't working because my lack of SQL knowledge. I guess it's an easy fix for you pro's :-)
The working SQL query with the daily outcome of one month:
SELECT
DATE_FORMAT(date, '%d/%m/%y') AS Datum,
COUNT(*) AS Berichten,
SUM(CASE WHEN virusinfected>0 THEN 1 ELSE 0 END) AS Virus,
SUM(CASE WHEN (virusinfected=0 OR virusinfected IS NULL) AND isspam>0 THEN 1 ELSE 0 END) AS Ongewenst,
SUM(CASE WHEN (virusinfected=0 OR virusinfected IS NULL) AND (isspam=1) AND isrblspam>0 THEN 1 ELSE 0 END) AS RBL,
SUM(size) AS Grootte
FROM
maillog
WHERE
1=1
AND (1=1)
AND
date < '2017-04-01'
AND
date >= '2017-03-01'
AND
to_domain = 'domain1.nl'
OR
date < '2017-04-01'
AND
date >= '2017-03-01'
AND
to_domain = 'domain2.nl'
GROUP BY
Datum
ORDER BY
date
The incorrect query trying to create the monthly totals:
SELECT Datum,
SUM(Berichten) AS Berichten,
SUM(Virus) AS Virus,
SUM(Ongewenst) AS Ongewenst,
SUM(RBL) AS RBL,
SUM(Grootte) AS Grootte,
FROM ( SELECT
DATE_FORMAT(date, '%d/%m/%y') AS Datum,
COUNT(*) AS Berichten,
SUM(CASE WHEN virusinfected>0 THEN 1 ELSE 0 END) AS Virus,
SUM(CASE WHEN (virusinfected=0 OR virusinfected IS NULL) AND isspam>0 THEN 1 ELSE 0 END) AS Ongewenst,
SUM(CASE WHEN (virusinfected=0 OR virusinfected IS NULL) AND (isspam=1) AND isrblspam>0 THEN 1 ELSE 0 END) AS RBL,
SUM(size) AS Grootte
FROM
maillog
WHERE
1=1
AND (1=1)
AND
date < '2017-04-01'
AND
date >= '2017-03-01'
AND
to_domain = 'domain1.nl'
OR
date < '2017-04-01'
AND
date >= '2017-03-01'
AND
to_domain = 'domain2.nl'
GROUP BY
Datum
ORDER BY
date
) t
GROUP BY Datum;
Thanks in advance.
What you want can be done with just a little addition to your first SQL statement: add with rollup after the group by clause:
GROUP BY Datum WITH ROLLUP
It will run more efficiently than the version with sub-query, although it could work that way, but you should then remove the outer group by clause and not select Datum there, since you don't want the totals per date any more, but overall.
Still, you will lose the details and only get the overall totals then. You would have to use a union with your original query to get both levels of totals. You can imagine that the with rollup modifier will do the job more efficiently.

SELECT records using multiple critira

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.