grouped sql query (mysql) - order by - mysql

Lets's say i have a table sign_ins which has data like so: (the real table has 3.5 million rows)
+-----------+---------+------------------+
| school_id | user_id | date(created_at) |
+-----------+---------+------------------+
| 1 | 4 | 2009-04-20 |
| 1 | 4 | 2009-04-21 |
| 1 | 4 | 2009-05-06 |
| 1 | 5 | 2009-04-20 |
| 1 | 5 | 2009-06-26 |
| 1 | 5 | 2009-06-26 |
| 2 | 6 | 2009-04-21 |
| 2 | 6 | 2009-06-26 |
| 2 | 7 | 2009-04-20 |
| 2 | 7 | 2009-04-20 |
+-----------+---------+------------------+
created_at is a datetime field but i'm calling date() on it to get the day.
I have the concept of a "login_days" which is the number of distinct days on which a given user has a sign_in record. I want to order the schools by the number of login days, highest first, and return the number of login days.
So, looking at the data above, school 1 has two users (4 & 5). User 4 has three sign_ins, on 3 distinct days, so 3 "login_days". User 5 has three logins, but only 2 distinct days, so 2 "login_days". Therefore school 1 has 5 login days.
Looking at school 2, it has 3 login days: 2 from user 6 and 1 from user 7.
So, i would want to get this back from the query:
+-----------+------------+
| school_id | login_days |
+-----------+------------+
| 1 | 5 |
| 2 | 4 |
+-----------+------------+
I can't quite figure out how to do the query. I started off with this (i have the id < 11 part in there just to get my example data instead of my entire table of 3.5 million rows):
mysql> select school_id from sign_ins where id < 11 group by school_id, user_id, date(created_at);
+-----------+
| school_id |
+-----------+
| 1 |
| 1 |
| 1 |
| 1 |
| 1 |
| 2 |
| 2 |
| 2 |
+-----------+
8 rows in set (0.00 sec)
I can see in here that there are 5 rows for school 1 and 3 for school 2, which looks like it's worked. But i need to group that further, and order by that grouped number, to get it like in my required results. It must be something simple, can someone show me what i'm missing?
thanks, Max

MySQL allows you to count the number of distinct values for multiple expressions. So, this is basically an aggregation query with the appropriate count:
select school_id, count(distinct user_id, date(created_at)) as NumLoginDays
from sign_ins
group by school_id;

Related

What would be the best way to filter grouped table by its max values?

I have this following table, which tells me how many rentals a certain film had in a certain month. Here's the top 10 rows:
| month | title | rentals |
+-------+-------------------+---------+
| 2 | ACE GOLDFINGER | 1 |
| 2 | AFFAIR PREJUDICE | 1 |
| 2 | AFRICAN EGG | 1 |
| 2 | ALI FOREVER | 1 |
| 2 | ALONE TRIP | 1 |
| 2 | AMADEUS HOLY | 1 |
| 2 | AMERICAN CIRCUS | 1 |
| 2 | AMISTAD MIDSUMMER | 1 |
| 2 | ARMAGEDDON LOST | 1 |
| 2 | BAKED CLEOPATRA | 1 |
+-------+-------------------+---------+
My main objective here is to create a new table where, for each month, it gives me the title of the filme with the most rentals in that month.
So far, I've tried using a combination of group by queries, but it didn't gave much result. Despite that, I achieved to create a new table that gives me the number of rentals the top movie (or movies) had in each month. Here it is:
CREATE VIEW temp AS (SELECT month, MAX(rentals) rentals FROM film_per_month GROUP BY 1);
mysql> SELECT * FROM temp;
+-------+---------+
| month | rentals |
+-------+---------+
| 2 | 2 |
| 5 | 5 |
| 6 | 7 |
| 7 | 16 |
| 8 | 13 |
+-------+---------+
5 rows in set (0.05 sec)
The obstacle here is that I can't extract it to show the titles of the movies that were rented that maximum amount of times.
I've tried to amend that using inner join, self-joins, but I just messed it up.
So my question is: What would be the better way to create a new table where, for each month, it gives me the title of the filme with the most rentals in that month?
I think you can try to use EXISTS subquery to get Max rentals for each month rows.
SELECT t1.*
FROM film_per_month t1
WHERE EXISTS (
SELECT 1
FROM film_per_month tt
WHERE tt.month = t1.month
GROUP BY tt.month
HAVING MAX(tt.rentals) = t1.rentals
)

