MySQL JOIN ratings by ID to details by ID - php

hoping someone can help. MySQL JOIN statements have never been my strong point and they just hurt my brain trying to figure them out.
I have table which stores ratings of an item in another table, tracked by ID.
What I need to do now though is display a list of items ordered by the ratings, high to low and low to high.
I can get a list of ratings per item, grouped by ID from the ratings table easily enough, but it's getting it JOINed to the items I get stuck on. Not all items have ratings either (yet), and so it would also be beneficial if the combined list didn't just stop at the end of the ratings that do exist.
OK, so here's my grouping statement:
SELECT `themeID` , SUM( `rating` ) AS ratings
FROM `votes`
GROUP BY `themeID`
ORDER BY `ratings` DESC
outputs
themeID ratings
1 6
3 3
2 2
6 2
Then the details table consists of various info, such as id, filename, name, date etc
Between the two tables, themeID and id are the same which links them. I've looked at some of the other answers to similar queries on SO, but I couldn't get any of the answers to work with my tables/queries (probably because I don't fully grasp JOIN's)
ANy help would be saving me a massive headache!

Just join the two tables and add the aggregation function.
SELECT d.id, d.filename, d.name, IFNULL(SUM(v.rating), 0) AS ratings
FROM details AS d
LEFT JOIN votes AS v ON d.id = v.themeID
GROUP BY d.id
I used LEFT JOIN so this will show the details even if there are no votes.

I have tested in sql server same u can get in Mysql
DROP TABLE #Item
create table #Item (ID int identity(1,1),ItemNAme varchar(10))
INSERT INTO #Item(ItemNAme)
SELECT 'A'
UNION ALL
SELECT 'B'
UNION ALL
SELECT 'C'
DROP TABLE #ItemRating
create table #ItemRating (ItemID int ,rating int)
INSERT INTO #ItemRating(ItemID,rating)
SELECT 3,2
UNION ALL
SELECT 2,11
UNION ALL
SELECT 1, 3
UNION ALL
SELECT 2, 4
union all
SELECT 1,5
UNION ALL
SELECT 3,12
UNION ALL
SELECT 1, 4
UNION ALL
SELECT 2, 1
SELECT m.ItemNAme,SUM(I.rating) as Rating
FROM #Item m INNER JOIN #ItemRating I ON m.ID=I.ItemID
group by m.ItemNAme
ORDER BY SUM(I.rating) asc
--OR same thing can achive
SELECT ItemNAme, Rating
FROM (
SELECT m.ItemNAme,SUM(I.rating) as Rating
FROM #Item m INNER JOIN #ItemRating I ON m.ID=I.ItemID
group by m.ItemNAme
)P
ORDER BY Rating Desc

Related

MySQL, getting all top users

I have a table named items which has 3 columns : id, user_id, item_name.
I want to select and show all users that have most submitted items in that table.
For instance :
User-1 has 3 items,
User-2 has 8 items,
User-3 has 5 items,
User-4 has 8 items, and
User-5 has 8 items too.
Based on what I need, the query should be outputting User-2, User-4 and User-5.
My knowledge of MySQL is not thorough unfortunately and I can't get this done by myself.
Your help is much appreciated.
EDIT #1 :
Here's the query that I tried and didn't output my desired result :
SELECT COUNT(id) AS total_count
, user_id
FROM ".DB_PREFIX."items
GROUP
BY user_id
It shows all users and their total number of items submitted. As I mentioned earlier, I need all top users.
E.g.:
SELECT a.*
FROM
( SELECT user_id
, COUNT(*) total
FROM my_table
GROUP
BY user_id
) a
JOIN
( SELECT COUNT(*) total
FROM my_table
GROUP
BY user_id
ORDER
BY total DESC
LIMIT 1
) b
ON b.total = a.total;

Selecting N rows from each group in MYSQL

