MySQL - how to retrieve columns in same row as the values returned by min/mx - sql

I couldn't frame the question's title properly. Suppose a table of weekly movie earnings as below:
MovieName <Varchar(450)>
MovieGross <Decimal(18)>
WeekofYear <Integer>
Year <Integer>
So how do I get the names of top grossers for each week of this year, if I do:
select MovieName , Max(MovieGross) , WeekofYear
from earnings where year = 2010 group by WeekofYear;
Then obviously the query wont run, while
select Max(MovieName) , Max(MovieGross) , WeekofYear
from earnings where year = 2010 group by WeekofYear;
would just give movies starting with lowest alphabet. Is using group_concat() and then substring_index() the only option here?
select
substring_index(group_concat(MovieName order by MovieGross desc),',',1),
Max(MovieGross) , WeekofYear from earnings where year = 2010
group by WeekofYear ;
Seems clumsy. Is there any better way of achieving this?

It's the ever-recurring max-per-group problem. You solve it by selecting the defining properties of your group and then joining your "real" data against that.
select
e.MovieName,
e.MovieGross,
e.WeekofYear
from
earnings e
inner join (
select Max(MovieGross) MovieGross, Year, WeekofYear
from earnings
group by Year, WeekofYear
) max on max.Year = e.Year
and max.WeekofYear = e.WeekofYear
and max.MovieGross = e.MovieGross
where
e.year = 2010
The defining properties of your group are Year, WeekofYear and MAX(MovieGross). There will be one row with different values for each group range.
An INNER JOIN against your data table elimitates all rows that do not fulfill the defining properties of your group. This also means that it lets through all rows that do - you could end up with two movies that made the same amount of money in any particular week. Group the "outer" query again to eliminate the duplicate rows in favor of a single movie.

You need to determine the max weekly gross and then select the movie name based on that criterion. Something like this:
SELECT e.MovieName, m.Gross, m.WeekofYear
FROM earnings e JOIN
(SELECT MAX(MovieGross) Gross, WeekofYear
FROM earnings WHERE `year` = 2010 GROUP BY WeekofYear) m
ON e.MovieGross=m.Gross AND e.WeekofYear=m.WeekofYear

This is pretty fast query, that does the job:
SELECT e.WeekofYear as WeekofYear
, max(MovieGross) as MovieGross
, (SELECT MovieName FROM earnings
WHERE WeekofYear=e.WeekofYear ORDER BY MovieGross DESC LIMIT 1
) as MovieName
FROM earnings AS e
WHERE year='2010'
GROUP BY WeekofYear
ORDER BY WeekofYear;
Happy to help you :)
P.S. and thanks for ratings ;)

Ok, trying again with the having clause, I cannot help myself.
Hopefully this will help you get started. First, create a list of the weeks of the year, then for the inner query, find the one that has the max for that week.
Select MovieName, max(MovieGross) as max_gross, WeekofYear
from earnings
where year = 2010
order by MovieGross desc
Having MovieGross=max_gross
group by WeekofYear
This should return the top grossing movie for each week. This should also return multiple entries for a week in the event of a tie.

Related

Most rented movie by title PostgreSQL sample database

I am working on getting the most popular movies rented per month by title. So far I have been able to get the titles of the movies and the dates they were rented but the count column and date column are giving individual results. Here is the query I am using. Any help would be much appreciated.
SELECT x.rental_date, x.title, x.count FROM(
SELECT ren.rental_date,fil.title,COUNT(ren.rental_id)
FROM rental AS ren
JOIN inventory AS inv ON ren.inventory_id = inv.inventory_id
JOIN film AS fil ON inv.film_id = fil.film_id
GROUP BY title, rental_date) AS x
ORDER BY x.count,x.rental_date;
Simply aggregate by the year/month of the specific dates using to_char. Also, subquery is not necessary.
SELECT TO_CHAR(ren.rental_date, 'YYYY-MM') AS rental_month,
fil.title,
COUNT(ren.rental_id) AS rental_count
FROM rental AS ren
JOIN inventory AS inv
ON ren.inventory_id = inv.inventory_id
JOIN film AS fil
ON inv.film_id = fil.film_id
GROUP BY TO_CHAR(ren.rental_date, 'YYYY-MM'),
fil.title
ORDER BY rental_month,
rental_count DESC
Consider also date_part for year and month extraction or date_trunc to normalize dates to first day of month to keep the timestamp type:
SELECT DATE_PART('year', ren.rental_date) AS rental_year,
DATE_PART('month', ren.rental_date) AS rental_month,
...
SELECT DATE_TRUNC('month', ren.rental_date) AS rental_month,
...

Is there a way to count how many strings in a specific column are seen for the 1st time?

**Is there a way to count how many strings in a specific column are seen for
Since the value in the column 2 gets repeated sometimes due to the fact that some clients make several transactions in different times (the client can make a transaction in the 1st month then later in the next year).
Is there a way for me to count how many IDs are completely new per month through a group by (never seen before)?
Please let me know if you need more context.
Thanks!
A simple way is two levels of aggregation. The inner level gets the first date for each customer. The outer summarizes by year and month:
select year(min_date), month(min_date), count(*) as num_firsts
from (select customerid, min(date) as min_date
from t
group by customerid
) c
group by year(min_date), month(min_date)
order by year(min_date), month(min_date);
Note that date/time functions depends on the database you are using, so the syntax for getting the year/month from the date may differ in your database.
You can do the following which will assign a rank to each of the transactions which are unique for that particular customer_id (rank 1 therefore will mean that it is the first order for that customer_id)
The above is included in an inline view and the inline view is then queried to give you the month and the count of the customer id for that month ONLY if their rank = 1.
I have tested on Oracle and works as expected.
SELECT DISTINCT
EXTRACT(MONTH FROM date_of_transaction) AS month,
COUNT(customer_id)
FROM
(
SELECT
date_of_transaction,
customer_id,
RANK() OVER(PARTITION BY customer_id
ORDER BY
date_of_transaction ASC
) AS rank
FROM
table_1
)
WHERE
rank = 1
GROUP BY
EXTRACT(MONTH FROM date_of_transaction)
ORDER BY
EXTRACT(MONTH FROM date_of_transaction) ASC;
Firstly you should generate associate every ID with year and month which are completely new then count, while grouping by year and month:
SELECT count(*) as new_customers, extract(year from t1.date) as year,
extract(month from t1.date) as month FROM table t1
WHERE not exists (SELECT 1 FROM table t2 WHERE t1.id==t2.id AND t2.date<t1.date)
GROUP BY year, month;
Your results will contain, new customer count, year and month

