Combining these three SQL Queries - php

I have developed these three queries, found below, that find the most views, most comments, and most likes that each Article has (an article is defined by both the Content ID and Format). I undersand that Target ID != ContentID and that Format != TargetClass, but I treat them as they are the same thing.
I need to use these three queries to output the top 3 articles that hold the most comments, likes and views, in priority of that order. I'm not exceptionally talented at SQL, so could someone please provide a solution and a mild explanation? If the question needs to be rephrased, please make it clear and I will do so. Cheers.
MOST VIEWS (In descending order; with most views being at the top)
SELECT ContentID, Format
FROM Content
GROUP BY ContentID, Format
ORDER BY COUNT(View) DESC
MOST COMMENTS (In descending order; with most comments being at the top)
SELECT TargetID, TargetClass
FROM Comments
GROUP BY TargetID, TargetClass
ORDER BY COUNT(TargetID) DESC
MOST LIKES (In descending order; with most likes being at the top)
SELECT ContentID, Format
FROM Likes
GROUP BY ContentID, Format
ORDER BY COUNT(ContentID) DESC
EXAMPLE DATA AND OUTPUT (As requested)
TABLE 1:
ContentID| Format |View|
---------|---------|----|
1 |Paperback|1700|
---------|---------|----|
1 | Ebook |1500|
---------|---------|----|
2 |Paperback|1500|
-------------------------
TABLE 2:
CommentID|TargetID |TargetClass|
---------|---------|-----------|
1 | 1 | Ebook |
---------|---------|-----------|
2 | 2 | Paperback |
---------|---------|-----------|
3 | 1 | Ebook |
--------------------------------
TABLE 3:
LikeID | ContentID| Format |
---------|---------|---------|
1 | 1 |Ebook |
---------|---------|---------|
2 | 2 |Paperback|
---------|---------|---------|
3 | 2 |Paperback|
------------------------------
DESIRED SOLUTION:
In table 1 contentId = 1 and format = paperback have the most views, BUT, views are weigh less than likes when decided the popularity of an article. In table 3, ContentId=2 and format=paperback, have more likes than either of the other two articles (the one absent from the table has no likes at all). Comments are weighted most heavily however, and in table 2, targetid=1, format=ebook has more comments than either of the other two articles.
This means that targetid=1 and format=ebook is the most popular book. HOWEVER, when two have the same number of comments, they fall back on the likes to determine the most popular article. However, if, once again, they have the same number of likes, it falls back on views to determine the most popular article.
The output required is a list as follows:
ContentID | Format
1 | ebook
2 | paperback
1 | paperback
In order of their "popularity".

I think this is the correct solution:
SELECT V1.ContentID, V1.Format
FROM
((SELECT ContentID, Format ,Sum(View) AS CountView
FROM Content
GROUP BY ContentID, Format) V1) LEFT OUTER JOIN
(
SELECT V2.ContentID, V2.Format ,CountComments,CountLikes
FROM
((SELECT TargetID AS ContentID, TargetClass AS Format,Count(TargetID) AS CountComments
FROM Comments
GROUP BY TargetID, TargetClass) V2) LEFT OUTER JOIN
((SELECT ContentID, Format ,Count(ContentID) AS CountLikes
FROM Likes
GROUP BY ContentID, Format) V3)
ON(V3.ContentID=V2.ContentID AND V3.Format =V2.Format)
) V12
ON(V1.ContentID=V12.ContentID AND V1.Format =V12.Format)
ORDER BY CountComments DESC,CountLikes DESC ,CountView DESC
LIMIT 3;

I think you can do this with union or union all. Perhaps the following does what you want:
(SELECT TargetID as ContentId, TargetClass
FROM Comments
GROUP BY TargetID, TargetClass
ORDER COUNT(*) DESC
LIMIT 1
)
UNION ALL
(SELECT ContentID, Format
FROM CONTENT
GROUP BY ContentID, Format
ORDER BY COUNT(*) DESC
LIMIT 1
)
UNION ALL
(SELECT ContentID, Format
FROM Likes
GROUP BY ContentID, Format
ORDER BY COUNT(*) DESC
LIMIT 1
)

Related

