Combine 2 SELECTS in one result Side By Side - mysql

I have 2 selects that just get the SUM of IDs and are grouped by month.. I would like to have them only on 1 SELECT - side by side...
SELECT MONTH(data) AS month, COUNT(id) AS TOTAL
FROM numeracao
WHERE data BETWEEN '2023-01-01' AND '2023-12-31'
GROUP BY MONTH(DATA);
the 2nd select:
SELECT MONTH(dia) AS month, COUNT(id) AS TOTAL
FROM revisoes
WHERE dia BETWEEN '2023-01-01' AND '2023-12-31'
GROUP BY MONTH(dia)
The rewsult was to be expected somethinig like this:
month
NUM
REV
1
22
4
2
52
23
...
...
...
where:
month is from 1 to 12
NUM is the sum of query 1 on table numeracao
REV is the sum of query 2 on table revisoes

You can do this by using union and then an outer aggregation:
select Month, Max(Num) Num, Max(Rev) Rev
from (
select Month(data) as month, Count(id) Num, null Rev
from numeracao
where data between '2023-01-01' and '2023-12-31'
group by Month(DATA)
union all
select Month(dia) as month, null Num, Count(id) Rev
from revisoes
where dia between '2023-01-01' and '2023-12-31'
group by Month(dia)
)t;

You can apply the inner join on the results of the aggregations to make counts appear on the same row for each month.
SELECT numtab.month,
numtab.num,
revtab.rev
FROM (SELECT MONTH(data) AS month, COUNT(id) AS num
FROM numeracao
WHERE data BETWEEN '2023-01-01' AND '2023-12-31'
GROUP BY MONTH(DATA)) numtab
INNER JOIN (SELECT MONTH(dia) AS month, COUNT(id) AS rev
FROM revisoes
WHERE dia BETWEEN '2023-01-01' AND '2023-12-31'
GROUP BY MONTH(dia)) revtab
ON numtab.month = revtab.month
Computing the aggregation after the inner join would bring to duplicate rows and incorrect output, given than the two tables most likely have a n-to-n association.
If you're not ensured to have at least one value for all months on both tables, you should have a month calendar table and use left join instead.
SELECT calendar.month,
numtab.num,
revtab.rev
FROM (SELECT 1 AS month UNION ALL
SELECT 2 AS month UNION ALL
SELECT 3 AS month UNION ALL
SELECT 4 AS month UNION ALL
SELECT 5 AS month UNION ALL
SELECT 6 AS month UNION ALL
SELECT 7 AS month UNION ALL
SELECT 8 AS month UNION ALL
SELECT 9 AS month UNION ALL
SELECT 10 AS month UNION ALL
SELECT 11 AS month UNION ALL
SELECT 12 AS month ) calendar
LEFT JOIN (SELECT MONTH(data) AS month, COUNT(id) AS num
FROM numeracao
WHERE data BETWEEN '2023-01-01' AND '2023-12-31'
GROUP BY MONTH(DATA)) numtab
ON calendar.month = numtab.month
LEFT JOIN (SELECT MONTH(dia) AS month, COUNT(id) AS rev
FROM revisoes
WHERE dia BETWEEN '2023-01-01' AND '2023-12-31'
GROUP BY MONTH(dia)) revtab
ON calendar.month = revtab.month
I wouldn't recommend this one anyways if your problem gets solved with the former query.

SELECT month,TOTAL FROM(SELECT MONTH(data) AS month, COUNT(id) AS TOTAL
FROM numeracao
WHERE data BETWEEN '2023-01-01' AND '2023-12-31'
GROUP BY MONTH(DATA)
UNION ALL
SELECT MONTH(dia) AS month, COUNT(id) AS TOTAL
FROM revisoes
WHERE dia BETWEEN '2023-01-01' AND '2023-12-31' )as rows
GROUP BY month

Related

How to set default value from mysql join interval yearmonth

