Correct join syntax within multiple queries and sub queries - mysql

I have two queries that end up having the same format. Each has a Month, a year, and some relevant data per month/year. The schema looks like this:
subs Month Year
8150 1 2015
11060 1 2016
5 2 2014
6962 2 2015
8736 2 2016
Cans months years
2984 1 2015
2724 1 2016
13 2 2014
2563 2 2015
1901 2 2016
The first query syntax looks like this:
SELECT
COUNT(personID) AS subs_per_month,
MONTH(Date_1) AS month_1,
YEAR(Date_1) AS year_1
FROM
(SELECT
personID, MIN(date) AS Date_1
FROM
orders
WHERE
isSubscription = 1
GROUP BY personID
ORDER BY Date_1) AS my_sub_q
GROUP BY month_1 , year_1
The second query:
SELECT
COUNT(ID), MONTH(date) AS months, YEAR(date) AS years
FROM
orders
WHERE
status = 4 AND isSubscription = 1
GROUP BY months , years
ORDER BY months, years
The end goal is to write a simple join so that the final dataset looks like this:
subs cans months years
8150 2984 1 2015
11060 2724 1 2016
5 13 2 2014
6962 2563 2 2015
8736 1901 2 2016
I'm a little overwhelmed with how to do this correctly, and after a lot of trial and all error, I thought I'd ask for help. What's confusing is where the JOIN goes, and how that looks relative to the rest of the syntax.