subquery returns more than one row for this query [duplicate]

I want to select information from two SQL tables within one query, the information is unrelated though, so no potential joints exist.
An example could be the following setup.
tblMadrid
id | name | games | goals
1 | ronaldo | 100 | 100
2 | benzema | 50 | 25
3 | bale | 75 | 50
4 | kroos | 80 | 10
tblBarcelona
id | name | games | goals
1 | neymar | 60 | 25
2 | messi | 150 | 200
3 | suarez | 80 | 80
4 | iniesta | 40 | 5
I want to have a query that gives me the following:
name | games | goals
messi | 150 | 200
ronaldo | 100 | 100
I tried to follow this logic: Multiple select statements in Single query but the following code did not work:
USE Liga_BBVA
SELECT (SELECT name,
games,
goals
FROM tblMadrid
WHERE name = 'ronaldo') AS table_a,
(SELECT name,
games,
goals
FROM tblBarcelona
WHERE name = 'messi') AS table_b
ORDER BY goals
Any advice on this one? Thanks
Info: The football stuff is just a simplifying example. In reality it is not possible to put both tables into one and have a new "team" column. The two tables have completely different structures, but I need something that matches the characteristics of this example.
You can do something like this:
(SELECT
name, games, goals
FROM tblMadrid WHERE name = 'ronaldo')
UNION
(SELECT
name, games, goals
FROM tblBarcelona WHERE name = 'messi')
ORDER BY goals;
See, for example: https://dev.mysql.com/doc/refman/5.0/en/union.html
If you like to keep records separate and not do the union.
Try query below
SELECT (SELECT name,
games,
goals
FROM tblMadrid
WHERE name = 'ronaldo') AS table_a,
(SELECT name,
games,
goals
FROM tblBarcelona
WHERE name = 'messi') AS table_b
FROM DUAL
The UNION statement is your friend:
SELECT a.playername, a.games, a.goals
FROM tblMadrid as a
WHERE a.playername = "ronaldo"
UNION
SELECT b.playername, b.games, b.goals
FROM tblBarcelona as b
WHERE b.playername = "messi"
ORDER BY goals;
You can union the queries as long as the columns match.
SELECT name,
games,
goals
FROM tblMadrid
WHERE id = 1
UNION ALL
SELECT name,
games,
goals
FROM tblBarcelona
WHERE id = 2
You can combine data from the two tables, order by goals highest first and then choose the top two like this:
MySQL
select *
from (
select * from tblMadrid
union all
select * from tblBarcelona
) alldata
order by goals desc
limit 0,2;
SQL Server
select top 2 *
from (
select * from tblMadrid
union all
select * from tblBarcelona
) alldata
order by goals desc;
If you only want Messi and Ronaldo
select * from tblBarcelona where name = 'messi'
union all
select * from tblMadrid where name = 'ronaldo'
To ensure that messi is at the top of the result, you can do something like this:
select * from (
select * from tblBarcelona where name = 'messi'
union all
select * from tblMadrid where name = 'ronaldo'
) stars
order by name;
select name, games, goals
from tblMadrid where name = 'ronaldo'
union
select name, games, goals
from tblBarcelona where name = 'messi'
ORDER BY goals
Using union will help in this case.
You can also use join on a condition that always returns true and is not related to data in these tables.See below
select tmd .name,tbc.goals from tblMadrid tmd join tblBarcelona tbc on 1=1;
join will help you even in case when tables do not have common columns
You can use UNION in this case
select id, name, games, goals from tblMadrid
union
select id, name, games, goals from tblBarcelona
you jsut have to maintain order of selected columns ie id, name, games, goals in both SQLs
as i see you want most goals in each team
you can try this
select name,games,max(goals) as 'most goals' from tblRealMadrid
union
select name,games,max(goals) as 'most goals' from tblBarcelona
In your case, the two tables have completely different structures and cannot be joined.
The UNION operator could be used. The UNION operator joins the results of two or more SELECT statements to produce a single result set. The first column in the SELECT statement is used to sort the result set.
SELECT name, games, goals
FROM tblMadrid
WHERE name = 'ronaldo'
UNION
SELECT name, games, goals
FROM tblBarcelona
WHERE name = 'messi'
ORDER BY goals;
Each SELECT statement must have the same number of columns and data types that are compatible. Also, if you want to keep the duplicates, use UNION ALL rather than UNION.