I need to search for the updates sent by the friends of a giving user.
There is a table called friendship. It has a column called profile1 and another one called profile2. It represents the friendship between two users in this websystem, and a friendship is the presence of two giving ids, no matter in what position. So the profile with id 1 may have 2 friends, profile with id 2 and with id 3 as following:
friendship
profile1 profile2
1 2 <--
3 1 <--
2 5
...
Now I want to search for the updates sent by some user's friends. There is this table update
update
id content time profile
1 A text ... 2
2 A text ... 2
3 A text ... 3
4 A text ... 2
5 A text ... 3
6 A text ... 2
7 A text ... 10
8 A text ... 11
If my profile/user is identified by the id 1, and it has only 2 friends (the profiles identified by id 2 and 3) and also I need my search to return only 2 results by each user, my SELECT has to return updates 1,2,3 and 5.
Preferably updates should be grouped by its author and it would be great if I could set the number of different profiles to be considered in this search (for example, if profile 1 had 10 friends and I wanted only updates from 3 profiles, the most recent must appear first).
Do you know how can I achieve this??
thank you very much!
#EDIT
This returns all updates sent by friends of profile 1. But i'm not sure whether or not i'm in the right direction
SELECT u.*
FROM `update` u
INNER JOIN friendship f1 ON f1.profile1 = u.author
WHERE f1.profile2 =1
UNION
SELECT u.*
FROM `update` u
INNER JOIN friendship f2 ON f2.profile2 = u.author
WHERE f2.profile1 =1
If you are willing to do it in two queries, you can do it like this. First, get three profiles who have most recently posted based on your constraints:
-- Get the three latest updated profiles from here.
-- (we can't use a CTE because MySQL doesn't support
-- them yet).
SELECT DISTINCT p.profile FROM
(
SELECT ui.profile, ui.time FROM
(
SELECT u.profile, u.time
FROM `update` u
INNER JOIN `friendship` f ON f.profile2 = u.profile
WHERE f.profile1 = 1
UNION ALL
SELECT u.profile, u.time
FROM `update` u
INNER JOIN `friendship` f ON f.profile1 = u.profile
WHERE f.profile2 = 1
) ui ORDER BY ui.time DESC
) p LIMIT 0, 3;
From that query, get the three profile IDs out and put them in place of <id1>, <id2> and <id3> in the following query
-- Use a union to get the result set back
(SELECT a.content, a.time, a.profile FROM `update` a
WHERE a.profile = <id1>
ORDER BY a.time DESC
LIMIT 0, 2)
UNION ALL
(SELECT a.content, a.time, a.profile FROM `update` a
WHERE a.profile = <id2>
ORDER BY a.time DESC
LIMIT 0, 2)
UNION ALL
(SELECT a.content, a.time, a.profile FROM `update` a
WHERE a.profile = <id3>
ORDER BY a.time DESC
LIMIT 0, 2);
If you get less than three profiles back, either remove parts of the query in your PHP code, or set the WHERE clause to something like 0 so it always evaluates to fault (assuming you don't have a profile ID of zero)
The 2 in the limit clauses above can be changed if you want more or fewer results per profile.
Sample SQL fiddle: http://sqlfiddle.com/#!2/22e57/1 (updated fiddle to make the content more meaningful and to use times)
I would suggest doing a series of queries for each author within one transaction, that way there would not be a need for grouping - you could simply append results together outside of your SQL.
SELECT * FROM `update` WHERE
profile IN (SELECT profile2 FROM `friendship` WHERE profile1=1) OR
profile IN (SELECT profile1 FROM `friendship` WHERE profile2=1);
try this sqlFiddle
SELECT T1.profile,T1.content,T1.time
FROM
(SELECT UPD.profile,UPD.content,UPD.time,
IF (#prevProfile != UPD.profile,#timeRank:=1,#timeRank:=#timeRank+1) as timeRank,
#prevProfile := UPD.profile
FROM
(SELECT UP.profile,UP.content,UP.time
FROM
(SELECT profile,max(time) as latestUpdateTime
FROM friendship F INNER JOIN updates U
ON (F.profile1 = 1 AND U.profile = profile2) /* <-- specify profile on this line */
OR(F.profile2 = 1 AND U.profile = profile1) /* <-- specify profile on this line */
GROUP BY profile
ORDER BY latestUpdateTime DESC
LIMIT 3 /* limit to 3 friends profiles that have the most recent updates */
)as LU
INNER JOIN updates UP
ON (UP.profile = LU.profile)
ORDER BY profile,time DESC
)as UPD,(SELECT #prevProfile:=0,#timeRank:=0)variables
)T1
WHERE T1.timeRank BETWEEN 1 AND 2 /* grab 2 lastest updates for each profile */
ORDER BY T1.time DESC
in my example, profile id 1 has more than 3 friends, but i am only grabbing 3 friends that made the most recent updates.
explanation of above query.
LU grabs 3 profiles that are friends with profile id 1 that made the latest updates.
UPD grabs all contents that belong to these 3 friends.
T1 returns the contents along with a timeRank number for each content in order from 1 counting upward order by time DESCENDING for each profile
and finally the WHERE we only grab 2 content updates for each profile
then we finally ORDER these updates based on TIME starting from most recent.

Selecting SUM+COUNT from one table and COUNT from another in Single query

I want to fetch Sum of age and total users which are active from users table and total number of png pictures from pictures table. I tried the following but it is giving me error. How can i do this in single query? I'm using PDO
SELECT
(SELECT SUM(age) AS age_sum,COUNT(*) AS total FROM users where type='active'),
(SELECT COUNT(*) AS pictotal FROM pictures where extension='png')
ERROR -> Operand should contain 1 column(s)
SELECT
AA.age_sum, AA. total, BB.pictotal
FROM
(SELECT SUM(age) AS age_sum,COUNT(*) AS total FROM users where type='active') AA,
(SELECT COUNT(*) AS pictotal FROM pictures where extension='png') BB
For your task is a Sub Select the better decision - like this
SELECT SUM(age) AS age_sum,COUNT(*) AS total, (SELECT COUNT(*) FROM pictures where extension='png') AS pictotal FROM users where type='active')
You simple can't "merge" two different querys in one query when the amount of columns, that are returned from each query, are not equal.
You did it
This select is your problem returns 2 columns
(SELECT SUM(age) AS age_sum,COUNT(*) AS total
SELECT SUM(age) AS age_sum,COUNT(*) AS total,
(SELECT COUNT(*) FROM pictures where extension='png') AS pictotal
FROM users where type='active'
You could try something like this:
SELECT 'AGE_SUM',SUM(age) FROM users where type='active'
UNION
SELECT 'TOTAL',COUNT(*) FROM users where type='active'
UNION
SELECT 'PIC_TOTAL',COUNT(*) FROM pictures where extension='png'

SQL Query for TOP 10 depending on two columns

I am new to SQL (beginner programmer), so I apologize if this might seem like a simple question. I am trying to create a table on my website that displays the lowest 10 grades along with some information about the student. I am pretty comfortable from this aspect, but I am having trouble coding the write SQL query. I am using SQL Server 2008.
I have a table in my database with 10 columns and 500 rows. Two of these columns contain grades (grade1 and grade2). My goal is to display in my website table the TOP 10 lowest GRADE1, but if GRADE1 is NULL I want it to take GRADE2 into consideration and display that instead. So in context, if a student named Billy has no GRADE1 (its NULL) but his GRADE2 is the lowest of all (GRADE1's AND GRADE2's combined), he should be first in the list.
I would really appreciate help making a query capable of accomplishing this task, I have been researching for a solution but it has only confused me more.
You can use case in the order by clause
select top 10 *
from students
order by case when grade1 is null then grade2 else grade1 end desc
EDIT
Following BillyCode comment on including only those students that apears 3 or more times in the table I suggest this
select top 10 s.*
from students s
inner join (select StudentId, Count(*) as total from students) c on s.StudentId = c.StudentId
where c.total >= 3
order by case when grade1 is null then grade2 else grade1 end desc
But I'm not sure if you can join to a subquery.
In SQL Server you want to use isNull()
SELECT TOP 10 isNull(grade1,grade2) AS `Grade`
FROM mytable
ORDER BY Grade DESC
The COALESCE function does what you want. COALESCE(GRADE1, GRADE2) will display GRADE1 if not null, or GRADE2 if GRADE1 is null.
So instead of ORDER BY GRADE1, you can do ORDER BY COALESCE(GRADE1, GRADE2)
See here for more details about COALESCE.
Try this:
select Top 10
student , (case when grade1 is null then grade2 else grade1 end ) as g1 , grade2 as g2 from table order by g1 desc
Another method is to first find the 10 students with the lowest grade1. Rename grade1 into grade. And then find the 10 students with the lowest grade2. Rename grade2 into grade. After that union these two results. From there, find the 10 students with with lowest grade. The SQL is something like:
SELECT id, grade
FROM (SELECT id, grade1 AS grade
FROM students
ORDER BY grade1 DESC
LIMIT 10
UNION
SELECT id, grade2 AS grade
FROM students
ORDER BY grade2 DESC
LIMIT 10)
ORDER BY grade DESC
LIMIT 10

inner join large table with small table , how to speed up

dear php and mysql expertor
i have two table one large for posts artices 200,000records (index colume: sid) , and one small table (index colume topicid ) for topics has 20 record .. have same topicid
curent im using : ( it took round 0.4s)
+do get last 50 record from table:
SELECT sid, aid, title, time, topic, informant, ihome, alanguage, counter, type, images, chainid FROM veryzoo_stories ORDER BY sid DESC LIMIT 0,50
+then do while loop in each records for find the maching name of topic in each post:
while ( .. ) {
SELECT topicname FROM veryzoo_topics WHERE topicid='$topic'"
....
}
+Now
I going to use Inner Join for speed up process but as my test it took much longer from 1.5s up to 3.5s
SELECT a.sid, a.aid, a.title, a.time, a.topic, a.informant, a.ihome, a.alanguage, a.counter, a.type, a.images, a.chainid, t.topicname FROM veryzoo_stories a INNER JOIN veryzoo_topics t ON a.topic = t.topicid ORDER BY sid DESC LIMIT 0,50
It look like the inner join do all joining 200k records from two table fist then limit result at 50 .. that took long time..
Please help to point me right way doing this..
eg take last 50 records from table one.. then join it to table 2 .. ect
Do not use inner join unless the two tables share the same primary key, or you'll get duplicate values (and of course a slower query).
Please try this :
SELECT *
FROM (
SELECT a.sid, a.aid, a.title, a.time, a.topic, a.informant, a.ihome, a.alanguage, a.counter, a.type, a.images, a.chainid
FROM veryzoo_stories a
ORDER BY sid DESC
LIMIT 0 , 50
)b
INNER JOIN veryzoo_topics t ON b.topic = t.topicid
I made a small test and it seems to be faster. It uses a subquery (nested query) to first select the 50 records and then join.
Also make sure that veryzoo_stories.sid, veryzoo_stories.topic and veryzoo_topics.topicid are indexes (and that the relation exists if you use InnoDB). It should improve the performance.
Now it leaves the problem of the ORDER BY LIMIT. It is heavy because it orders the 200,000 records before selecting. I guess it's necessary. The indexes are very important when using ORDER BY.
Here is an article on the problem : ORDER BY … LIMIT Performance Optimization
I'm just give test to nested query + inner join and suprised that performace increase much: it now took only 0.22s . Here is my query:
SELECT a.*, t.topicname
FROM (SELECT sid, aid, title, TIME, topic, informant, ihome, alanguage, counter, TYPE, images, chainid
FROM veryzoo_stories
ORDER BY sid DESC
LIMIT 0, 50) a
INNER JOIN veryzoo_topics t ON a.topic = t.topicid
if no more solution come up , i may use this one .. thanks for anyone look at this post

Categories