Single query to retrieve multiple values from multiple tables - mysql

Expenses table
1/1/2016 exp1 2000
13/1/2016 exp11 2500
1/2/2016 exp2 1500
1/3/2016 exp3 1000
10/3/2016 exp1 2000
Income table
1/1/2016 income1 2500
1/2/2016 income2 3500
1/3/2016 income3 1500
10/3/2016 income3 1000
1/4/2016 income4 5000
From single query what I need is group by month, this is what I need
Expenses Incomes Month
4500 2500 Jan
1500 3500 Feb
3000 2500 Mar
0 5000 April
I need the above query to show the data in Google graph

Terrible data structure and format, but not impossible:
SELECT
IFNULL(exp.Expenses,0) Expenses,
IFNULL(inc.Incomes,0) Incomes,
inc.`monthname` `Month`
FROM
(
SELECT
SUM(i.amount) Incomes,
MONTHNAME(STR_TO_DATE(i.`date`, '%d/%m/%Y')) `monthname`,
MONTH(STR_TO_DATE(i.`date`, '%d/%m/%Y')) `month`
FROM
incomes i
GROUP BY
MONTHNAME(STR_TO_DATE(i.`date`, '%d/%m/%Y')),
MONTH(STR_TO_DATE(i.`date`, '%d/%m/%Y'))
) inc
LEFT JOIN
(
SELECT
SUM(e.amount) Expenses,
MONTHNAME(STR_TO_DATE(e.`date`, '%d/%m/%Y')) `monthname`,
MONTH(STR_TO_DATE(e.`date`, '%d/%m/%Y')) `month`
FROM
expenses e
GROUP BY
MONTHNAME(STR_TO_DATE(e.`date`, '%d/%m/%Y')),
MONTH(STR_TO_DATE(e.`date`, '%d/%m/%Y'))
) exp
ON exp.`month` = inc.`month`
ORDER BY
inc.`month`
Output of this simplicity:
+----------+---------+----------+
| Expenses | Incomes | Month |
+----------+---------+----------+
| 4500 | 2500 | January |
| 1500 | 3500 | February |
| 3000 | 2500 | March |
| 0 | 5000 | April |
+----------+---------+----------+
4 rows in set
Anyway better thing seriously how to improve and normalize your data.

In my solution, I give the number of the month rather than text. I'll leave it to you to convert it to text (using a CASE expression, for example) if you wish:
SELECT
sum(expense) AS total_expense, sum(income) AS total_income, trans_month
FROM (
SELECT
month(trans_date) AS trans_month,
0 AS income,
sum(amount) AS expense
FROM expense
GROUP BY month(trans_date)
UNION ALL
SELECT
month(trans_date) AS trans_month,
sum(amount) AS income,
0 AS expense
FROM income
GROUP BY month(trans_date)
) AS a
GROUP BY trans_month;

Related

Conditional Window Functions