Get rows with condition on last grouping row

I have table favourite_products with schema like below. I need to count how many people (account_id) like product with id = 12. But the condition is that person marked product as liked on last time.
In this example user with id = 1 marked product 12 as positive for the first time, but then he marked is a non-positive so this value shouldn't be returned. The following example should return 2 rows (for user_id = 5 and user_id = 8). I heard about window function but have mysql in version 5.7 and I can't upgrade it. Do you have some ideas how to write this query?
| id | user_id | product_id | positive |
| 1 | 1 | 12 | 1 |
| 2 | 1 | 12 | 0 |
| 3 | 1 | 15 | 1 |
| 4 | 5 | 12 | 1 |
| 5 | 5 | 12 | 1 |
| 6 | 11 | 18 | 1 |
| 7 | 8 | 12 | 1 |
| 8 | 8 | 12 | 1 |
Following approach should work for all the cases, including the case when a product was disliked and then liked again at the end.
In a Derived table, we can get the maximum id value for every user_id and product_id = 12. This result-set will be joined to the main table appropriately. This will fetch us the complete row (recent activity done by user for a product).
Now, we can consider only those users where the last activity is positive.
Query
SELECT fp.user_id
FROM favourite_products AS fp
JOIN (SELECT user_id,
Max(id) AS max_id
FROM favourite_products
WHERE product_id = 12
GROUP BY user_id) AS dt
ON dt.user_id = fp.user_id
AND dt.max_id = fp.id
AND fp.positive = 1;
Result
| user_id |
| ------- |
| 5 |
| 8 |
View on DB Fiddle

SELECT, how to combine some rows as one, show rows with specific column value first and exclude a row

I have two tables, units and power.
Take a look at:
The power table:
+----+-------+
| id | power |
+----+-------+
| 1 | 2 |
| 2 | 4 |
| 3 | 6 |
| 4 | 8 |
+----+-------+
The units table:
+----+---------+----------+--------+----------+---+---+
| id | user_id | power_id | amount | group_id | x | y |
+----+---------+----------+--------+----------+---+---+
| 1 | 10 | 3 | 1000 | 0 | 1 | 1 |
| 2 | 10 | 3 | 1000 | 0 | 1 | 1 |
| 3 | 10 | 3 | 1000 | 0 | 1 | 1 |
| 4 | 11 | 2 | 100 | 4 | 1 | 1 |
| 5 | 11 | 2 | 100 | 4 | 1 | 1 |
| 6 | 11 | 1 | 100 | 4 | 1 | 1 |
| 7 | 12 | 4 | 1000 | 0 | 1 | 1 |
+----+---------+----------+--------+----------+---+---+
I want to get result looking like this:
+----------+--------------+-------------+----------+---------+--+
| units.id | total_amount | total_power | group_id | user_id | |
+----------+--------------+-------------+----------+---------+--+
| 3 | 1000 | 6000 | 0 | 10 | |
| 2 | 1000 | 6000 | 0 | 10 | |
| 4 | 300 | 1000 | 4 | 11 | |
| 7 | 1000 | 8000 | 0 | 12 | |
+----------+--------------+-------------+----------+---------+--+
Explanation:
Exclude a specific id, should work same for both a single row and a row which is a sum of multiple rows in the table. As you see, in the result set, row with id 1 was excluded. The id is provided by the app, I think it's better to do it in MySQL than PHP, because MySQL could just discard this row (I think), but with PHP it would have to do if() checking for every loop iteration, which seems less efficient.
Show the summed-rows row before single rows, every time.
Show user's units before other users', every time. You can see that when rows with user_id of 10 (imagine this user is the one seeing the page) appear first in my result set.
Show units with highest power first.
Show units with highest amount first.
Show units with highest id first.
The last(4.5.6) are sorted in regards to the priority of the result set, with 4th having the most of it.
I have this query:
SELECT units.id,
units.amount*power.power AS total_power,
Sum(units.amount) AS total_amount
FROM units
JOIN power
ON units.power_id = power.id
WHERE x = ?
AND y = 1
GROUP BY group_id
ORDER BY group_id desc, total_power desc, total_amount desc, units.id desc limit ?,?
But it combines all units where group_id is 0, however I want units with group_id=0 to be separate rows in the result set. How to do that? Thanks!
Edit: To answer #Linoff's question about how I determine which id to exclude, I exclude the 1 in the example because a user always will see the result set through accessing it with a unit_id, which, again, in my example happens to be 1. I hope it is clear now.
Edit: The user can access this list of units on page "index.php?page=unit/view&id=. Then I SELECT the entered id separately for the purpose of my app, and then SELECT the list. But as I already have data for the entered id (for instance, 1 in this case) I do not need to have them when I SELECT from the units and power.
#Anonymous, to answer your question 1, answer 1 is: all rows with same group_id (except 0 which is a not-a-group marker) are grouped and combined together, so rows which are id 4,5 and 6 which have identical group_id are combined. My problem is, I don't know how to exclude grouping for rows which are stand alone (no group marking) and how to sort the result so the rows with specified user_id are sorted first and grouped rows (4,5,6-turned-to-4) are also sorted first, but in user_id=10-first,user_id=??-second hierarchy, if this makes sense.

