Get greatest common value in a column across tables - mysql

I have 4 tables (say A, B, C and D) all with the column 'date'. I need to find the greatest common date value across all four tables. That is, the greatest value of date that exists in all four tables. How can I do this?
For now, I'm making do with finding the MIN of the MAX date values of all four tables, but this fails in the cases where the MIN exists in one table but not in the second.
Here is an example to make things clearer :
A.date
------
2015-03-31
2015-03-30
2015-03-29
2015-03-27
B.date
------
2015-03-30
2015-03-29
2015-03-28
2015-03-27
C.date
------
2015-03-29
2015-03-27
2015-03-26
2015-03-25
D.date
------
2015-03-28
2015-03-27
2015-03-26
2015-03-25
What I was doing to find the highest common date was :
SELECT MIN(max_date) FROM (
SELECT MAX(date) AS max_date FROM A
UNION
SELECT MAX(date) AS max_date FROM B
UNION
SELECT MAX(date) AS max_date FROM C
UNION
SELECT MAX(date) AS max_date FROM D
) T;
This gives me 2015-03-28, but then I realized that some tables might not have this date at all. The date I actually want to get is 2015-03-27.

Here is one method:
select date
from (select date, 'a' as which from a union all
select date, 'b' as which from b union all
select date, 'c' as which from c union all
select date, 'd' as which from d
) x
group by date
having count(distinct which) = 4
order by date desc
limit 1;
The following version might perform a bit better, especially if you have an index on date in each table:
select date
from (select distinct date, 'a' as which from a union all
select distinct date, 'b' as which from b union all
select distinct date, 'c' as which from c union all
select distinct date, 'd' as which from d
) x
group by date
having count(*) = 4
order by date desc
limit 1;

You need to get an intersection of all date values across the 4 separate tables. Then, select the MAX of these values:
SELECT MAX(date)
FROM A
WHERE date IN (
SELECT date
FROM B
WHERE date IN (
SELECT date
FROM C
WHERE date IN (
SELECT date
FROM D)))
SQL Fiddle Demo here

Related

MySQL Query to get each sales per month

I have 2 tables in Mysql. I want to regroup and count the Number of Orderid per month for each customer. If there is no order, I would like to add 0.
Customer Table
CustomerID
1
2
3
Order Table
OrderId CustomerID Date
1 1 2022-01-02
2 1 2022-01-04
3 2 2022-02-03
4 2 2022-03-03
Expect results
CustomerID Date CountOrderID
1 2022-01 2
2 2022-01 1
3 2022-01 0
1 2022-02 0
2 2022-02 1
3 2022-02 0
1 2022-03 0
2 2022-03 1
3 2022-03 0
How I can do this in Mysql?
SELECT customer.CustomerID,
year_month.y_m AS `Date`,
COUNT(order.OrderId) AS CountOrderID
FROM customer
CROSS JOIN (
SELECT DISTINCT DATE_FORMAT(`date`, '%Y-%m') AS y_m
FROM order
) AS year_month
LEFT JOIN order ON order.CustomerID = customer.CustomerID
AND DATE_FORMAT(order.`date`, '%Y-%m') = year_month.y_m
GROUP BY 1, 2;
If order table does not contains for some year and month then according row won't present in the output. If you need in it then you'd generate calendar table instead of year_month subquery.
you can reduce the number of cte's I added more here to explain the steps:
first you need the format year and month, for that I used DATE_FORMAT() function
since you need to have all the combination of dates and the year month you need a cross join. This will produce all the distinct dates with all the distinct customer id's. In other words all the pairs between dates and customer id
once you have a table with all the combinations you need to pass the actual data with the left join this will produce null where you actually don't have rows and hence will produce 0 when the count is performed
the last step is simply count function
with main as (
select distinct DATE_FORMAT(date,'%Y-%m') as year_month from order
),
calendar as (
select * from customer
cross join main
),
joining_all as (
select
calendar.*,
order. OrderId
left join order
on calendar.CustomerID = order.CustomerID
and calendar.year_month = DATE_FORMAT(order.date,'%Y-%m')
)
select
CustomerID,
year_month as Date,
count(OrderId) as CountOrderID
from joining_all
group by 1,2
maybe the shorter version can work with the code below. if runs into syntax you can use the one above
with main as (
select distinct DATE_FORMAT(date,'%Y-%m') as year_month from order
cross join customer
)
select
main.CustomerID,
main.year_month as Date,
count(order.OrderId) as CountOrderID
from main
left join order
on main.CustomerID = order.CustomerID
and main.year_month = DATE_FORMAT(order.date,'%Y-%m')
group by 1,2