I have a sales table that looks like this:
store_id cust_id txn_id txn_date amt industry
200 1 1 20180101 21.01 1000
200 2 2 20200102 20.01 1000
200 2 3 20200103 19 1000
200 3 4 20180103 19 1000
200 4 5 20200103 21.01 1000
300 2 6 20200104 1.39 2000
300 1 7 20200105 12.24 2000
300 1 8 20200105 25.02 2000
400 2 9 20180106 103.1 1000
400 2 10 20200107 21.3 1000
Here's the code to generate this sample table:
CREATE TABLE sales(
store_id INT,
cust_id INT,
txn_id INT,
txn_date bigint,
amt float,
industry INT);
INSERT INTO sales VALUES(200,1,1,20180101,21.01,1000);
INSERT INTO sales VALUES(200,2,2,20200102,20.01,1000);
INSERT INTO sales VALUES(200,2,3,20200103,19.00,1000);
INSERT INTO sales VALUES(200,3,4,20180103,19.00,1000);
INSERT INTO sales VALUES(200,4,5,20200103,21.01,1000);
INSERT INTO sales VALUES(300,2,6,20200104,1.39,2000);
INSERT INTO sales VALUES(300,1,7,20200105,12.24,2000);
INSERT INTO sales VALUES(300,1,8,20200105,25.02,2000);
INSERT INTO sales VALUES(400,2,9,20180106,103.1,1000);
INSERT INTO sales VALUES(400,2,10,20200107,21.3,1000);
What I would like to do is create a new table, results that answers the question: what percentage of my VIP customers have, since January 3rd 2020, shopped i) at my store only; ii) at my store and at other stores in the same industry; iii) at only other stores in the same industry? Define a VIP customer to be someone who has shopped at a given store at least once since 2019.
Here's the target output table:
store industry pct_my_store_only pct_both pct_other_stores_only
200 1000 0.5 0.5 0.0
300 2000 0.5 0.5 0.0
400 1000 0.0 1.0 0.0
I'm trying to use window functions to accomplish this. Here's what I have so far:
CREATE TABLE results as
SELECT s.store_id, s.industry,
COUNT(DISTINCT (CASE WHEN s.txn_date>=20200103 THEN s.cust_id END)) * 1.0 / sum(count(DISTINCT (CASE WHEN s.txn_date>=20200103 THEN s.cust_id END))) OVER (PARTITION BY s.industry) AS pct_my_store_only
...AS pct_both
...AS pct_other_stores_only
FROM sales s
WHERE sales.txn_date>=20190101
GROUP BY s.store_id, s.industry;
The above does not seem to be correct; how can I correct this?
Join the distinct store_ids and industries to the concatenated distinct store_ids and industries for each customer and then use window function avg() with the function find_in_set() to determine if a customer how many customer have shopped or not from each store:
with
stores as (
select distinct store_id, industry
from sales
where txn_date >= 20190103
),
customers as (
select cust_id,
group_concat(distinct store_id) stores,
group_concat(distinct industry) industries
from sales
where txn_date >= 20190103
group by cust_id
),
cte as (
select *,
avg(concat(s.store_id) = concat(c.stores)) over (partition by s.store_id, s.industry) pct_my_store_only,
avg(find_in_set(s.store_id, c.stores) = 0) over (partition by s.industry) pct_other_stores_only
from stores s inner join customers c
on find_in_set(s.industry, c.industries) and find_in_set(s.store_id, c.stores)
)
select distinct store_id, industry,
pct_my_store_only,
1 - pct_my_store_only - pct_other_stores_only pct_both,
pct_other_stores_only
from cte
order by store_id, industry
See the demo.
Results:
> store_id | industry | pct_my_store_only | pct_both | pct_other_stores_only
> -------: | -------: | ----------------: | -------: | --------------------:
> 200 | 1000 | 0.5000 | 0.5000 | 0.0000
> 300 | 2000 | 0.5000 | 0.5000 | 0.0000
> 400 | 1000 | 0.0000 | 1.0000 | 0.0000

Income and Expense calculation

I need to show income and expense per day
Income and expenses are different table
I need to show in following format, for ex:
17/08/2019 date have two incomes in the table
I need to calculate sum of the income in the date, showing on the result with the same day expense.
I have tried with some queries, but it's not working.
Date | Income | Expense | Profit
Select SUM(d.amount)
, SUM(e.amount)
, d.date
, e.date
FROM due d
JOIN expenses e
ON d.date = e.date
Expense table -table-name : expenses
id | date | details | amount
1 13-08-2019 daily 50
2 17-08-2019 cleaning 50
3 17-08-2019 cleaning 50
4 18-08-2019 Tea 150
5 18-08-2019 other 50
Income table -table-name : due
id | date | amount
4 12-08-2019 150
5 13-08-2019 100
6 18-08-2019 450
7 18-08-2019 50
result will be:
id | date | Income | Expense | Profit
1 12-08-2019 150 NULL 150
2 13-08-2019 100 50 50
3 17-08-2019 NULL 100 -100
4 18-08-2019 500 200 300
In the future, I'd suggest posting some table details by using SHOW CREATE TABLE table_name which will allow us to better assist you.
You should be able to use a union and some grouping to get what you are after:
SELECT
Date,
SUM(Income) as Income,
SUM(Expense) as Expense,
SUM(Income) - SUM(Expense) as Profit
FROM (
SELECT
due.date as Date,
due.amount as Income,
0 as Expense
FROM due
UNION ALL
SELECT
expenses.date as Date
0 as Income,
expenses.amount as Expense
FROM expenses
)
GROUP BY Date