Without giving consideration to simplifying your queries you can use your two queries as inline views and simply select from both (I aliased Q1 and Q2 for your queries and named fields the same within each for simplicity.
Select Q1.cnt as Subs, Q2.cnt as Cans, Q1.months, Q1.years
from (SELECT
COUNT(personID) AS Cnt,
MONTH(Date_1) as Months,
YEAR(Date_1) AS years
FROM (SELECT personID, MIN(date) AS Date_1
FROM orders
WHERE isSubscription = 1
GROUP BY personID) AS my_sub_q
GROUP BY month_1 , year_1) Q1
INNER JOIN (SELECT COUNT(ID) cnt, MONTH(date) AS months, YEAR(date) AS years
FROM orders
WHERE status = 4
AND isSubscription = 1
GROUP BY months, years) Q2
ON Q1.Months = Q2.Months
and Q1.Years = Q2.years
Order by Q1.years, Q2.months

Temporary table approach:
create temporary table first_query
<<your first query here>>;
create temporary table second_query
<<your second query here>>;
select fq.subs, sq.cans, fq.months, fq.years
from first_query fq
join second_query sq using (months, years)
Your table preview and query columns do not match for first query, so I assumed both tables have columns - months and years.
One messy query approach:
SELECT fq.subs_per_month subs, sq.cans, sq.months, sq.years
FROM
(SELECT
COUNT(personID) AS subs_per_month,
MONTH(Date_1) AS month_1,
YEAR(Date_1) AS year_1
FROM
(SELECT
personID, MIN(date) AS Date_1
FROM
orders
WHERE
isSubscription = 1
GROUP BY personID
ORDER BY Date_1) AS my_sub_q
GROUP BY month_1 , year_1) fq
JOIN
(SELECT
COUNT(ID) cans, MONTH(date) AS months, YEAR(date) AS years -- I added 'cans'
FROM
orders
WHERE
status = 4 AND isSubscription = 1
GROUP BY months , years
ORDER BY months, years) sq
ON fq.month_1 = sq.months AND fq.year_1 = sq.years

Please use following query
select t1.subs as subs,t2.Cans as cans,t1.months,t1.year as years from table1 t1 inner join
table2 t2 on t1.month=t2.months and t1.year=t2.years

Related

SQL COUNT with conditions

I have this table that lists which months each product is available on the market. For example product 1 is available from Mar to Dec and product 2 is available from Jan to Feb.
product_id
start_month
end_month
1
3
12
2
1
2
3
4
6
4
4
8
5
5
5
6
10
11
I need to count how many product_ids each month of the year has but can't think of how to put: WHERE month >= start_month AND month >= end_month. Can I use a loop for this or would that be overkill>
I used dbFiddle to test out this solution.
It's dependent on there being at least 1 product available for sale in each month. Although, maybe it's better that a month isn't returned when there isn't a product for sale?
Could use #derviş-kayımbaşıoğlu approach to generating the months, but not group on product_id, but on month instead.
with months as (
Select distinct start_month [month]
from Product
)
Select m.month
,count(*) [products]
from months m
left join Product p
on m.month >= p.start_month and m.month <= p.end_month
group by m.month
something like this needs to help but you may have syntax error since we don't know exact DBMS and version
select product_id, count(*) cnts
from table1
inner join (
select 1 month union
select 2 union
select 3 union
select 4 union
select 5 union
select 6 union
select 7 union
select 8 union
select 9 union
select 10 union
select 11 union
select 12 union
) t2
on t2.month between table1.start_month and table1.end_month
group by product_id

SQL finding max value of column where another column has got maximum but repetable value

I have following table:
id year month
1 2019 9
2 2019 10
3 2019 11
4 2019 12
5 2020 1
6 2020 2
7 2020 3
8 2020 4
I need to select max value of column month but only from where year has got max value.
In that case i need to select row
id max_year max_month
8 2020 4
I tried to make it with this
SELECT m.id, m.max_year, MAX(m.month) AS max_month FROM (SELECT id, month, MAX(year) AS max_year FROM tbl_months GROUP BY id) AS m GROUP BY m.id
Unnfortunately I get
id max_year max_month
5 2020 1
6 2020 2
7 2020 3
8 2020 4
Any clues why?
Is there another way to make it simpler and cleaner?
Thanks.
Use order by and limit;
select t.*
from t
order by year desc, month desc
limit 1;
with cte as
(
select * from temp where year = ( select max(year) from temp)
),cte2 as
(
select * from temp where month = ( select max(month) from temp)
)
select * from cte2
As another option
select t1.*
from t t1
where m = (select max(t2.m) from t t2 where y = (select max(t3.y) from t t3))
This way called subquery. I am not sure if this is faster vs order by/limit option, but might be depend on index.

Mysql: query missing rows between min and max of a field

I am working with a parts / motorcycle fitment Mysql database where all parts are linked to all motorcycles they can be installed on. It looks like this:
part_number motorcycle year
1000 HONDA_CBR1000 2008
1000 HONDA_CBR1000 2009
1000 HONDA_CBR1000 2010
1000 HONDA_CBR1000 2011
1000 HONDA_CBR1000 2012
1000 HONDA_CBR1000 2013
1001 HONDA_CBR600 2008
1001 HONDA_CBR600 2009
1001 HONDA_CBR1000 2008
1001 HONDA_CBR1000 2009
1001 HONDA_CBR1000 2013
So it means that:
part #1000 can be installed on the Honda CBR1000 from 2008 to 2013
part #1001 can be installed on the Honda CBR600 from 2008 to 2009 AND on the Honda CBR1000 from 2008 to 2013.
Unfortunately, the table (which has ~650,000 rows) was not always filled correctly. In this example, you will notice the following lines are missing:
part_number motorcycle year
1001 HONDA_CBR1000 2010
1001 HONDA_CBR1000 2011
1001 HONDA_CBR1000 2012
because the part #1001 which can be installed on the HONDA_CBR1000 from 2008, 2009 and 2013 can also be installed in the "forgotten" years in between (2010, 2011 and 2012).
So the simple query:
SELECT * FROM mytable WHERE motorcycle = 'HONDA_CBR1000' AND year = '2011'
would only retrieve the row for part #1000 (while in reality, part #1001 is also installable on this bike).
in plain English, I guess a query like
SELECT * FROM mytable WHERE motorcycle = 'HONDA_CBR1000'
AND ("minimum year of part_number applicable to HONDA_CBR1000" <= '2011')
AND ("maximum year of part_number applicable to HONDA_CBR1000" >= '2011')
would retrieve all results (1000 and 1001).
But how can I ask that in SQL? Do you think it would too slow?
Thanks for any help!
SELECT part_number, max(year), Min(year)
FROM mytable
WHERE motorcycle = 'HONDA_CBR1000'
Group By part_number
Having Min(year) <= 2011
And max(year) >= 2011
*********************Edit****************
To improve performance, Lets try this,
1)
SELECT part_number
FROM mytable t,
(Select part_number, Min(year) Minyear, max(year) Maxyear
FROM mytable
Group BY part_number) t1
WHERE t.motorcycle = 'HONDA_CBR1000'
AND t.year Between MinYear and Maxyear
AND t.year = '2011'
*********************EDIT 2**********************************
So This is the query that will list out the years that are missed out. You can put the entire query in to a insert statement
SELECT partsnumber , yrs.allyears
FROM (Select max(year) maxyear, min(year) minyear, partsnumber
FROM yourtable
group by partsnumber) q1
(Select 1950+1+b+a*10 as allyears
from (select 0 as a union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) a,
(select 0 as b union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) b) y
Where yrs.allyears between maxyear and minyear
MINUS
SELECT partsnumber , yrs.allyears
From yourtable
yrs --> Subquery that generates years from 1950 to 2050 (If you have more years ( beyond 2050 or before 1950 ) then this has to be changed)
Am selecting the years between the min and max years for each productnumber. then with yrs table as reference am finding the years between min and max years.
The result from above query will give all years between min and max. The minus will give the years that are missed
Here is my approach for getting all combinations of parts and motorcycles and the years they have no data.
Generate all the rows for all the years, then filter out the ones you have. The first part uses cross join. The second left join:
select pm.part_number, pm.motorcycle, y.year
from (select part_number, motorcycle, min(year) as miny, max(year) as maxy
from mytable
group by part_number, motorcycle
) pm cross join
(select distinct year
from mytable
) y
on y.year between pm.miny and pm.maxy left join
mytable t
on t.part_number = pm.part_number and t.motorcycle = pm.motorcycle and
t.year = y.year
where y.year is null;
This assumes that all years are in your table, somewhere. The y table is just a list of years, so you can get it from another table or by creating a derived table. The subquery is just a convenient way to get it.