MYSQL: closest to supplied date grouped by user_id

I need to get multiple rows with a date_added closest to but not past a user supplied date, grouped by user_id.
I've looked at a bunch of max in group type answers but I'm not quite there:
Get nearest records to specific date grouped by type
SQL Query to show nearest date?
Find closest datetime to specified datetime in mysql query
Get closest date from MySQL table
https://dev.mysql.com/doc/refman/5.0/en/example-maximum-column-group-row.html
This is close: https://stackoverflow.com/a/17038667/5319244. Finds the max date though, I need the max specified by user input, not max outright.
Here's a subset of the data with the correct organisation_id, framework_id and level_id already filtered.
+----+---------+-----------------+--------------+----------+---------------------+
| id | user_id | organisation_id | framework_id | level_id | date_added |
+----+---------+-----------------+--------------+----------+---------------------+
| 2 | 1 | 2 | 1 | 1 | 2015-07-31 14:02:49 |
| 9 | 2 | 2 | 1 | 1 | 2015-09-01 11:05:09 |
| 11 | 1 | 2 | 1 | 1 | 2015-09-07 14:13:39 |
+----+---------+-----------------+--------------+----------+---------------------+
If the supplied date is 2015-09-07. I'd expect to see id's: 9 and 11.
If the supplied date is 2015-09-01. I'd expect to see id's: 2 and 9.
If the supplied date is 2015-07-31. I'd expect to see id: 2.
This query is as close as I got:
SELECT t1.id
, t1.user_id
, t1.date_added
FROM
completed_frameworks AS t1
WHERE date_added = (
SELECT
MAX(date_added)
FROM
completed_frameworks
WHERE
user_id = t1.user_id
AND
date_added <= '2015-09-07 23:59:59'
)
AND
(
t1.organisation_id = 2
AND
t1.framework_id = 1
AND
t1.level_id = 1
)
It returns what I expect for the date: 2015-09-07
When the date is 2015-09-01 however it only returns id 9. Not also 2 as I'd expect.
When the date is 2015-07-31 it returns 0 rows..
Let me know if I there's anything else I can provide.
Cheers!
EDIT:
Thanks for the replies thus far. I need to clarify two points:
1) I don't have a limit. I'm expecting those rows due to the user id's. There could be n users returned. I just want a row for each user where the date_added is closest to the user supplied date.
2) The date supplied will not have a time value. It will be from a simple datepicker UI. In my example query I've added the time of 23:59:59 to encompass all of that day.
Try this subquery instead. The organisation_id/framework_id/level_id filter is moved into the subquery and it now returns the right values for the examples you've given.
SELECT t1.id, t1.user_id, t1.date_added
FROM
completed_frameworks AS t1
WHERE date_added = (
SELECT
MAX(date_added)
FROM
completed_frameworks
WHERE
user_id = t1.user_id
AND
date_added <= '2015-09-01 23:59:59'
AND
organisation_id = 2
AND
framework_id = 1
AND
level_id = 1
)
Here is a sample query you could use for an input date of '2015-09-07 14:13:39'. The inner query returns the input date along with the next highest date. This temporary table is then used to filter completed_frameworks to give you only records from the two dates most recent to the input date.
SELECT t1.id, t1.user_id, t1.date_added
FROM completed_frameworks t1
INNER JOIN
(
SELECT cf.date_added
FROM completed_frameworks cf
GROUP BY cf.date_added
HAVING cf.date_added <= '2015-09-07 14:13:39'
ORDER BY cf.date_added DESC
LIMIT 2
) t2
ON t1.date_added = t2.date_added
WHERE t1.organisation_id = 2 AND t1.framework_id = 1 AND t1.level_id = 1
I think this does what you want:
SELECT f.id, f.user_id f.date_added
FROM completed_frameworks f
WHERE date(f.date_added) <= '2025-09-07' -- or whatever your date is
ORDER BY f.date_added DESC
LIMIT 2;
EDIT:
If you want one date per user id that is closest to the specified date, I would suggest:
select f.*
from completed_frameworks f join
(select user_id, max(date_added) as maxda
from completed_frameworks
where date(date_added) <= '2015-09-07'
group by user_id
) u
on f.user_id = u.user_id and f.date_added = maxda