get total amount of expenses per month in MySQL

I am currently working with 2 tables, expenses and income. To keep the structure simple and can see it, this is the fiddle: http://sqlfiddle.com/#!9/256cd64/2
The result I need from my query is the total amount for each month of the current year, for this and tried something like this:
select sum(e.amount) as expense, DATE_FORMAT(e.date,'%m') as month
from expenses e
where year(e.date) = 2019
group by month
My problem with this is that it only takes me the months where there was registration and I would like it to take 12 months whether or not they have a registration, in the case that they did not return 0 as a total amount.
At the moment I am working with the table of expenses but I would like to have a single query that returns the monthly expenses and income, this is an example of the final output that I would like to obtain:
| Month | Expense| Incomes |
|---------|--------|---------|
| 01| 0 | 0 |
| 02| 3000 | 4000 |
| 03| 1500 | 5430 |
| 04| 2430 | 2000 |
| 05| 2430 | 1000 |
| 06| 2430 | 1340 |
| 07| 0 | 5500 |
| 08| 2430 | 2000 |
| 09| 1230 | 2000 |
| 10| 8730 | 2000 |
| 11| 2430 | 2000 |
| 12| 6540 | 2000 |
You need to generate the month values and then use left join to match to expenses:
select coalesce(sum(e.amount), 0) as expense, m.month
from (select '01' as month union all
select '02' as month union all
select '03' as month union all
select '04' as month union all
select '05' as month union all
select '06' as month union all
select '07' as month union all
select '08' as month union all
select '09' as month union all
select '10' as month union all
select '11' as month union all
select '12' as month
) m left join
expenses e
on year(e.date) = 2019 and
DATE_FORMAT(e.date,'%m') = m.month
group by m.month;
Here is a db<>fiddle.
As for income, you should ask another question about that.
You can use MONTH to get month value from your date column and then GROUP BY them to get your desired output as below-
SELECT SUM(e.amount) AS expense,
MONTH(e.date) AS month
FROM expenses e
WHERE YEAR(e.date) = 2019
GROUP BY MONTH(e.date)
Try changing your sum(e.amount) as expense to: COALESCE(sum(e.amount),0) as expense
The COALESCE function returns the first non NULL value.
SELECT
t1.month,
COALESCE(t2.amount, 0) AS expenses,
COALESCE(t3.amount, 0) AS incomes
FROM
(
SELECT 1 AS month UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5 UNION ALL
SELECT 6 UNION ALL
SELECT 7 UNION ALL
SELECT 8 UNION ALL
SELECT 9 UNION ALL
SELECT 10 UNION ALL
SELECT 11 UNION ALL
SELECT 12
) t1
LEFT JOIN
(
SELECT MONTH(date) AS month, SUM(amount) AS amount
FROM expenses
GROUP BY MONTH(date)
) t2
ON t1.month = t2.month
LEFT JOIN
(
SELECT MONTH(date) AS month, SUM(amount) AS amount
FROM incomes
GROUP BY MONTH(date)
) t3
ON t1.month = t3.month
ORDER BY
t1.month;

I need two different column values based on different condition from same table

