Retrieving groupings from many-to-many tables - php

This is kind of hard to explain but I'll do what I can.
I have 2 tables with a many-to-many relationship; there is a linking table defining the relationship. These tables are named Question, QuestionTopic and Topic. Topic has the fields TopicID and TopicName.Question has the fields QuestionID and QuestionText.
I want to retrieve a listing of the topics and the number of questions belonging to that topic. However topics could be grouped together and the count of questions unique to that combination should be known. As an example:
Topic(s) | Count
Topic1,Topic2 | 10
Topic1 | 3
Topic2 | 2
The above implies there are 3 questions unique to topic1 and 10 which have the topics Topic1 and Topic2. The remaining 2 questions have topic2.
I'm using MySQL and PHP. Thanks.

"Cheating" solution, using GROUP_CONCAT(). This will not show the count of questions that are not related to any topic:
SELECT
TopicIds
, COUNT(*) AS QuestionCount
FROM
( SELECT
QuestionId
, GROUP_CONCAT(TopicId ORDER BY TopicId) AS Topics
FROM
QuestionTopic
GROUP BY
QuestionId
) AS grp
GROUP BY
Topics

did u try GROUP_CONCAT?
http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat
then, u can group by topic id.

GROUP_CONCAT as mentioned above will work. But on the other side as I see your database structure does not really fit to your task. Looks like it too normalized and you need to do some de-normalization and migration.
Since you have groups of topics I suggest you create two more tables:
1) TopicGroups: Group | Topic - to list all unique combinations ("groups") of topics in questions.
2) GroupQuestions: Group | Question - to relate question to group of topics it covers
Then the solution for your task will be simple group by query on GroupQuestions.

Related

Select all from a table ORDER BY another tables column, with match primary key [duplicate]

This question already has answers here:
PHP/MySQL Order by column in a different table
(2 answers)
Closed 5 years ago.
I've been looking for a while but the query I am trying to accomplish seems fairly hard to find any information or documentation on how to do what I am trying to do.
I have two tables, one of them stores my user accounts and basic information. I then have a second table that holds a little more information about the user.
Both of these tables have primary keys (table one is id and table two is user_id) which I use to know who is who and match records between both tables.
What I am trying to do today is I want to get 10 records from table one, order by a column in table two (room_count) DESC.
Table #1's name is "users" and Table #2's name is "user_information".
What have I tried?
I'm not really sure where to start so I haven't tried anything yet.
How would I got about doing something like this?
Thank you to any answers posted.
For example, let's say I have 4 users, I'll write the username followed by the room_count column in the other table below.
Adam Sandler : 4
Jenny Hang : 9
Peter Foreign : 0
If I was to use the query with ASC it would start with Peter Foreign and end with Jenny Hang
Don't you just need a simple join?
SELECT
FROM users
INNER JOIN user_information ON users.id = user_information.user_id
ORDER BY user_information.room_count DESC
LIMIT 2
Please try this:
SELECT *
FROM users u
INNER JOIN user_info ui
ON u.id = ui.user_id
ORDER BY ui.room_count DESC
LIMIT 10
Try something basic like
select users.*
from
users, user_information
where
users.id =user_information.user_id
order by
user_information.room_count
desc
Limit 10
Edit: changed select users.id to select users.* to better fit the question asked.

MySQL Selecting from two tables ordered by date

Beginner here so please go easy on me :)
So I have these two tables in my DB
Reply Table
+------------------------------------------------+
| message_id | client_id | message | date_posted |
+------------------------------------------------+
Request Table (Exactly the same)
+------------------------------------------------+
| message_id | client_id | message | date_posted |
+------------------------------------------------+
Problem:
They serve a messaging app I was testing but now I don't know how to query these tables to get all chat ordered by date from two tables. For example
Client 14 (2 hours ago): Hello there // Coming from request table
Admin (1 hour ago): Welcome // Coming from reply table
So the messages are displayed oldest first...
I tried using JOIN on clien_id since that is what I want. However, it doesn't seem to work.
I also tried selecting from a subquery containing UNION ALL, also no luck... Any ideas on how this can be done? Thanks in advance!
A union is what you're looking for. In your case, a join would combine columns from the two tables into a single row, where as you're looking to union rows from multiple tables into a single result set.
You'll want to enclose your select statements individually, and then add the order clause.
Edit: Updating this answer to include a column for the source table, as per OP's comment
(select source='reply_table', * from reply_table)
union
(select source='request_table', * from request_table)
order by date_posted desc
MySQL's docs are pretty good, and its page on unions outlines several sorting scenarios: https://dev.mysql.com/doc/refman/5.7/en/union.html
But the instruction specific to your case is:
To use an ORDER BY or LIMIT clause to sort or limit the entire UNION result, parenthesize the individual SELECT statements and place the ORDER BY or LIMIT after the last one.
select a.message
from table1 a
inner join
table2 b
on a.client_id=b.client_id
order by a.date_posted desc;

avoid MySQL query from eating unnecessary bandwidth