Select 3 Different Columns (COUNT) from 3 different tables and group by Date

I have 3 different tables called that tracks the number of pens sold, pencils sold and ink sold.
**pens table:**
date who_bought
12.03.2020 a
12.03.2020 d
13.03.2020 b
14.03.2020 c
**pencils table:**
date who_bought
12.03.2020 z
16.03.2020 r
17.03.2020 j
17.03.2020 k
**ink table:**
date who_bought
11.03.2020 h
11.03.2020 j
13.03.2020 z
17.03.2020 r
I want to aggregate data and get for each day, how many pens, inks and pencils I sold. The "who_bought" column is not relevant (I mean the values). I just want to count the number of records for each day. finally, I want to sort by date.
In the above case, I would like to get results like
11.03.2020 pens:0 pencils:0 ink: 2
12.03.2020 pens:2 pencils:1 ink: 0
13.03.2020 pens:1 pencils:0 ink:1
14.03.2020 pens:0 pencils:0 ink:0
16.03.2020 pens:0 pencils:1 ink:0
17.03.2020 pens:0 pencils:2 ink:1
How can this be achieved?
I tried something like this, but it's not working:
select
COUNT(pens.*) as pens,
COUNT(pencils.*) as pencils,
COUNT(ink.*) as ink,
DATE(date) as date
from
pens
full join pencils on pencils.date=pens.date
full join ink on ink.date=pens.date
group by
date
order by
date asc
Your attempt using full join is on the right track; unfortunately, MySQL does not support this syntax.
You could do this with union all and conditional aggregation:
select
date,
sum(what = 'pens') no_pens,
sum(what = 'pencils') no_pencils,
sum(what = 'ink') no_inks
from (
select 'pens' what, date from pens
union all select 'pencils', date from pencils
union all select 'ink', date from ink
) t
group by date
If you want all dates, including those for which no sale happened for any product, then it is a bit different. Basically, ou need a calendar table for this. Here is one way to do it with a recursive query (available in MySQL 8.0 only).
with dates as (
select min(date) date, max(date) max_date
from (
select date from pens
union all select date from pencils
union all select date from ink
) t
union all
select date + interval 1 day from cte where date < max_date
)
select
d.date,
pn.no_pens,
pl.no_pencils,
ik.no_inks
from dates d
left join (select date, count(*) no_pens from pens group by date) pn on pn.date = d.date
left join (select date, count(*) no_pencils from pencils group by date) pl on pl.date = d.date
left join (select date, count(*) no_inks from inks group by date) ik on ik.date = d.date

MYSQL union how to maintain date field order when date came from 2 fields?

I have two tables Transactions and Expenses. I have written a query to get date wise transaction statement. Here Transactions table is deposit table. For this query I am getting my desire result without order date.
SELECT IFNULL(date(t1.created), date(ex.created)) as Date , sum(t1.amount) as ReceiveAmount,ex.amount as ExpensesAmount
FROM transactions as t1
LEFT JOIN (
SELECT sum(e.amount) as amount, created
FROM expenses as e
group by date(e.created)
) as ex
ON date(ex.created) = date(t1.created)
GROUP BY date(t1.created)
UNION
SELECT IFNULL(date(t1.created), date(ex.created)) as Date, sum(t1.amount) as Receive,ex.amount as ExpensesAmount
FROM transactions as t1
RIGHT JOIN (
SELECT sum(e.amount) as amount, created
FROM expenses as e
group by date(e.created)
) as ex
ON date(t1.created) = date(ex.created)
GROUP BY date(t1.created)
OUTPUT :
Date ReceiveAmount ExpensesAmount
2018-12-04 600 NULL
2019-08-01 500 NULL
2019-10-18 500 NULL
2019-11-18 820 500 <== that should come at last.
2019-11-04 NULL 100
I need to see date ASC order. Here last 2 date 2019-11-18 and 2019-11-04 not maintaining ORDER. How can I solve this problem ?
You may add an ORDER BY clause to your union query, after placing both halves of the union in parentheses:
(SELECT IFNULL(t1.created, DATE(ex.created)) AS Date, SUM(t1.amount) AS ReceiveAmount,
ex.amount AS ExpensesAmount
FROM transactions as t1
LEFT JOIN
...
)
UNION ALL
(SELECT IFNULL(t1.created, DATE(ex.created)), SUM(t1.amount), ex.amount
FROM transactions as t1
RIGHT JOIN
...
)
ORDER BY Date
I assume here that you really want a UNION ALL, and not a UNION. Note that in most other RDBMS you would have to use a formal subquery to apply an ORDER BY clause to the entire union query.