how to check the record occurrence in mysql

kindly suggest me a sql query to sort this.
there is a non normalized table named test.there ar2 two fields on is primary key and it is auto incremented. other field 'name' and it is repetitive as follow.
so i just need to know what insert/update mysql query should i used to get below output in the 'occurrence' field.
eg:- in the 5th row name 'occurance' value is 3 because 'name'= "chanaka" has included 3 times totally in the table with including record 5.
Read up on mysql row number simulation
Here's an example
MariaDB [sandbox]> select id,company_id
-> , if(company_id <> #p ,#rn:=1, #rn:=#rn+1) occurance
-> , #p:=company_id
-> from medication, (select #rn:=0,#p:=0) rn
-> order by company_id, id;
+------+------------+-----------+----------------+
| id | company_id | occurance | #p:=company_id |
+------+------------+-----------+----------------+
| 1 | 1 | 1 | 1 |
| 2 | 1 | 2 | 1 |
| 3 | 1 | 3 | 1 |
| 4 | 2 | 1 | 2 |
| 5 | 2 | 2 | 2 |
| 6 | 2 | 3 | 2 |
| 8 | 2 | 4 | 2 |
| 7 | 3 | 1 | 3 |
+------+------------+-----------+----------------+
8 rows in set (0.00 sec)
You should be able to count "name" and that should get you the amount of
occurrences.
SELECT id, name, count(name)
FROM test
GROUP by name

MySQL query SELECT FROM 2 tables, COUNT the most used

I have this 2 tables and I need to return the moset used office. Note: 1 office can be used by more than 1 guys and the column ido from TableB is populate from TableA
Probaly is a query with group by and desc limit 1
TableA
| ido| office | guy |
---------------------
| 1 | office1| guy1|
| 2 | office2| guy2|
| 3 | office1| guy3|
| 4 | office1| guy4|
| 5 | office5| guy5|
| 6 | office2| guy6|
TableB
| idb| vizit | ido|
---------------------
| 1 | date | 4 |
| 2 | date | 2 |
| 3 | date | 5 |
| 4 | date | 6 |
| 5 | date | 1 |
| 6 | date | 6 |
Thanks!
You were correct in that GROUP BY, LIMIT and DESC are useful here; it leads to a fairly straight forward query;
SELECT TableA.office
FROM TableA
JOIN TableB
ON TableA.ido = TableB.ido
GROUP BY TableA.office
ORDER BY COUNT(*) DESC
LIMIT 1
What it does is basically create rows with all valid combinations, counting the number of generated rows per office. A plain descending sort by that count will give you the most frequently used office.
An SQLfiddle to test with.