I am concerned about optimizing my queries as far as the amount of data that is sent back from the database after doing a query.
Let's say that I have 2 tables. One called "artists" and another called "albums".
Let's say "artists" table columns are: id and name
while "albums" table columns are: id, artist_id, title
Let's say that I want a page to diplay the artist's name as the heading. And then below that, I want to display a list of the artist's albums.
I can get that done easily by doing something like:
SELECT artists.name AS artist_name, albums.title AS album_title
LEFT JOIN albums
ON albums.artist_id = artists.id
WHERE artists.id = 3;
This would give me a result that could look something like:
artist_name | album_title
|
Justin Bieber | My First Crappy Album
Justin Bieber | Another Crappy Album
Justin Bieber | Yet Another Crappy Album
The problem with this result is that it gives me back the artist name multiple times, when I only really need it once. I am concerned that this way of doing things is not very efficient. Especially if the artist already has plenty of albums. I could be wrong about this, and if I am, someone please correct me.
In any case, would there be a better way of doing this? One where I don't have to retrieve the artist name multiple times?
You can use the aggregate functions.
SELECT ar.name AS artist_name, GROUP_CONCAT(al.title) AS album_titles
FROM artists ar, albums al
WHERE ar.id = al.artist_id
AND ar.id = 3
GROUP BY artist_name;
This should give you something like:
artist_name | album_titles
Justin Bieber | Album1, Album2, Album3
I haven't used this command in a while, but you can find more documentation on it here: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_group-concat
Also, I prefer to list my tables in the FROM clause and use the WHERE clause to join them, but it's the same as your JOIN above... just a different syntax.
Then do two seperate requests.
First request the artist name
SELECT name
FROM artists
WHERE id = 3
Then the album titles..
SELECT title
FROM albums
WHERE artist_id = 3
I don't know about any bandwith logging inside php, but you can calculate witch way is the fastest in executing using microtime()

Find videos which are connected to several categories (1:N Relation) mysql

I have to select all videos, which are related (1:N Relation) to many categories.
But finally I only want these videos, which are related to all given categories.
For example:
Request: cat[]=5&cat[]=6
Results: All videos, which are in category 5 AND 6
In fact I know how to handle this within 2 given categories (IN Statement, WHERE Exists etc). But we want to do it as dynamically as possible. Which means, that a customer is able to select about 30 categories at a time.
I see a little bit of a performance issue in that case.
Currently I am handling these issue in PHP where I build several arrays, to validate each selected video (which results of an IN select) against each selected category.
But of course I have other problems with that. For example the pagination, limits etc. A lot of overhead I think.
My other idea was to create a cache_table, where the categories are concatenated and stored in an additional column which I would query with a INSTR or FULLTEXT search.
Does anybody know better ways to handle such a Query?
I think you should create a table with possible categories, a table with the videos, and a table which links the 2.
Categories Videos VideoCategories
1 cats 1 Cat being lazy 1 1
2 dogs 2 Dog barking 2 2
3 Dog chasing Cat 3 1
3 2
If you index those tables a query like the following is quite fast:
Select * from videos
where videoId in
( select videoId, count(*) numberOfMatches
from VideoCategories
where categoryid in (1,2)
group by videoId
having numberOfMatches >= 2 )
I don't claim this is the fastest solution, but i think its a good way to get started, and its definitely faster than a fulltext search
Since IN ( SELECT ... ) is very poorly optimized, turn it into a JOIN:
Select b.*
from videos b
JOIN
( SELECT videoId, count(*) numberOfMatches
from VideoCategories
where categoryid in (5,6)
group by videoId
having numberOfMatches >= 2
) a USING videoId;

Mysql conditions with grouping many-to-many tables

I was wondering if somebody can think of a more elegant solutions to my problem. I have trouble finding similar cases.
I have 5 tables. 3 are details for employees, skills and subskills. The remaining 2 are linking tables.
skill_links
skill_id subskill_id
1 4
1 5
2 4
2 6
emp_skill_links
employee_id subskill_id acquired
1 4 2013-04-05 00:00:00
1 5 2014-02-24 00:00:00
2 6 2012-02-26 00:00:00
2 5 2011-06-14 00:00:00
Both have many-to-many relations. Skills with subskills (skill_links) and employees with subskills (emp_skill_links).
I want to pick employees who have acquired all subskills for a skill. I tried doing it with one query, but couldn't manage it with the grouping involved. At the moment my solution is two separate queries and matching these in php array later. That is:
SELECT sl.skill_id, COUNT(sl.subskill_id) as expected
FROM skill_links sl
GROUP BY sl.skill_id
to be compared with:
SELECT sl.skill_id, esl.employee_id, COUNT(esl.subskill_id) as provided
FROM emp_skill_links esl
INNER JOIN skill_links sl
ON sl.subskill_id = esl.subskill_id
GROUP BY sl.skill_id, esl.employee_id
Is there a more efficient single query solution to my problem? Or would it not be worth the complexity involved?
If you consider a query consisting of sub-queries as meeting your requirement for "a more efficient single query solution" (depends on your definition of "single query"), then this will work.
SELECT employeeTable.employee_id
FROM
(SELECT sl.skill_id, COUNT(*) AS subskill_count
FROM skill_links sl
GROUP BY sl.skill_id) skillTable
JOIN
(SELECT esl.employee_id, sl2.skill_id, COUNT(*) AS employee_subskills
FROM emp_skill_links esl
JOIN skill_links sl2 ON esl.subskill_id = sl2.subskill_id
GROUP BY esl.employee_id, sl2.skill_id) employeeTable
ON skillTable.skill_id = employeeTable.skill_id
WHERE employeeTable.employee_subskills = skillTable.subskill_count
What the query does:
Select the count of sub-skills for each skill
Select the count of sub-skills for each employee for each main skill
Join those results based on the main skill
Select the employees from that who have a sub-skill count equal to
the count of sub-skills for the main skill
DEMO
In the is example, users 1 and 3 each have all sub-skills of main skill 1. User 2 only has 2 of the 3 sub-skills of main skill 2.
You'll note that the logic here is similar to what you're already doing, but it has the advantage of just one db request (instead of two) and it doesn't involve the PHP work of creating, looping through, comparing, and reducing arrays.

Categories