Heading
i want to get result sum of 1st record of each 'tank id' per day wise and sum of 'receipt' per day wise and whole data will be limited for particular month
SELECT
DATE(t.DATE) AS 'Date',
SUM(t.in_stock) AS 'Opening Stock',
SUM(receipt) AS 'Receipt',
SUM(in_stock + receipt)AS 'Total Stock'
FROM diesel_tank_details t
WHERE NOT EXISTS (
SELECT
1
FROM diesel_tank_details t2
WHERE
DATE(t2.DATE) = DATE(t.DATE)
AND t2.tank_id = t.tank_id
AND t2.dt_id < t.dt_id
)
AND YEAR(DATE) = 2019
AND MONTH(DATE) = 7
GROUP BY DATE(DATE)
Tank_ID In_Stock Receipt Date
1 1000 1000 2019-07-10 00:28:33
2 2000 2000 2019-07-10 00:28:40
3 3000 3000 2019-07-10 00:28:47
1 300 0 2019-07-10 00:32:40
1 250 0 2019-07-15 15:15:06
3 2500 0 2019-07-15 15:15:37
2 1800 0 2019-07-15 15:35:49
3 2200 0 2019-07-15 16:13:17
1 500 250 2019-07-15 16:13:51
2 2000 200 2019-07-15 16:13:57
3 3000 800 2019-07-15 16:14:03
i need this Output from above table
Date Opening Stock Receipt Total Stock
2019-07-10 6000 6000 12000
2019-07-15 4550 1250 4550
Since you want different conditions for stock and receipt you must sum different rows for each case:
select s.day, s.`Opening Stock`, r.Receipt,
(s.`Opening Stock` + s.Receipt) `Total Stock`
from (
select g.day, sum(t.in_stock) `Opening Stock`, sum(t.receipt) receipt
from diesel_tank_details t inner join (
select date(date) day, tank_id, min(date) mindate
from diesel_tank_details
where in_stock <> 0
group by date(date), tank_id
) g on g.tank_id = t.tank_id and g.mindate = t.date
group by g.day
) s inner join (
select g.day, sum(t.receipt) Receipt
from diesel_tank_details t inner join (
select date(date) day, tank_id, min(date) mindate
from diesel_tank_details
where receipt <> 0
group by date(date), tank_id
) g on g.tank_id = t.tank_id and g.mindate = t.date
group by g.day
) r on r.day = s.day
See the demo.
Results:
| day | Opening Stock | Receipt | Total Stock |
| ---------- | ------------- | ------- | ----------- |
| 2019-07-10 | 6000 | 6000 | 12000 |
| 2019-07-15 | 4550 | 1250 | 4550 |
Query:
SELECT DATE(t.Date) as 'Date',
SUM(t.In_Stock) as 'Opening Stock',
SUM(t.Receipt) as 'Receipt',
SUM(t.In_Stock + t.Receipt) as 'Total Stock'
FROM diesel_tank_details t
WHERE AND YEAR(t.Date) = 2019
AND MONTH(t.Date) = 7
GROUP BY DATE(t.Date)
Just include your additional filter. I don't know what DT_ID does so i just removed it. but basically you just wanted to group it by day.

Get last row for given dates if date doesn't exist

How can we query a set of records to get data for particular dates where there might be gaps in dates.
Example Data
date | Price
------------------
2018-03-31 | 115
2018-03-29 | 114
2018-03-28 | 113
...
2017-03-29 | 117
2017-03-28 | 118
...
2016-12-30 | 143
2016-12-29 | 140
...
2015-12-31 | 110
2015-12-30 | 111
Required Data for dates: 2018-03-31, 2017-03-31, 2016-12-31, 2015-12-31
date | Price
------------------
2018-03-31 | 115
2017-03-31 | 117
2016-12-31 | 143
2015-12-31 | 110
You can do this with correlated sub query. The following will return the price for the exact date or the closest prior date:
SELECT dates.dt, (
SELECT price
FROM t
WHERE date <= dates.dt
ORDER BY date DESC
LIMIT 1
) AS price
FROM (
SELECT '2018-03-31' AS dt UNION ALL
SELECT '2017-03-31' UNION ALL
SELECT '2016-12-31' UNION ALL
SELECT '2015-12-31'
) AS dates
Demo on db<>fiddle
You can use MySQL Last day function and date_format to achieve the desired result. Check out the query :-
select last_day(a11.d_date), a11.price
from test a11
join
(select MAX(d_date) d_date, DATE_FORMAT(d_date, "%M %Y")
from test
group by DATE_FORMAT(d_date, "%M %Y")
) a12
on a11.d_date = a12.d_date
SQL Fiddle
seem you need the max price for year
select max(date), max(price)
from my_table m
inner join (
select max(date), year(date) my_year
from my_table
group by year(date)
) t on t.my_year = year(m.date)
group by year(m.date)