I have problem with my query. I have two tables and I want join them to get the results based on primary key on first table, but I missing 1 data from first table.
this my fiddle
as you can see, I missing "xx3" from month 1
I have tried to change left and right join but, the results stil same.
So as you can see I have to set coalesce(sum(b.sd_qty),0) as total, if no qty, set 0 as default.
You should cross join the table to the distinct dates also:
SELECT a.item_code,
COALESCE(SUM(b.sd_qty), 0) total,
DATE_FORMAT(d.sd_date, '%m-%Y') month_year
FROM item a
CROSS JOIN (
SELECT DISTINCT sd_date
FROM sales_details
WHERE sd_date >= '2020-04-01' - INTERVAL 3 MONTH AND sd_date < '2020-05-01'
) d
LEFT JOIN sales_details b
ON a.item_code = b.item_code AND b.sd_date = d.sd_date
GROUP BY month_year, a.item_code
ORDER BY month_year, a.item_code;
Or, for MySql 8.0+, with a recursive CTE that returns the starting dates of all the months that you want the results, which can be cross joined to the table:
WITH RECURSIVE dates AS (
SELECT '2020-04-01' - INTERVAL 3 MONTH AS sd_date
UNION ALL
SELECT sd_date + INTERVAL 1 MONTH
FROM dates
WHERE sd_date + INTERVAL 1 MONTH < '2020-05-01'
)
SELECT a.item_code,
COALESCE(SUM(b.sd_qty), 0) total,
DATE_FORMAT(d.sd_date, '%m-%Y') month_year
FROM item a CROSS JOIN dates d
LEFT JOIN sales_details b
ON a.item_code = b.item_code AND DATE_FORMAT(b.sd_date, '%m-%Y') = DATE_FORMAT(d.sd_date, '%m-%Y')
GROUP BY month_year, a.item_code
ORDER BY month_year, a.item_code;
See the demo.

Merging together results from a UNION in sql