SQLite - Use a CTE to divide a query

quick question for those SQL experts out there. I feel a bit stupid because I have the feeling I am close to reaching the solution but have not been able to do so.
If I have these two tables, how can I use the former one to divide a column of the second one?
WITH month_usage AS
(SELECT strftime('%m', starttime) AS month, SUM(slots) AS total
FROM Bookings
GROUP BY month)
SELECT strftime('%m', b.starttime) AS month, f.name, SUM(slots) AS usage
FROM Bookings as b
LEFT JOIN Facilities as f
ON b.facid = f.facid
GROUP BY name, month
ORDER BY month
The first one computes the total for each month
The second one is the one I want to divide the usage column by the total of each month to get the percentage
When I JOIN both tables using month as an id it messes up the content, any suggestion?
I want to divide the usage column by the total of each month to get the percentage
Just use window functions:
SELECT
strftime('%m', b.starttime) AS month,
f.name,
SUM(slots) AS usage
1.0 * SUM(slots) AS usage
/ SUM(SUM(slots)) OVER(PARTITION BY strftime('%m', b.starttime)) ratio
FROM Bookings as b
LEFT JOIN Facilities as f
ON b.facid = f.facid
GROUP BY name, month
ORDER BY month

How to show all holiday types with the code

For my homework, I have to write sql code to show "Among all the orders in 2015, calculate the number of days for each holidaytype, and the average sales per day for each holidaytype. Exclude holidaytype=NULL. Sort the results by the average sales per day from high to low."
This is the code that I have been trying to use
select distinct holidaytype, sum(AvgSales) as AvgSales, sum(NumDays) as NumDays
from(
Select min(holidaytype) as holidaytype, count(order_date) as cnt, count(numholidays) as NumDays, avg(o.sales*o.quantity) as AvgSales
from orderline o, orders1 o1, calendar c
where o.Order_ID=o1.Order_ID and datepart(yyyy,order_date)= 2015 and holidaytype is not null) J
group by holidaytype
go
In the output, only 1 holiday type is showing but I am supposed to have 6 or 7 different holiday types.
You are grouping by Holiday Type, so it doesn't make sense to aggregate it, which isn't what you want anyway, according to your question.
So instead of this:
Select min(holidaytype) as holidaytype,
You should do this:
Select holidaytype,

Average Group size per month Over previous ten years

I need to find the average size (average number of employees) of all the groups (employers) that we do business with per month for the last ten years.
So I have no problem getting the average group size for each month. For the Current month I can use the following:
Select count(*)
from Employees EE
join Employers ER on EE.employerid = ER.employerid
group by ER.EmployerName
This will give me a list of how many employees are in each group. I can then copy and paste the column into excel get the average for the current month.
For the previous month, I want exclude any employees that were added after that month. I have a query for this too:
Select count(*)
from Employees EE
join Employers ER on EE.employerid = ER.employerid
where EE.dateadded <= DATEADD(month, -1,GETDATE())
group by ER.EmployerName
That will exclude all employees that were added this month. I can continue to this all the way back ten years, but I know there is a better way to do this. I have no problem running this query 120 times, copying and pasting the results into excel to compute the average. However, I'd rather learn a more efficient way to do this.
Another Question, I can't do the following, anyone know a way around it:
Select avg(count(*))
Thanks in advance guys!!
Edit: Employees that have been terminated can be found like this. NULL are employees that are currently employed.
Select count(*)
from Employees EE
join Employers ER on EE.employerid = ER.employerid
join Gen_Info gne on gne.id = EE.newuserid
where EE.dateadded <= DATEADD(month, -1,GETDATE())
and (gne.TerminationDate is NULL OR gen.TerminationDate < DATEADD(day, -14,GETDATE())
group by ER.EmployerName
Are you after a query that shows the count by year and month they were added? if so this seems pretty straight forward.
this is using mySQL date functions Year & month.
Select AVG(cnt) FROM (
Select count(*) cnt, Year(dateAdded), Month(dateAdded)
from System_Users su
join system_Employers se on se.employerid = su.employerid
group by Year(dateAdded), Month(dateAdded)) B
The inner query counts and breaks out the counts by year and month We then wrap that in a query to show the avg.
--2nd attempt but I'm Brain FriDay'd out.
This uses a Common table Expression (CTE) to generate a set of data for the count by Year, Month of the employees, and then averages out by month.
if this isn't what your after, sample data w/ expected results would help better frame the question and I can making assumptions about what you need/want.
With CTE AS (
Select Year(dateAdded) YR , Month(DateAdded) MO, count(*) over (partition by Year(dateAdded), Month(dateAdded) order by DateAdded Asc) as RunningTotal
from System_Users su
join system_Employers se on se.employerid = su.employerid
Order by YR ASC, MO ASC)
Select avg(RunningTotal), mo from cte;