I need to join two table in mysql

I need some help to solve an issue with my query. I want to join the output of two select statements:
1st
select extract(year from createdDate) as year,
count(extract(year from createdDate)) as count
from table
where to_user_id= 322
group by extract(year from createdDate);
and its output
Year Count
2014 18
2015 117
2016 9
and 2nd query
select count(extract(year from createdDate)) as count
from table
where userId=322
group by extract(year from createdDate);
and its output
Count
18
110
11
I want to add this two tables into one table.
I want that type of output,
Year Count Count
2014 18 18
2015 117 110
2016 9 11
Note that I use to_user_id in query 1 but userId in query 2.
I tried to solved out this thing but I got repeated values in the output.
Anyone know the solution?
Write them as subqueries and join them together.
SELECT a.year, a.count AS t_user_count, b.count AS user_count
FROM (select YEAR(create_date) AS year, COUNT(*) AS count
FROM table
WHERE to_user_id = 322
GROUP BY year) AS a
JOIN (SELECT YEAR(create_date) AS year, COUNT(*) AS count
FROM table
WHERE user_id = 322
GROUP BY year) AS b
ON a.year = b.year

Get product total sales per moth, with 0 in the gaps

I have been stuck in a recent problem with a SQL Query. What I'm trying to archieve is to get each product in the store and show how many of them has been sold each month. However, sometimes there are some months where these products were not sold, which means they won't be displayed.
For instance, this is the result I'm getting right now
Article Month Sold
CN140027 6 312
CN140027 7 293
CN140027 12 122
CN140186 1 10
CN140186 4 2
While I want to get something more like this
Article Month Sold
CN140027 6 312
CN140027 7 293
CN140027 8 0
CN140027 9 0
CN140027 10 0
CN140027 11 0
CN140027 12 122
CN140186 1 10
CN140186 2 0
CN140186 3 0
CN140186 4 2
And here is the query I'm using at the moment
SELECT k.artikelnr, Months.datefield as `Months`, IFNULL(SUM(k.menge),0) as `Quantity`
FROM store_shop_korb as k LEFT OUTER JOIN office_calendar AS Months
ON Months.datefield = month(k.date_insert)
WHERE k.date_insert BETWEEN "2014-12-01" AND "2015-12-31"
group by k.artikelnr, Months.datefield
What am I missing? Or what am I doing wrong? Any help is really appreciated.
Thanks in advance.
EDIT:
Additional information:
office_calendar is the calendar table. It only contains the months as registry, from 1 to 12.
Additionally, I'm taking the article/product ID from a table called 'store_shop_korb', which contains all the lines of a made order (so it contains the article ID, its price, the quantity for each order..)
This works for me:
SELECT k.artikelnr, c.datefield AS `Month`, COALESCE(s.Quantity, 0) AS Sold
FROM (
SELECT artikelnr
FROM store_shop_korb
GROUP BY artikelnr
) k
JOIN office_calendar c
LEFT JOIN (
SELECT artikelnr, MONTH(date_insert) AS monthfield, SUM(menge) AS Quantity
FROM store_shop_korb
GROUP BY artikelnr, MONTH(date_insert)
) s ON k.artikelnr = s.artikelnr AND c.datefield = s.monthfield
ORDER BY k.artikelnr, c.datefield
If you have a table of articles, you can use it in the place of subquery k. I'm basically normalizing on the fly.
Explanation:
There's basically 3 sets of data that get joined. The first is a distinct set of articles (k), the second is a distinct set of months (c). These two are joined without restriction, meaning you get the cartesian product (every article x every month). This result is then left-joined to the sales per month (s) so that we don't lose 0 entries.
Add another where condition , i think it will solve your problem
SELECT k.artikelnr, Months.datefield as `Months`, IFNULL(SUM(k.menge),0) as `Quantity`
FROM store_shop_korb as k LEFT OUTER JOIN office_calendar AS Months
ON Months.datefield = month(k.date_insert)
WHERE IFNULL(SUM(k.menge),0)>0 AND k.date_insert BETWEEN "2014-12-01" AND "2015-12-31"
group by k.artikelnr, Months.datefield
I have tried this in MSAccess and it seems to work OK
SELECT PRODUCT, CALENDAR.MONTH, A
FROM CALENDAR LEFT JOIN (
SELECT PRODUCT, MONTH(SALEDTE) AS M, SUM(SALEAMOUNT) AS A
FROM SALES
WHERE SALEDTE BETWEEN #1/1/2015# AND #12/31/2015#
GROUP BY PRODUCT, MONTH(SALEDTE) ) AS X
ON X.M = CALENDAR.MONTH
If you already have a calender table then use this.
SELECT B.Article,
A.Month,
COALESCE(c.Sold, 0)
FROM (SELECT DISTINCT Months.datefield --Considering this as months feild
FROM office_calendar AS Months) A
CROSS JOIN (SELECT DISTINCT article
FROM Yourtable) B
LEFT OUTER JOIN Yourtable C
ON a.month = c.Month
AND b.Article = c.Article
Else you need a months table. Try this.
SELECT *
FROM (SELECT 1 AS month UNION
SELECT 2 UNION
SELECT 3 UNION
SELECT 4 UNION
SELECT 5 UNION
SELECT 6 UNION
SELECT 7 UNION
SELECT 8 UNION
SELECT 9 UNION
SELECT 10 UNION
SELECT 11 UNION
SELECT 12) A
CROSS JOIN (SELECT DISTINCT article
FROM Yourtable) B
LEFT OUTER JOIN Yourtable C
ON a.month = c.Month
AND b.Article = c.Article