I am trying to combine the results of a Union from
SELECT MONTHNAME(terms) AS month, COUNT(DISTINCT project_num) as total
FROM projects
WHERE terms >= '2017/01/01' AND Building_designer='SOMEPERSON'
GROUP BY MONTH(terms)
UNION
SELECT MONTHNAME(terms) AS month, COUNT(DISTINCT project_num) as total
FROM archive
WHERE terms >= '2017/01/01' AND Building_designer='SOMEPERSON'
GROUP BY MONTH(terms)
I get the following: RESULTS FROM THE SQL STATEMENT
I am trying to make it so the total will be the combination of the multiple instances of the month.
The sql tables are exactly the same.
This Is what I would like it to look like:
A FULL OUTER JOIN would be ideal. But in your case, let's do two levels of aggregation:
SELECT month, MAX(total_projects) as total_projects, MAX(total_archive) as total_archive
FROM ((SELECT MONTHNAME(terms) AS month, COUNT(DISTINCT project_num) as total_projects, 0 as total_archive
FROM projects
WHERE terms >= '2017/01/01' AND Building_designer = 'SOMEPERSON'
GROUP BY MONTH(terms)
) UNION ALL
(SELECT MONTHNAME(terms) AS month, 0, COUNT(DISTINCT project_num
FROM archive
WHERE terms >= '2017/01/01' AND Building_designer = 'SOMEPERSON'
GROUP BY MONTH(terms)
)
) pa
GROUP BY month
ORDER BY month;
EDIT:
Oops. You only want one column. If you want to count the number of distinct projects for each month, then do a union all and then combine the results at the next higher level:
SELECT month, COUNT(DISTINCT project_num) as total
FROM ((SELECT MONTHNAME(terms) AS month, project_num
FROM projects
WHERE terms >= '2017/01/01' AND Building_designer = 'SOMEPERSON'
) UNION ALL
(SELECT MONTHNAME(terms) AS month, project_num
FROM archive
WHERE terms >= '2017/01/01' AND Building_designer = 'SOMEPERSON'
)
) pa
GROUP BY month
ORDER BY month;
A quick thought would be to just do something like this. You essentially want to sum the counts from each table.
select month, sum(total) from
(
SELECT MONTHNAME(terms) AS month, COUNT(DISTINCT project_num) as total FROM projects WHERE terms >= '2017/01/01' AND Building_designer='SOMEPERSON' GROUP BY MONTH(terms)
UNION
SELECT MONTHNAME(terms) AS month, COUNT(DISTINCT project_num) as total FROM archive WHERE terms >= '2017/01/01' AND Building_designer='SOMEPERSON' GROUP BY MONTH(terms)
) group by month;
transform into a derived table, put an alias then aggregate
select
x.month,
sum(x.total) [Total]
from (
SELECT
MONTHNAME(terms) AS month,
COUNT(DISTINCT project_num) AS total
FROM projects
WHERE terms >= '2017/01/01'
AND Building_designer = 'SOMEPERSON'
GROUP BY MONTH(terms)
UNION
SELECT
MONTHNAME(terms) AS month,
COUNT(DISTINCT project_num) AS total
FROM archive
WHERE terms >= '2017/01/01'
AND Building_designer = 'SOMEPERSON'
GROUP BY MONTH(terms)
) x
group by x.month
You can try creating a sum expression on the entire query.
SELECT month, SUM (total) FROM
(SELECT MONTHNAME(terms) AS month, COUNT(DISTINCT project_num) as total FROM projects WHERE terms >= '2017/01/01' AND Building_designer='SOMEPERSON' GROUP BY MONTH(terms)
UNION
SELECT MONTHNAME(terms) AS month, COUNT(DISTINCT project_num) as total FROM archive WHERE terms >= '2017/01/01' AND Building_designer='SOMEPERSON' GROUP BY MONTH(terms))
GROUP BY month

How can i get count of customers per day by unique and repeat customer for specific date?

I am trying to get a result from my order table to get list of counts of customers who 1st time ordered and repeat orders. Something like below.
Date 1st time time repeat order
2014-09-01 43 90
2014-09-02 3 45
2014-09-03 12 30
2014-09-04 32 0
2014-09-05 1 98
I am beginner in sql and i ma using mysql.
My table structure is like.
OrderNumber int
OrderDate datetime
CustomerID int
I have tried this query in mysql but it only gives me first timed ordered count.
SELECT DATE(OrderDate), COUNT(*)
FROM orders T JOIN (
SELECT MIN(OrderDate) as minDate, CustomerID
FROM orders
GROUP BY CustomerID) T2 ON T.OrderDate = T2.minDate AnD T.CustomerID = T2.CustomerID
GROUP BY DATE(T.OrderDate)
You can get the total orders per day by grouping on OrderDate:
SELECT OrderDate, COUNT(OrderNumber) AS total FROM orders GROUP BY OrderDate
And you can get the no. of first orders per day from the following query :
SELECT OrderDate, COUNT(q1.CustomerID) AS first FROM (SELECT CustomerID, min(OrderDate) AS OrderDate FROM orders GROUP BY CustomerID)q1 GROUP BY q1.OrderDate
Now join these two on OrderDate to get the distribution of first and repeated orders :
SELECT a.OrderDate, a.first, (b.total - a.first) AS repeated FROM
(SELECT OrderDate, COUNT(q1.CustomerID) AS first FROM (SELECT CustomerID, min(OrderDate) AS OrderDate FROM orders GROUP BY CustomerID)q1 GROUP BY q1.OrderDate)a
JOIN
(SELECT OrderDate, COUNT(OrderNumber) AS total FROM orders GROUP BY OrderDate)b
on(a.OrderDate = b.OrderDate)
A slightly complicated query but this should do:
First Time Users: Just Group by customerID to get the min orderdate and then group by on that date to get the number of new users on a particular day. Query would look like this:
select date(mdate) as day, COUNT(*) from (select customerid, min(orderdate) as mDate from orders GROUP BY CustomerID)q1 GROUP BY day;
Repeat Users: First filter out all such orderno which were placed as first orders and then do a group by orderdate to get repeat. Query would be :
select date(orderdate) day, COUNT(*) from (select * from orders where orderno not in (select orders.orderno from orders JOIN (select customerid, min(orderdate) as mdate from orders GROUP BY CustomerID)as order2 ON (orders.customerid = order2.customerid) and (orders.orderdate = order2.mdate))) as q1 GROUP BY day;
You can do a join on day for both these queries to get combined results in a way you mentioned. Let me know if doesn't work
EDIT:
This would be the complete query: Here I am doing a UNION on both left and right outer joins since it might happen that you come across where there are no new requests or no repeated requests. This would take care of both the scenarios.
select q2.*, q3.repeated from (select date(mdate) as day, COUNT(*) as first from (select customerid, min(orderdate) as mDate from orders GROUP BY CustomerID)q1 GROUP BY day) as q2 LEFT OUTER JOIN (select date(orderdate) day, COUNT(*) as repeated from (select * from orders where orderno not in (select orders.orderno from orders JOIN (select customerid, min(orderdate) as mdate from orders GROUP BY CustomerID)as order2 ON (orders.customerid = order2.customerid) and (orders.orderdate = order2.mdate))) as q1 GROUP BY day) as q3 on q2.day = q3.day UNION select q2.*, q3.repeated from (select date(mdate) as day, COUNT(*) as first from (select customerid, min(orderdate) as mDate from orders GROUP BY CustomerID)q1 GROUP BY day) as q2 RIGHT OUTER JOIN (select date(orderdate) day, COUNT(*) as repeated from (select * from orders where orderno not in (select orders.orderno from orders JOIN (select customerid, min(orderdate) as mdate from orders GROUP BY CustomerID)as order2 ON (orders.customerid = order2.customerid) and (orders.orderdate = order2.mdate))) as q1 GROUP BY day) as q3 on q2.day = q3.day
this is my answer but not sure is still can improve.
SELECT userID, COUNT(*) AS repeat_order_cnt FROM
(SELECT DATE(OrderDate) AS order_DT, userID, COUNT(*) AS no_of_order FROM order
AND YEAR(orderDate) = '2015'
AND MONTH(orderDate) = '01'
GROUP BY order_DT,userID) AS order2
GROUP BY userID
HAVING COUNT(*) > 1

Sort data from MYSQL database by hour

I have users in db that I want to sort by hour and display count of users registered at that hour.
select
date_format(create_time, '%Y-%m-%d %h%p') as date,
count(id) as 'Number of registrations'
from users
group by 1
order by 1 desc
;
The above code will work; however, what I am trying to do is display 0's for the hours that have no user registrations. For example, if there were no registrations at 5pm, this will skip row for 5pm, which is logical. Is there a way to achieve what I am trying?
You could use a query like this:
select
date_format(t.d + INTERVAL t.h HOUR, '%Y-%m-%d %h%p') as date,
count(id) as 'Number of registrations'
from (
SELECT *
FROM
(SELECT DISTINCT DATE(create_time) d FROM users) dates,
(SELECT 0 h UNION ALL SELECT 1 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 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15
UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18 UNION ALL SELECT 19
UNION ALL SELECT 20 UNION ALL SELECT 21 UNION ALL SELECT 22 UNION ALL SELECT 23) hours
) t LEFT JOIN users
ON DATE(users.create_time)=t.d AND HOUR(users.create_time)=t.h
group by t.d, t.h
order by t.d, t.h
Please see fiddle here.
You need to generate all possible day and hour combinations.
Assuming that you have at least one record on each day and one record for each hour, you can do:
select concat(d.theday, ' ', h.thehour) as date,
count(id) as 'Number of registrations'
from (select distinct date_format(create_time, '%Y-%m-%d') as theday from users
) d cross join
(select distinct date_format(create_time, '%h%p') as thehour from users
) h left outer join
users u
on date_format(u.create_time, '%Y-%m-%d %h%p) = concat(d.theday, ' ', h.thehour)
group by concat(d.theday, ' ', h.thehour)
order by 1 desc;

specific status on consecutive days

I have a MySQL table ATT which has EMP_ID,ATT_DATE,ATT_STATUS with ATT_STATUS with different values 1-Present,2-Absent,3-Weekly-off. I want to find out those EMP_ID's which have status 2 consecutively for 10 days in a given date range.
Please help
Please have a try with this:
SELECT EMP_ID FROM (
SELECT
IF((#prevDate!=(q.ATT_DATE - INTERVAL 1 DAY)) OR (#prevEmp!=q.EMP_ID) OR (q.ATT_STATUS != 2), #rownum:=#rownum+1, #rownum:=#rownum) AS rownumber, #prevDate:=q.ATT_DATE, #prevEmp:=q.EMP_ID, q.*
FROM (
SELECT
EMP_ID
, ATT_DATE
, ATT_STATUS
FROM
org_tb_dailyattendance, (SELECT #rownum:=0, #prevDate:='', #prevEmp:=0) vars
WHERE ATT_DATE BETWEEN '2013-01-01' AND '2013-02-15'
ORDER BY EMP_ID, ATT_DATE, ATT_STATUS
) q
) sq
GROUP BY EMP_ID, rownumber
HAVING COUNT(*) >= 10
The logic is, to first sort the table by employee id and the dates. Then introduce a rownumber which increases only if
the days are not consecutive or
the employee id is not the previous one or
the status is not 2
Then I just grouped by this rownumber and counted if there are 10 rows in each group. That should be the ones who were absent for 10 days or more.
Have you tried something like this
SELECT EMP_ID count(*) as consecutive_count min(ATT_DATE)
FROM (SELECT * FROM ATT ORDER BY EMP_ID)
GROUP BY EMP_ID, ATT_DATE
WHERE ATT_STATUS = 2
HAVING consecutive_count > 10