join 2 tables and only display the max values for a id - mysql

I have two tables that hold information about a drawing that I join in my query. The first table contains the drawings unique number, title and who it was drawn by. The second table contains the revision and the date the drawing was revised.
Table 1
|dwg_id|project_no|sws_dwg_no|dwg_title|dwg_by|
|1 |153 |153-100 |Pipe... |JS |
Table 2
|dwg_id|dwg_rev|dwg_date |rev_id|
|1 |A |2015-07-15 11:00:00 |1 |
|1 |B |2015-07-23 12:00:00 |2 |
|1 |C |2015-08-06 10:00:00 |3 |
I want join the two tables and only show the most recent revision change for a drawing.
This is my current query.
SELECT
`drawings`.`dwg_id`,
`project_no`,
`sws_dwg_no`,
`client_dwg_no`,
`dwg_title`,
`dwg_by`,
`dwg_rev`.`dwg_rev`,
`dwg_rev`.`dwg_date`,
MAX(`dwg_rev`.`dwg_rev`) AS dwg_rev
FROM
(`drawings`)
JOIN `dwg_rev` ON `drawings`.`dwg_id` = `dwg_rev`.`dwg_id`
WHERE
`project_no` = '153'
GROUP BY
`sws_dwg_no`,
`dwg_rev`.`dwg_rev`
ORDER BY
`dwg_rev`.`dwg_date` ASC,
`dwg_rev`.`dwg_rev` ASC
The results that this query returns doesn't contain the latest revision numbers or it returns all the revision for each drawing.
You can use the following query:
SELECT d.*, dr.*
FROM drawings AS d
INNER JOIN (
SELECT dwg_id, MAX(rev_id) AS maxRevId
FROM dwg_rev
GROUP BY dwg_id
) AS t ON d.dwg_id = t.dwg_id
INNER JOIN dwg_rev AS dr ON t.dwg_id = dr.dwg_id AND t.maxRevId = dr.rev_id
WHERE project_no = 153
The key point in the above query is the usage of a derived table that returns the latest revision, i.e. MAX(rev_id), per dwg_id. Using an INNER JOIN on that derived table you get back exactly this row out of dwg_rev table.
Using something like the above is necessary if you have multiple dwg_id per project_no. In this case, the above query will fetch the most recent revision per drawing for project_no = 153.
Demo here
Sometimes MAX isn't the best way to go, instead use LIMIT Try this:
SELECT
`drawings`.`dwg_id`,
`project_no`,
`sws_dwg_no`,
`client_dwg_no`,
`dwg_title`,
`dwg_by`,
`dwg_rev`.`dwg_rev`,
`dwg_rev`.`dwg_date`,
`dwg_rev`.`dwg_rev` AS dwg_rev
FROM
(`drawings`)
JOIN `dwg_rev` ON `drawings`.`dwg_id` = `dwg_rev`.`dwg_id`
WHERE
`project_no` = '153'
GROUP BY
`sws_dwg_no`,
`dwg_rev`.`dwg_rev`
ORDER BY
`dwg_rev`.`dwg_date` DESC,
`dwg_rev`.`dwg_rev` DESC
LIMIT 1;
If you need the latest revision you should order DESC check code below.
and also you can order only by dwg_rev.rev_id` DESC , if this rev_id is populated.
SELECT
drawings.dwg_id,
project_no,
sws_dwg_no,
client_dwg_no,
dwg_title,
dwg_by,
dwg_rev.dwg_rev,
dwg_rev.dwg_date,
MAX(dwg_rev.dwg_rev) AS dwg_rev
FROM
(drawings)
JOINdwg_revONdrawings.dwg_id=dwg_rev.dwg_id
WHERE
project_no= '153'
GROUP BY
sws_dwg_no,
dwg_rev.dwg_rev
ORDER BY
dwg_rev.dwg_dateDESC,
dwg_rev.dwg_revDESC
LIMIT 1;

MySQL Order by sum of another table

I currently have 2 tables in mysql; comments and comment_rating
comment_rating has the following structure:
+------------+
| Field |
+------------+
| id |
| comment_id |
| user_id |
| positive |
| created_at |
+------------+
The field positive is either 1 or -1 1 being a positive (up vote) and -1 being negative (down vote)
I have this current query which will get me the most rated on comment:
SELECT *, COUNT(comment_rating.id) AS rating_count FROM comments LEFT JOIN comment_rating ON comments.id = comment_rating.comment_id GROUP BY comments.id ORDER BY rating_count DESC
I Need to know how (using mysql query) I am able to get comments ordered by best rating;
Meaning ordered by the sum of from the rating per comment.
Example:
Comment X has 2 upvotes and 4 downvotes (grand total of -2)
Comment Y has no votes (grand total of 0)
Comment Z has 1 upvote (grand total of 1)
The order these will come out will be:
Comment Z
Comment Y
Comment X
SELECT comments.id,
COUNT(comment_rating.id) AS rating_count,
sum(positive) as rating
FROM comments
LEFT JOIN comment_rating ON comments.id = comment_rating.comment_id
GROUP BY comments.id
ORDER BY rating DESC
Thank you juergen d
Your query was almost perfect:
here is a slightly modified version which will do the ordering correctly:
SELECT comments.id,
COUNT(comment_rating.id) AS rating_count,
COALESCE(SUM(positive),0) as rating
FROM comments
LEFT JOIN comment_rating ON comments.id = comment_rating.comment_id
GROUP BY comments.id
ORDER BY rating DESC
This way if the row doesnt exist it will be set to zero rather than null and will be ordered correctly.

MYSQL sorting and limit using a certain row and selecting previous and next rows

I have a simple table called users with the following data:
id | hops
1 | 3
2 | 1
3 | 5
4 | 2
5 | 4
6 | 5
I want to select the number of hops of any given id that I specify and also select the next and previous ids according to the number of hops sorted from highest to lowest.
To explain more I use the following query:
SELECT * FROM test WHERE id = 1
OR (id > 1 AND hops >= (SELECT hops FROM test WHERE id= 1) )
OR (id < 1 AND hops <= (SELECT hops FROM test WHERE id= 1) )
LIMIT 3
So in the above query I tried to get id=1, next id with the same or higher number of hops, and the previous id with the same or lower number of hops.
This is the result i get:
id | hops
1 | 3
3 | 5
5 | 4
As you can see it selected id=1 and two higher ids although I want only one higher id and one lower id. So, in this case the result should be like this instead:
id | hops
1 | 3
3 | 5
As there is no lower id than 1, so nothing lower to fit the criteria and selects only 1 higher id. The wrong result is because of using LIMIT 3 but I can't use a LIMIT for each condition. So don't know how to approach this at all.
Have another question, would using the sub-query
"SELECT hops FROM test WHERE id= 1"
slow down the server on a large scale?? I heard that it's not preferable to use sub-queries but have no other way to get this number except using a separate query.
Thanks
here you go, change the order by ID according to your liking...you didn't say if you wanted the closest number of hops or the closest ID, just one greater or lower
SELECT * FROM test
WHERE id IN (1,(
SELECT id FROM test WHERE id > 1 AND hops >= (
SELECT hops FROM test WHERE id = 1
) ORDER BY id LIMIT 1
), (
SELECT id FROM test WHERE id < 1 AND hops <= (
SELECT hops FROM test WHERE id = 1
) ORDER BY id DESC LIMIT 1
))
If I understand your question correctly, I believe the following will work.
-- Previous Rec
SELECT t2.*
FROM test as t1
JOIN test as t2 ON t2.hop <= t1.id
WHERE t1.id = 1
ORDER BY t2.id DESC
LIMIT 1
UNION ALL
-- Current Rec
SELECT *
FROM test as t
WHERE id = 1
UNION ALL
-- Following Rec
SELECT t2.*
FROM test as t1
JOIN test as t2 ON t2.id >= t1.hop
WHERE t1.id = 1
ORDER BY t2.id ASC
LIMIT 1

Categories