mysql finding the sum of subgroup maximums

If I have the following table in MySQL:
date type amount
2017-12-01 3 2
2018-01-01 1 100
2018-02-01 1 50
2018-03-01 2 2000
2018-04-01 2 4000
2018-05-01 3 2
2018-06-01 3 1
...is there a way to find the sum of the amounts corresponding to the latest dates of each type? There are guaranteed to be no duplicate dates for any given type.
The answer I'd be looking to get from the data above could broken down like this:
The latest date for type 1 is 2018-02-01, where the amount is 50;
The latest date for type 2 is 2018-04-01, where the amount is 4000;
The latest date for type 3 is 2018-06-01, where the amount is 1;
50 + 4000 + 1 = 4051
Is there a way to arrive directly at 4051 in a single query? This is for a Django project using MySQL if that makes a difference; I wasn't able to find an ORM-related solution either, so figured a raw SQL query might be a better place to start.
Thanks!
Not sure for Django but in raw sql you could use a self join to pick latest row for each type based on latest date and then aggregate your results to get the sum of amounts for each type
select sum(a.amount)
from your_table a
left join your_table b on a.type = b.type
and a.date < b.date
where b.type is null
Demo
Or
select sum(a.amount)
from your_table a
join (
select type, max(date) max_date
from your_table
group by type
) b on a.type = b.type
and a.date = b.max_date
Demo
Or by using a correlated subuery
select sum(a.amount)
from your_table a
where a.date = (
select max(date)
from your_table
where type = a.type
)
Demo
For Mysql 8 you can use window functions to get you desired result as
select sum(amount)
from (select *, row_number() over (partition by type order by date desc) as seq
from your_table
) t
where seq = 1;
Demo

How to Count Rows from two Tables with one Command in MySQL

i want to count 2 tables from diferent table, im select the date then i group it.
here what i try
SELECT
(SELECT date(date), COUNT(sh_sh) FROM sh_url GROUP BY date(date)) AS URLs,
(SELECT date(date), COUNT(ip) FROM tracking GROUP BY date(date)) AS IP
FROM dual
but i get error
1241 - Operand should contain 1 column(s)
is that posible to do it in one command?
the output should be like this
date(url) count(sh_sh) date(ip) count(ip)
--------- ------------ ---------- ----------
2018-04-25 123 2018-04-25 123123
2018-04-26 456 2018-04-26 321
2018-04-27 789 2018-04-27 3125
I would phrase your problems using a join of two subqueries:
SELECT
t1.date,
t1.url_cnt,
COALESCE(t2.ip_cnt, 0) AS ip_cnt
FROM
(
SELECT date, COUNT(*) url_cnt
FROM sh_url
GROUP BY date
) t1
LEFT JOIN
(
SELECT date, COUNT(*) ip_cnt
FROM tracking
GROUP BY date
) t2
ON t1.date = t2.date;
When you use a subquery as a value, it can only return one row and one column. You need to use a JOIN:
SELECT urls.date, urls.count AS sh_count, ip.count AS ip_count
FROM (SELECT date(date) AS date, COUNT(*) AS count FROM sh_url GROUP BY date(date)) AS urls
JOIN (SELECT date(date) AS date, COUNT(*) AS count FROM tracking GROUP BY date(date)) AS ip
ON urls.date = ip.date