How to get TOP 10 in MySQL with combined data rows? - php

I have a challenge I can't seem to handle.
+------+--------+-----------+-------+
| id | user | genres | books |
+------+--------+-----------+-------+
| 1 | John | crimes | 2 |
| 2 | John | scienc | 1 |
| 3 | John | nature | 4 |
| 4 | Pete | nature | 3 |
| 5 | Pete | crime | 2 |
| 6 | Mary | nature | 20 |
+------+--------+-----------+-------+
I would like to have a SQL query that gets the total amount of books the users own, no matter the genre and would like to ORDER them by who has the most.
In this example, you see that Mary has 20 books, Pete 5 and John has 7 so my desired result would be an array like:
result[0][user] = "Mary";
result[0][total] = 20;
result[1][user] = "John";
result[1][total] = 7;
result[2][user] = "Pete";
result[2][total] = 5;
How can I get this into one SQL? Should I use CONCAT or TOP or something? I use MySQL & PHP.

You need GROUP BY with SUM
SELECT `user`, SUM(books) AS total_books
FROM `table`
GROUP BY `user`
ORDER BY total_books DESC
If you only want the first 10 then you can use
SELECT `user`, SUM(books) AS total_books
FROM `table`
GROUP BY `user`
ORDER BY total_books DESC LIMIT 10`
By the way, you might want to rethink your schema slightly. Duplicating info is against the principles of normalisation. You might want to add a new owners table:
+-----------+-------------+
| owner_id | owner_name |
+-----------+-------------+
| 1 | John |
| 2 | Pete |
| 3 | Mary |
+-----------+-------------+
and then reference this by owner_id in your books table.

select user, sum(books) as total
from your_table
group by user
order by sum(books)
limit 10

SELECT sum(books) as book_count, user FROM `books` GROUP BY (user) order by book_count DESC

Related

How to check if 2 values IS NULL

I have these tables...
GROUP_MEMBERS
+---------------------------------+
| id | group_id | member_id |
+---------------------------------+
| 1 | 1 | 1 |
| 2 | 1 | 4 |
+---------------------------------+
MEMBERS
+-------------------------------------+
| id | first | last | role_id |
+-------------------------------------+
| 1 | Jack | Jones | 1 |
| 2 | Jane | Doe | 2 |
| 3 | Bob | Bee | 2 |
| 4 | Jen | Nee | 1 |
+-------------------------------------+
GROUPS
+-----------------+
| id | name |
+-----------------+
| 1 | group1 |
| 2 | group2 |
+-----------------+
As it is, I am using the following query...
SELECT
(members.id) AS memid,
members.first,
members.last,
members.role_id
FROM
members
LEFT JOIN group_members ON
members.id = group_members.member_id
WHERE
group_members.member_id IS NULL
GROUP BY
members.id;
This outputs the members (Jane and Bob) who are not in the 'GROUP_MEMBERS' table as it should, but what I am trying get working is if I am on and another group ($_GET['group_id']), how can I show all members that do not have rows that match group_id and member_id on the 'GROUP_MEMBERS' table...
i.e if group_id = '2' show all members
I have tried adding in WHERE clause... AND group_members.group_id IS NULL.. but it shows nothing then.
Does anyone have a query which would get the output I'm looking for?
Thanks
[EDITED]
Just to clarify...
If my url had 'group_id=1'
I should see:
Bob
Jane
If my url has 'group_id=2'
I should see:
Jack
Jane
Bob
Jen
So it only shows 'members' that do not exist( with the 'group_id' in the url) in the 'GROUP_MEMBERS' table
If I have understood the question correctly, you are looking for something like I have made on this fiddle:
DB Fiddle
The query I use is:
$sql = 'SELECT * FROM groups
RIGHT JOIN group_members ON groups.id = group_id
RIGHT JOIN members ON member_id = members.id
WHERE group_id <> ? OR group_id is NULL;'
$group_id = $_GET['group_id'];
$query = $mysqli->prepare($sql);
$query->bind_param('i', $group_id);
In short, this query will select from the groups table, ensuring that we will select every group in your database.
Then we will join the other two tables completely (using the RIGHT JOIN).
Finally, we are going to select every member that isn't the specified the one provided by the URL, or any member that is not in a group.
You can use a sub-query
how can I show all members that do not have rows that match 'group_id'
$group_id= $_GET['group_id'];
$q = "SELECT * FROM MEMBERS WHERE MEMBERS.id NOT IN(
SELECT member_id FROM GROUP_MEMBERS WHERE group_id='$group_id'
);";
Explanation
SELECT member_id FROM GROUP_MEMBERS WHERE group_id='$grID'
this will get all the members in this group by a given ID
then you select all members that are not among them.
SELECT * FROM MEMBERS WHERE MEMBERS.id NOT IN()
this one will give members data except the ids inside the brackets
the sub query will get the ids of members in a given group
no need for joining the three tables since you are using id of the group existing in GROUP_MEMBERS and linking the GROUP and MEMBERS
one side note
if you have a group name and what all users not in this group you then will need to use the GROUPS table
SELECT * FROM MEMBERS WHERE MEMBERS.id NOT IN(
SELECT member_id FROM GROUP_MEMBERS WHERE group_id = (
SELECT id from GROUPS WHERE name = '$Group_Name'
)
);
you may use WHERE group_id IN (...) it will work the same
This is a demonstration, I created same database with same data and tested the queries
+----+-------+-------+---------+
| id | first | last | role_id |
+----+-------+-------+---------+
| 1 | Jack | Jones | 1 |
| 2 | Jane | Doe | 2 |
| 3 | Bob | Bee | 2 |
| 4 | Jen | Nee | 2 |
+----+-------+-------+---------+
+----+--------+
| id | name |
+----+--------+
| 1 | group1 |
| 2 | group2 |
+----+--------+
+----+----------+-----------+
| id | group_id | member_id |
+----+----------+-----------+
| 1 | 1 | 1 |
| 2 | 1 | 4 |
+----+----------+-----------+
I run the sub-query as above and the results as expected,
MariaDB []> select * from members where id not in
(select member_id from group_members where group_id = 1);
+----+-------+------+---------+
| id | first | last | role_id |
+----+-------+------+---------+
| 2 | Jane | Doe | 2 |
| 3 | Bob | Bee | 2 |
+----+-------+------+---------+
similar for when you have group name
MariaDB []> select * from members where id not in
(select member_id from group_members where group_id =
(select id from groups where name='group1'));
+----+-------+------+---------+
| id | first | last | role_id |
+----+-------+------+---------+
| 2 | Jane | Doe | 2 |
| 3 | Bob | Bee | 2 |
+----+-------+------+---------+

for each value of one table get and display count number of coresponding values from another table

unfortunately i have to do this in mysql / php . I looked for three days, and there is like 10.000 explantions of this but NONE (and I repeat NONE) works for me. I tried it all. I have to ask, sorry.
I have two tables - articles and control.
table "articles"
------------------
art_id | name |
------------------
1 | aaa |
2 | bbb |
3 | ccc |
4 | ddd |
table "control"
--------------------------------------------
con_id | art_id | data |
--------------------------------------------
1 | 1 | something-a |
2 | 2 | something-b |
3 | 1 | something-a |
4 | 2 | something-c |
5 | 3 | something-f |
art_id exists in both tables. Now what i wanted - for query:
"select * from articles order by art_id ASC" displayed in a table
to have also one cell displaying the count for each of art_id's from table CONTROL...
and so i tried join, left join, inner join - i get errors ... I also tried for each get only one result (for example 2 for everything)... this is semi-right but it displays the array of correct results and it's not even with join!!! :
$query = "SELECT art_id, count(*) as counting
FROM control GROUP BY art_id ORDER BY con_id ASC";
$result = mysql_query($query);
while($row=mysql_fetch_array($result)) {
echo $row['counting'];
}
this displays 221 -
-------------------------------------------------
art_id | name | count (this one from control) |
-------------------------------------------------
1 | aaa | 221 |
2 | bbb | 221 |
3 | ccc | 221 |
and it should be:
for art_id(value1)=2,
for art_id(2)=2,
for art_id(3)=1
it should be simple - like a count of values from CONTROL table displayed in query regarding the "articles" table...
The result query on page for table articles should be:
"select * from articles order by art_id ASC"
-------------------------------------------------
art_id | name | count (this one from control) |
-------------------------------------------------
1 | aaa | 2 |
2 | bbb | 2 |
3 | ccc | 1 |
So maybe i should go with JOIN or with join plus for each... Tried tha too, but then i'm not sure what is the proper thing to echo... all-in-all i'm completely lost here. Please help. Thank you.
So imagine this in two steps:
Get the counts per art_id from the control table
Using your articles table, pick up the counts from step 1
That will give you a query that looks like this:
SELECT a.art_id, a.name, b.control_count
FROM articles a
INNER JOIN
(
SELECT art_id, COUNT(*) AS control_count
FROM control
GROUP BY art_id
) b
ON a.art_id = b.art_id;
Which will give you the results you're looking for.
However, instead of using a subquery, you can do it all in one shot:
SELECT a.art_id, a.name, COUNT(b.art_id) AS control_count
FROM articles a
INNER JOIN control b
ON a.art_id = b.art_id
GROUP BY a.art_id, a.name;
SQL Fiddle demo
SELECT *, (SELECT COUNT(control.con_id) FROM control WHERE control.art_id = articles.art_id) AS count_from_con FROM articles ORDER BY art_id DESC;
If I understood your question right, this query should do the trick.
Edit: Created the tables you have described, and it works.
SELECT * FROM articles;
+--------+------+
| art_id | name |
+--------+------+
| 1 | aaa |
| 2 | bbb |
| 3 | ccc |
| 4 | ddd |
+--------+------+
4 rows in set (0.00 sec)
SELECT * FROM control;
+--------+--------+------+
| con_id | art_id | data |
+--------+--------+------+
| 1 | 1 | NULL |
| 2 | 2 | NULL |
| 3 | 1 | NULL |
| 4 | 2 | NULL |
| 5 | 3 | NULL |
+--------+--------+------+
5 rows in set (0.00 sec)
SELECT *, (SELECT COUNT(control.con_id) FROM control WHERE control.art_id = articles.art_id) AS count_from_con FROM articles ORDER BY art_id ASC;
+--------+------+----------------+
| art_id | name | count_from_con |
+--------+------+----------------+
| 1 | aaa | 2 |
| 2 | bbb | 2 |
| 3 | ccc | 1 |
| 4 | ddd | 0 |
+--------+------+----------------+
You haven't quite explained what you want to accomplish with the print out but here is an example in PHP: (Use PDO instead of mysql_)
$pdo = new PDO(); // Make your connection here
$stm = $pdo->query('SELECT *, (SELECT COUNT(control.con_id) FROM control WHERE control.art_id = articles.art_id) AS count_from_con FROM articles ORDER BY art_id ASC');
while( $row = $stm->fetch(PDO::FETCH_ASSOC) )
{
echo "Article with id: ".$row['art_id']. " has " .$row['count_from_con'].' connected rows in control.';
}
Alternatively with the mysql_ extension:
$result = mysql_query('SELECT *, (SELECT COUNT(control.con_id) FROM control WHERE control.art_id = articles.art_id) AS count_from_con FROM articles ORDER BY art_id ASC');
while( $row = mysql_fetch_assoc($result) )
{
echo "Article with id: ".$row['art_id']. " has " .$row['count_from_con'].' connected rows in control.';
}
This should be enough examples to help you accomplish what you need.

Mysql query comma separator

I having two tables
table 1: users
| id | username |
| 1 | john |
| 2 | marry |
| 3 | deep |
| 4 | query |
| 5 | value|
and
table 2:users_2
| table_2_id | user_id |
| 1 | 2,4 |
I need required something like this
| table_2_id | username |
| 1 | marry,query |
anyone can help me for this output in mysql
Is this what you are looking ?
select
`users_2`.`table_2_id` , GROUP_CONCAT(`users`.`username`) as `usernames`
from `users_2`
inner join `users` on FIND_IN_SET(`users`.`id`,`users_2`.`user_id`)
Check output here
http://sqlfiddle.com/#!2/c498bc/3
select a.table_2_id,b.username
from users b,users_2 a
where a.table_2_id=b.id
and b.id in(a.user_id)
group by a.table_2_id
First of all, you should not store a multiple value in a single field. For table users_2, the data should be:
table_2_id user_id
1 2
1 4
After you normalized your table, you can use mysql GROUP_CONCAT() to get the result in the format you mentioned
SELECT
users_2.table_2_id,
GROUP_CONCAT(users.username) AS username
FROM
users_2
JOIN
users ON users.id = users_2.user_id
GROUP BY
users_2.table_2_id
;

Selecting multiple tables MySQL and retrieving different data

I have 4 tables that I need to pull data from. I need to count how many people are signed for a single event and see if a user is applied for an event.
These are my table setups:
TABLE: users
+----+----------+-------+--------+-------+
| id | username | level | class | guild |
+----+----------+-------+--------+-------+
| 1 | example1 | 100 | Hunter | blah |
| 2 | example2 | 105 | Mage | blah2 |
| 3 | example3 | 102 | Healer | blah |
+----+----------+-------+--------+-------+
ID is primary
TABLE: event_randoms
+----+----------+-------+--------+----------+----------+
| id | username | level | class | apped_by | event_id |
+----+----------+-------+--------+----------+----------+
| 1 | random1 | 153 | Hunter | 3 | 3 |
| 2 | random2 | 158 | Healer | 3 | 1 |
| 3 | random3 | 167 | Warrior| 1 | 3 |
+----+----------+-------+--------+----------+----------+
ID is primary
apped_by should be foreign key to users.id
event_id should be foreign key to events.id
TABLE: events
+----+------------+------------+-----------+-----------+-----------+
| id | event_name | event_date | initiator | min_level | max_level |
+----+------------+------------+-----------+-----------+-----------+
| 1 | event1 | date1 | 1 | 100 | 120 |
| 2 | event2 | date2 | 1 | 121 | 135 |
| 3 | event3 | date3 | 1 | 100 | 120 |
| 4 | event4 | date4 | 1 | 150 | 200 |
+----+------------+------------+-----------+-----------+-----------+
ID is primary
TABLE: event_apps
+----+----------+--------------+
| id | event_id | applicant_id |
+----+----------+--------------+
| 1 | 3 | 2 |
| 2 | 4 | 2 |
| 3 | 3 | 1 |
| 4 | 1 | 3 |
+----+----------+--------------+
ID is primary
event_id should be foreign key to events.id
applicant_id should be foreign key to users.id
I will be the first to admit that I am very new to this. I just learned how to use MySQL a few days ago. I can grab stuff from a single table, but I am unsure how to grab from multiple tables.
This is the SQL query I tried
SELECT DD_events.id, event_id, applicant_id, guild, level, class, DD_users.id
FROM DD_events, DD_event_apps, DD_users
WHERE DD_event_apps.event_id = DD_events.id
AND DD_event_apps.applicant_id = DD_users.id
and tried to print_r an array but the array turns up empty.
So a few questions pertain to this:
1: How would I count and display as a number how many people (users and randoms) are signed up for an event?
eg: event 3 should have 4 total (2 users and 2 randoms)
2: How do I see if a particular individual is signed for an event and display text based if they are or not?
eg: user 1 is signed up for event 3 so it would be "Registered" but user 2, who is not signed, would display "Not Registered"
3: I want to display info for who is signed for a particular event in 2 tables, 1 for users and another for randoms.
eg: Event 3 would have 2 users info (username, guild, class, level) under the users table and then 2 random users info (name, class, level, what user applied this person) in the random table.
Any and all help is appreciated even if you can answer 1 part.
I'm thinking this would be your base query:
SELECT
event.id,
app.applicant_id,
usr.guild,
usr.level,
usr.class,
usr.id AS Userid
FROM
DD_events event
JOIN
DD_event_apps app
ON (event.id = app.event_id)
LEFT JOIN
DD_users usr
ON (app.user_id = usr.id)
You can make modifications to this to aggregate it, like so:
SELECT
event.id,
COUNT(app.applicant_id) AS ApplicantCount,
COUNT(DISTINCT usr.guild) AS UniqueGuilds,
COUNT(DISTINCT usr.level) AS UniqueLevels,
COUNT(DISTINCT usr.class) AS UniqueClasses,
COUNT(DISTINCT usr.id) AS UniqueUsers
FROM
DD_events event
JOIN
DD_event_apps app
ON (event.id = app.event_id)
LEFT JOIN
DD_users usr
ON (app.user_id = usr.id)
GROUP BY
event.id
I could write those scripts for you, but I think this provides a good starting point for you to continue from. You'll find that T-SQL is fairly simple when you are trying to get the results you are looking for. Hope this helps!
<?php $query = "SELECT count(*) AS numbuh FROM DD_event_apps WHERE event_id = {$row['id']}";
try
{
// These two statements run the query against your database table.
$stmt = $db->prepare($query);
$stmt->execute();
}
catch(PDOException $ex)
{
// Note: On a production website, you should not output $ex->getMessage().
// It may provide an attacker with helpful information about your code.
die("Failed to run query: " . $ex->getMessage());
}
echo($query);
// Finally, we can retrieve all of the found rows into an array using fetchAll
$count = $stmt->fetchAll();
echo($count['numbuh']); ?>

mysql group by table b, inner join table a for a random

here is my 2 tables, id for a inner join event. i want to do this thing:
in table b, there has 10 albums, i want random get out 4 albums. then each album select one record, the record is random position in the album.
so that i will get 4 records back (these 4 records with no duplicate id), then take these 4 records for a inner join query, to get the title from table a.
here just little records just for test. in the fact, i have 300,000 records in table a and 2,000,000 records in table b.
table a
+-----+-------+
| id | title |
+-----+-------+
| 1 | a1 |
+-----+-------+
| 2 | a2 |
+-----+-------+
| 3 | a3 |
+-----+-------+
| 4 | a4 |
+-----+-------+
| 5 | a5 |
+-----+-------+
| 6 | a6 |
+-----+-------+
table b
+-----+--------+
| id | album |
+-----+--------+
| 1 | album1 |
+-----+--------+
| 2 | album1 |
+-----+--------+
| 3 | album1 |
+-----+--------+
| 6 | album1 |
+-----+--------+
| 2 | album2 |
+-----+--------+
| 3 | album2 |
+-----+--------+
| 5 | album3 |
+-----+--------+
| 6 | album3 |
+-----+--------+
| 3 | album4 |
+-----+--------+
| 2 | album5 |
+-----+--------+
| 4 | album5 |
+-----+--------+
| 5 | album5 |
+-----+--------+
| 1 | album6 |
+-----+--------+
| 3 | album6 |
+-----+--------+
| 2 | album7 |
+-----+--------+
| 4 | album7 |
+-----+--------+
| 1 | album8 |
+-----+--------+
| 5 | album8 |
+-----+--------+
| 3 | album9 |
+-----+--------+
| 2 | album10|
+-----+--------+
| 5 | album10|
+-----+--------+
I am not good at mysql query. In my mind I would do
select * from b group by album order by random() limit 0,4
get back 4 album, then do a inner join query (this query not correct, how to check the b.id no duplicate?)
select * from b inner join a on b.id = a.id where (select id from b where b.album = '".$row['album']."' order by random() limit 1)
I need an easy and quicker method, the best is just use one query. many thanks.
AFAIR, "ORDER BY RAND()" is extremely slow solutions, especially on tables like you have (2 million+ records), so I'd recommend looking at something similar to these kind of articles first: http://www.greggdev.com/web/articles.php?id=6
So, you should know the number of records in your table before running the query and then do something like:
"SELECT * FROM `album` LIMIT 1 OFFSET " . rand(0,$count)
This will return you 1 random row a bit more efficiently, I believe.
Also, I think it's not a good idea to store album references as string in tracks table, you'd rather use a proper integer foreign key album_id referenced to albums.id. Then you can join both tables much fatser. If I were you, I'd do first:
ALTER TABLE `tracks` add column `album_id` int;
UPDATE `tracks` SET `album_id` = SUBSTRING(`album`,5);
Then, after doing this and combining with the solution above, launch something like:
"SELECT * FROM `album` INNER JOIN `tracks`ON `tracks`.`album_id` = `albums`.`id` LIMIT 1 OFFSET " . rand(0,$count)
Since I'm neither an expert on MySQL nor on PHP, I'll try with pseudocode and generic SQL. I have renamed your tables to albums and tracks for sake of readability.
First fetch the four random records to your PHP application:
select id from albums order by random() limit 4
Second, iterate over the resulting result set of four IDs and fetch the corresponding tracks (pseudo-php):
foreach($album_ids as $id):
execute_query("select id from tracks where album_id = ? order by random(), limit 1", $id)
It is not obvious to me how you match your tracks to their albums. You should have something like tracks.album_id as a foreign key to albums.id, that's how I designed my queries. You should adapt as appropriate, the underlying logic behind my solution should remain the same.

Categories