mysql most popular articles in most popular categories [closed] - php

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I have 'articles table':
id,
category_id
categories table:
id
view counts table:
id,
article_id,
ip,
What i need is mysql query which will give 5 categories having biggest views count of articles total (most often read category). Additionaly in each category must be a list of articles having biggest view count (most often read 5 articles in category)
So query should return something like:
sport, article1
sport, article2
sport, article3
sport, article4
sport, article5
tv, article6
tv, article7
tv, article8
tv, article9
tv, article10
etc...
Additionaly it would be great to have how many times article and category was watched but this is not necessary.
I have tried count all with having but with no success.
Regards.

To do this in MySQL you have to mimic the row_number() over (partition by category) functionality that would otherwise be available in other databases.
I've tested out the query below using some sample data here:
Fidde:
http://sqlfiddle.com/#!9/2b8d9/1/0
Query:
select id, category_id
from(
select x.*,
#row_number:=case when #category_id=x.category_id then #row_number+1 else 1 end as row_number,
#category_id:=x.category_id as grp
from (select art.id, art.category_id, count(*) as num_art_views
from articles art
join (select art.category_id, count(*)
from view_counts cnt
join articles art
on cnt.article_id = art.id
group by art.category_id
order by 2 desc limit 5) topcats
on art.category_id = topcats.category_id
join view_counts cnt
on art.id = cnt.article_id
group by art.id, art.category_id
order by art.category_id, num_art_views desc) x
cross join (select #row_number := 0, #category_id := '') as r
) x where row_number <= 5
For some clarification, this will show the top 5 articles within the top 5 categories.
Using LIMIT was sufficient to get the top 5 categories, but to get the top 5 articles WITHIN each category, you have to mimic the PARTITION BY of other databases by using a variable that restarts at each change in category.
It might help to understand if you run the just the inner portion, see fiddle here:
http://sqlfiddle.com/#!9/2b8d9/2/0
The output at that point is:
| ID | CATEGORY_ID | NUM_ART_VIEWS | ROW_NUMBER | GRP |
|-----------|-------------|---------------|------------|--------|
| article16 | autos | 2 | 1 | autos |
| article14 | planes | 2 | 1 | planes |
| article12 | sport | 4 | 1 | sport |
| article3 | sport | 3 | 2 | sport |
| article4 | sport | 3 | 3 | sport |
| article1 | sport | 3 | 4 | sport |
| article2 | sport | 3 | 5 | sport |
| article5 | sport | 2 | 6 | sport |
| article15 | trains | 2 | 1 | trains |
| article13 | tv | 6 | 1 | tv |
| article9 | tv | 3 | 2 | tv |
| article6 | tv | 3 | 3 | tv |
| article7 | tv | 3 | 4 | tv |
| article8 | tv | 3 | 5 | tv |
| article10 | tv | 2 | 6 | tv |
You can easily exclude anything not <= 5 at that point (which is what the above query does).

Related

Finding inner join mysql with array [duplicate]

This question already has answers here:
How to join two tables using a comma-separated-list in the join field
(5 answers)
Closed 2 years ago.
I have two tables, First one is products where it has list of products with some specifications, in the other hand I have a table with clients and what type of product they want, they might want a product in any town of a list exactly as explained in the following tables,
Products Table like
| id | owner | userid | city | town | status | price |
| 1 | jon spee | 10 | 10 | 4 | 0 | 10500 |
| 2 | Hiss Roe | 10 | 7 | 9 | 0 | 20000 |
| 3 | John Smi | 10 | 10 | 12 | 0 | 10000 |
Clients Table like
| id | fullname | userid | city | towns | status | price |
| 1 | name 1 | 10 | 10 |4,8,6,2| 0 | 20000 |
| 2 | name 2 | 10 | 7 | 7,2,9 | 0 | 25000 |
| 3 | name 3 | 10 | 10 | 1 | 0 | 20000 |
MySQL Query :
SELECT *
FROM clients
INNER JOIN products
ON (
clients.userid = products.userid AND
clients.price >= products.price AND
clients.city = products.city AND
clients.status = products.status
I want it to check also in towns like for each town it executs this query (dynamically),
(products.town LIKE '%4%' OR products.town LIKE '%8%' OR products.town LIKE '%6%' OR products.town LIKE '%2%')
You could go with this query
SELECT *
FROM clients
INNER JOIN products
ON (
clients.userid = products.userid AND
clients.price >= products.price AND
clients.city = products.city AND
find_in_set(clients.town, products.town) AND
clients.status = products.status
you can also fetch it in php and create your statement based on the results fetched
Your primary effort should go into fixing your data model. Don't store multiple integer values in a string column. You should have a separate table to store the relation betwen clients and towns, which each tuple on a separate row.
That said: for your current design, you can join on find_in_set():
on
clients.userid = products.userid
and ...
and find_in_set(product.town, client.towns)

Sql query showing additional results

i have
SELECT s.*
FROM shop
WHERE s.family IN (SELECT s2.family FROM shop s2 WHERE (s2.money like "%k%"))
it does return "ticket" which contanis the k letter but in addition it returns all the other results that doesn't have "k" in it
here is the table of shop :
+----+--------+-------+
| id | family | money |
+----+--------+-------+
| 1 | 1 | card |
| 2 | 1 |Cheque |
| 3 | 2 |coins |
| 4 | 2 |ticket |
+----+--------+-------+
i am using
IN (SELECT s2.family FROM shop s2 WHERE (s2.money like "%k%"));
because i want to show the results as a group of rows with the same family, i got this query from an other question
Did you try:
SELECT *
FROM shop
WHERE money LIKE "%text%";
You are using a statement from a previous question you posted that might not be valid in this situation.

GROUP BY multiple conditions at once

I have a tables like this:
Users
+----+----------+-------------+
| id | name | other_stuff |
+----+----------+-------------+
| 1 | John Doe | x |
| 2 | Jane Doe | y |
| 3 | Burt Olm | z |
+----+----------+-------------+
Places
+----+------------+-------------+
| id | name | other_stuff |
+----+------------+-------------+
| 1 | Building A | x |
| 2 | Building B | y |
+----+------------+-------------+
Subjects
+----+------------+-------------+
| id | name | other_stuff |
+----+------------+-------------+
| 1 | Math | x |
| 2 | English | y |
+----+------------+-------------+
And a joining table:
PastLectures = lectures that took place
+----+-----------+----------+------------+---------+------------+
| id | id_users | id_place | id_subjects| length | date |
+----+-----------+----------+------------+---------+------------+
| 1 | 1 | 1 | 1 | 60 | 2015-10-25 |
| 2 | 1 | 1 | 1 | 120 | 2015-11-06 |
| 3 | 2 | 2 | 2 | 120 | 2015-11-04 |
| 4 | 2 | 2 | 1 | 60 | 2015-11-10 |
| 5 | 1 | 2 | 1 | 60 | 2015-11-10 |
| 6 | 2 | 2 | 1 | 40 | 2015-11-15 |
| 7 | 1 | 2 | 2 | 30 | 2015-11-15 |
+----+-----------+----------+------------+---------+------------+
I would like to display SUM of all lessons for each user for given month. The SUM should by grouped by each Places and Subjects.
The result in final PHP output should look like this:
November 2015
+------------+-------------+---------------+-------------+
| Users.name | Places.name | Subjects.name | sum(length) |
+------------+-------------+---------------+-------------+
| Burt Olm | - | - | - |
| Jane Doe | Building B | Math | 100 |
| = | = | English | 120 |
| John Doe | Building A | Math | 120 |
| = | Building B | Math | 60 |
| = | = | English | 30 |
+------------+-------------+---------------+-------------+
I have tried creating the full output in pure SQL query using multiple GROUP BY (Group by - multiple conditions - MySQL), but when I do GROUP BY User.id,Places.id it shows each user only once (3 results) no matter the other GROUP BY conditions.
SQL:
SELECT PastLectures.id_users,Users.name AS user,Places.name AS places,Subjects.name AS subjects
FROM PastLectures
LEFT JOIN Users ON PastLectures.id_users = Users.id
LEFT JOIN Places ON PastLectures.id_Places = Places.id
LEFT JOIN Subjects ON PastLectures.id_Subjects = Subjects.id
WHERE date >= \''.$monthStart->format('Y-m-d H:i:s').'\' AND date <= \''.$monthEnd->format('Y-m-d H:i:s').'\'
GROUP BY Users.id,Places.id
ORDER BY Users.name,Places.name,Subjects.name
But I don't mind if part of the solution is done in PHP, I just don't know what to do next.
EDIT:
I also have a table Timetable, that stores who regularly teaches what and where. It stores only used combinations of the tables (each valid combination once).
Timetable = lectures that regularly take place
+----+-----------+----------+------------+-------------+
| id | id_users | id_place | id_subjects| other_stuff |
+----+-----------+----------+------------+-------------+
| 1 | 1 | 1 | 1 | x |
| 2 | 1 | 2 | 1 | y |
| 3 | 1 | 2 | 2 | z |
| 4 | 2 | 2 | 1 | a |
| 5 | 2 | 2 | 2 | b |
+----+-----------+----------+------------+-------------+
Is it possible to add only users with combinations that have a row in this table?
In this case it would mean omitting Burt Olm (no id=3 in Timetable). But if Burt has a Timetable entry and still no PastLectures entry, he would show here as in sample result (he should have had a lecture that month, because he is in Timetable, but no lectures took place).
Based on #Barmar's solution I updated the final SQL by making Timetable a primary table and adding one more LEFT JOIN to suffice those needs.
Final SQL:
SELECT Users.name AS user,Places.name AS places,Subjects.name AS subjects, SUM(PastLectures.length)
FROM Timetable
LEFT JOIN PastLectures ON PastLectures.id_users = Timetable.id_users AND PastLectures.id_place = Timetable.id_place AND PastLectures.id_subjects = Timetable.id_subjects
AND date BETWEEN '2015-11-01 00:00:00' AND '2015-11-30 23:59:59'
LEFT JOIN Places ON Timetable.id_Place = Places.id
LEFT JOIN Subjects ON Timetable.id_Subjects = Subjects.id
LEFT JOIN Users ON Timetable.id_users = Users.id
GROUP BY Timetable.id,Timetable.id_users,Timetable.id_Place,Timetable.id_Subjects
ORDER BY Users.name,Places.name,Subjects.name
You need to include Subjects.id in the GROUP BY, so you get a separate result for each subject.
Also, you shouldn't use columns in tables that are joined with LEFT JOIN in the GROUP BY column. If you do that, all the non-matching rows will be grouped together, because they all have NULL in that column. Use the columns in the main table.
GROUP BY PastLectures.id_users, PastLectures.id_Place, PastLectures.id_Subjects
DEMO
Note that there's no row for Burt Olm in the demo output, because all his rows are filtered out by the WHERE clause. If you want all users to be shown, you should make Users the main table, not PastLectures. And the date criteria needs to be moved into the ON clause when joining with PastLectures.
SELECT Users.name AS user,Places.name AS places,Subjects.name AS subjects, SUM(length)
FROM Users
LEFT JOIN PastLectures ON PastLectures.id_users = Users.id
AND date BETWEEN '2015-11-01 00:00:00' AND '2015-11-30 23:59:59'
LEFT JOIN Places ON PastLectures.id_Place = Places.id
LEFT JOIN Subjects ON PastLectures.id_Subjects = Subjects.id
GROUP BY Users.id, PastLectures.id_Place, PastLectures.id_Subjects
ORDER BY Users.name,Places.name,Subjects.name
DEMO
According to standard SQL, you should GROUP BY all the fields you select, except for the aggregated fields (like sum). Althought MySql allows to do otherwise, when it can be done adhering to the standards, it is better to do so (who knows when you need to port your code to another database engine). So write your SQL like this:
SELECT PastLectures.id_users,
Users.name AS user,
Places.name AS places,
Subjects.name AS subjects,
Sum(length)
FROM PastLectures
LEFT JOIN Users ON PastLectures.id_users = Users.id
LEFT JOIN Places ON PastLectures.id_Places = Places.id
LEFT JOIN Subjects ON PastLectures.id_Subjects = Subjects.id
WHERE date BETWEEN \''.$monthStart->format('Y-m-d H:i:s').'\'
AND \''.$monthEnd->format('Y-m-d H:i:s').'\'
GROUP BY PastLectures.id_users,
Users.name,
Places.name,
Subjects.name
ORDER BY Users.name,
Places.name,
Subjects.name

SQL: get data spread over 3 tables

I am trying to get some statistics for an online game I maintain. I am searching for an SQL statement to get the result on the bottom.
There are three tables:
A table with teams, each having a unique identifier.
table teams
---------------------
| teamid | teamname |
|--------|----------|
| 1 | team_a |
| 2 | team_x |
---------------------
A table with players, each having a unique identifier and optionally an affiliation to one team by it's unique teamid.
table players
--------------------------------
| playerid | teamid | username |
|----------|--------|----------|
| 1 | 1 | user_a |
| 2 | | user_b |
| 3 | 2 | user_c |
| 4 | 2 | user_d |
| 5 | 1 | user_e |
--------------------------------
Finally a table with events. The event (duration in seconds) is related to one of the players through their playerid.
table events.
-----------------------
| playerid | duration |
|----------|----------|
| 1 | 2 |
| 2 | 5 |
| 3 | 3 |
| 4 | 8 |
| 5 | 12 |
| 3 | 4 |
-----------------------
I am trying to get a result where the durations of all team members is summed up.
result
--------------------------
| teamid | SUM(duration) |
|--------|---------------|
| 1 | 14 | (2+12)
| 2 | 15 | (3+8+4)
--------------------------
I tried several combinations of UNION, WHERE IN, JOIN and GROUP but could not get it right. I am using PostgreSQL and PHP. Can anyone help me?
Just use sum with group by:
select t.teamid, sum(e.duration)
from team t
join players p on t.teamid = p.teamid
join events e on p.playerid = e.playerid
group by t.teamid
If you need all teams to be returned even if they don't have events, then use an outer join instead.
Try this
SELECT teamid, Sum(duration),
AS LineItemAmount, AccountDescription
FROM teams
JOIN teams ON teams.teamid = players.teamid
JOIN events ON players.playersid = events.playersid
JOIN GLAccounts ON InvoiceLineItems.AccountNo = GLAccounts.AccountNo
GROUP BY teamid
http://www.w3computing.com/sqlserver/inner-joins-join-two-tables/

In SQL, how do I get the awards total for each player

I have two tables, one called Players and one called Awards. End-users give an "award" to a player, and players can receive the same award multiple times.
Awards Table
----+------------------------------------------------------------------------+
| ID | name | player_id |
+----+-----------------------------------------------------------------------+
| 1 | Free-throw Excerpt | 1 |
| 2 | Free-throw Excerpt | 6 |
| 3 | Top Earner | 1 |
| 4 | Top Player | 5 |
| 5 | Free-throw Excerpt | 1 |
| 6 | Free-throw Excerpt | 1 |
| 7 | Top Earner | 1 |
| 8 | Top Player | 1 |
...
+----+-----------------------------------------------------------------------
`Players Table`
----+------------------------------------------------------------------------+
| ID | name |
+----+-----------------------------------------------------------------------+
| 1 | Player A |
| 2 | Player B |
| 3 | Player C |
| 4 | Player D
... |
+----+-----------------------------------------------------------------------
In my app, each player has a page, and on that page, I want to display all the awards that the player has won. For example, for Player B's page:
Player A Stats:
Free Throw Excerpt: 3 votes
Top Earner: 2 Votes
Top Player: 1 Vote
I can query the the awards table to get all the awards given to Player A, but I'm stuck here and don't know where to go. Do I need to then do a COUNT(*) to get the number of each award received? (1 for Top Player, 3 for Free Throw Excerpt).
You are right. Count with Group By will do it
Select a.Name, Count(*) AS Total From Awards a
Where a.PlayerID = 1
Group By a.Name
Order By Count(*) DESC

Categories