sql - how to get courses with module=5 - mysql

I'm trying to get all courses which has only "module=5", any different course. As you can see below I have 4 courses, such as, course 2, 3, 4, 5. But just two of them has only "module=5", e.g. course 2, 5.
+----+--------+--------+
| id | course | module |
+----+--------+--------+
| 1 | 2 | 5 |
| 2 | 3 | 5 |
| 3 | 3 | 11 |
| 4 | 4 | 5 |
| 5 | 4 | 3 |
| 6 | 5 | 5 |
| 7 | 4 | 6 |
| 8 | 4 | 5 |
+----+--------+--------+
I've tryed do two queries, in the first I return all courses which has module=5 and in the second I return all courses which has module!=5, then I save in 2 files and execute the unix command diff to see the difference between both files.
save in a file all courses which has module=5:
SELECT DISTINCT fullname
FROM mdl_course
LEFT JOIN mdl_course_modules
ON mdl_course.id=mdl_course_modules.course
WHERE mdl_course_modules.module=5
into outfile '/tmp/forum';
save in file all courses which has module!=5:
SELECT DISTINCT fullname
FROM mdl_course
LEFT JOIN mdl_course_modules
ON mdl_course.id=mdl_course_modules.course
WHERE mdl_course_modules.module!=5
into outfile '/tmp/plus_forum';
Then, execute the difference:
$ diff forum plus_forum
But I'd like to return all courses which has only module=5 in only one query. Is it possible?

To make it simple let just solve the mdl_course_modules
SELECT course
FROM mdl_course_modules
GROUP BY course
HAVING SUM(module <> 5) = 0
AND SUM(module = 5) = 1 -- or >= 1

You can use a not in subquery to do this:
SELECT DISTINCT fullname
FROM mdl_course
LEFT JOIN mdl_course_modules
ON mdl_course.id=mdl_course_modules.course
WHERE mdl_course_modules.module=5
AND course.id not in (SELECT course
FROM mdl_course_modules
WHERE module <> 5)

One way to do this is to use the exists predicate with a correlated subquery.
select *
from mdl_course_modules t
where not exists (
select 1
from mdl_course_modules
where t.course = course -- reference the outer table
and module <> 5 -- and find rows that have any other module than 5
)
Sample SQL Fiddle
Since it's not completely clear what data is in what table you'll have to adjust the table and column names to suit your setup, but the concept should be clear.

Related

Mysql select rows with same id's (3 tables)

I have the following tables:
'blog_content'
'blog_media'
'blog_media_content'
| blog_id | media_id |
========================
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 3 | 4 |
I want to select all blog_media.uri's where blog_media.media_id equals blog_media_content.blog_id.
Please help me to achieve my aim.
An inner join between blog_media and blog_media_content tables would suffice.
SELECT
bm.uri
FROM blog_media bm
INNER JOIN blog_media_content bmc ON bm.media_id = bmc.media_id
WHERE bmc.blog_id =3;
Note:
If you need any additional information from blog table then you need an additional inner join like below:
...INNER JOIN blog_table b ON bmc.blog_id = b.blog_id...
EDIT:
In order to get records for all blog_ids :
SELECT
bm.uri
FROM blog_media bm
INNER JOIN blog_media_content bmc ON bm.media_id = bmc.media_id
ORDER BY bmc.blog_id;

Optimization SQL for getting data from two joined tables (usernames for user-from-id and user-to-id msgs from two tables)

I have table "msgs" with messages between users (their ids):
+--------+-------------+------------+---------+---------+
| msg_id |user_from_id | user_to_id | message | room_id |
+--------+-------------+------------+---------+---------+
| 1 | 1 | 4 |Hello! | 2 |
| 2 | 1 | 5 |Hi there | 1 |
| 3 | 2 | 1 |CU soon | 2 |
| 4 | 3 | 7 |nice... | 1 |
+--------+-------------+------------+---------+---------+
I also have two tables with users names.
Table: user1
+--------+----------+
|user_id |user_name |
+--------+----------+
| 5 | Ann |
| 6 | Sam |
| 7 | Michael |
+--------+----------+
Table: user2
+--------+----------+
|user_id |user_name |
+--------+----------+
| 1 | John |
| 2 | Alice |
| 3 | Tom |
| 4 | Jane |
+--------+----------+
I need to get usernames for two users IDs in every row. Every user-id can be in first or second table with usernames.
I wrote this SQL query:
SELECT DISTINCT
m.msg_id,
m.user_from_id,
CASE WHEN c1.user_name IS NULL THEN c3.user_name ELSE c1.user_name END AS from_name,
m.user_to_id,
CASE WHEN c2.user_name IS NULL THEN c4.user_name ELSE c2.user_name END AS to_name,
m.message
FROM msgs m
LEFT JOIN users1 c1 ON c1.user_id=m.user_from_id
LEFT JOIN users1 c2 ON c2.user_id=m.user_to_id
LEFT JOIN users2 c3 ON c3.user_id=m.user_from_id
LEFT JOIN users2 c4 ON c4.user_id=m.user_to_id
WHERE m.room_id=1
LIMIT 0, 8
It works.
Execute query to get raw data without usernames (without any join) tooks about ~0.1 sec. But it's enough to join only one usernames table (user1 or user2 only) to get this data in about ~6.2 sec. (with join one table). I have quite a lot rows in this tables: 35K rows in msgs, 0.5K in user1, 25K in user2.
Executing query with join two tables (with all this data) is impossible.
How to optimize this query? I just need usernames for user_ids in first "msgs" table.
There are potentially many differences between the queries with and without the joins. I am going to assume that the ids have the appropriate indexes -- primary keys automatically do. If not, then check that.
The obvious solution is to use the original query as a subquery:
SELECT m.msg_id, m.user_from_id,
(CASE WHEN c1.user_name IS NULL THEN c3.user_name ELSE c1.user_name
END) AS from_name,
m.user_to_id,
(CASE WHEN c2.user_name IS NULL THEN c4.user_name ELSE c2.user_name
END) AS to_name,
m.message
FROM (SELECT m.*
FROM msgs m
WHERE m.room_id = 1
LIMIT 0, 8
) m LEFT JOIN
users1 c1
ON c1.user_id = m.user_from_id LEFT JOIN
users1 c2
ON c2.user_id = m.user_to_id LEFT JOIN
users2 c3
ON c3.user_id = m.user_from_id LEFT JOIN
users2 c4
ON c4.user_id = m.user_to_id;
For most data structures, the distinct is also unnecessary.
This also makes (the reasonable assumption) that user_id is unique in the users tables.
Also, use of LIMIT without ORDER BY is highly discouraged. The particular rows you get are indeterminate and might change from one execution to the next.

query which creates missing rows based on anther table

I have many forms that users fill out. Each form contains a list of questions. In this first table is the form id and the id's of the questions.
form_id | question_id
1 | 1
1 | 2
1 | 3
2 | 4
2 | 5
This table has two forms one which has 3 questions and the other 2. I have a second table which has the answers that the users have given for the questions.
user_id | form_id | question_id | answer
476 | 1 | 1 | "answer1"
476 | 1 | 3 | "answer2"
693 | 1 | 1 | "answer3"
693 | 1 | 2 | "answer4"
235 | 2 | 5 | "answer5"
In this example, 2 users have filled out form 1 and 1 user has filled in form 2. But none have filled in all the questions. Is it possible to write a query which combines the two tables and will give me the answers that the user have given including the questions that they didn't answer? I'd like the results to look like this.
user_id | form_id | question_id | answer
476 | 1 | 1 | "answer1"
476 | 1 | 2 | NULL
476 | 1 | 3 | "answer2"
693 | 1 | 1 | "answer3"
693 | 1 | 2 | "answer4"
693 | 1 | 3 | NULL
235 | 2 | 4 | NULL
235 | 2 | 5 | "answer5"
The problem that I have when I use a left join like this
select * from template t
left join answers a on a.template_id = t.template_id
AND a.question_id = t.question_id
AND t.template_id = t.template_id;
is that the row that results is missing user_id.
Yes, the specified result can be returned by a query.
One way to achieve this is a join to an inline view, and an "outer join" operation to the second table.
The "trick" is getting a distinct list of user_id and form_id from the second table, using a query, for example:
SELECT user_id, form_id
FROM second_table
GROUP BY user_id, form_id
And then using that query as an inline view (wrapping it in parens, assigning a table alias, and referencing it like it was a table in an outer query.
All that's required after that is an "outer join" to the second table.
For example:
SELECT r.user_id
, q.form_id
, q.question_id
, a.answer
FROM first_table q
JOIN ( SELECT p.user_id, p.form_id
FROM second_table p
GROUP BY p.user_id, p.form_id
) r
ON r.form_id = q.form_id
LEFT
JOIN second_table a
ON a.user_id = r.user_id
AND a.form_id = r.form_id
AND a.question_id = q.question_id
ORDER
BY r.user_id
, q.form_id
, q.question_id
Note that the keyword "LEFT" specifies an outer join operation, returning all rows from the left side, along with matching rows from the right side. A typical "inner" join would exclude rows that didn't find a matching row from the table on the right side.
use
left join
something like:
select * from table1 left join table2 on table1.form_id= table2.form_id

MySQL Filtering rows from three tables

Let's say i've got this database:
book
| idBook | name |
|--------|----------|
| 1 |Book#1 |
category
| idCateg| category |
|--------|----------|
| 1 |Adventures|
| 2 |Science F.|
book_categ
| id | idBook | idCateg | DATA |
|--------|--------|----------|--------|
| 1 | 1 | 1 | (null) |
| 2 | 1 | 2 | (null) |
I'm trying to select only the books which are in category 1 AND category 2
This is what I've got so far:
SELECT book.* FROM book,book_categ
WHERE book_categ.idCateg = 1 AND book_categ.idCateg = 2
Obviously, this giving 0 results becouse each row has only one idCateg it does work width OR but the results are not what I need. I've also tried to use a join, but I just can't get the results I expect.
Here it's the SQLFiddle of my current project, the data at the begining is just a sample.
SQLFiddle
Any help will be really appreciated.
You could double join with a constraint on the category id:
SELECT a.* FROM book AS a
INNER JOIN book_categ AS b ON a.idBook = b.idBook AND b.idCateg = 1
INNER JOIN book_categ AS c ON a.idBook = c.idBook AND c.idCateg = 2
You could use a subquery:
SELECT a.* FROM book AS a
WHERE
(SELECT COUNT(DISTINCT idCateg) FROM book_categ AS b
WHERE b.idBook = a.idBook AND b.idCateg IN (1,2)) = 2
If you are on MySQL as your fiddle implies, you should prefer the join variant, since most joins are much faster in MySQL than subqueries.
edit
This one should also work:
SELECT a.* FROM book a
INNER JOIN book_categ AS b ON a.idBook = b.idCateg
WHERE b.idCateg IN (5, 6)
GROUP BY idBook
HAVING COUNT(DISTINCT b.idCateg) = 2
and should be faster than the two above, although you have to change the last number according to the number of category ids you are requesting.

Finding cooccuring values in MYSQL weak relation table

I have a weak relation table, called header, it is basically just three ID's: id is an autoincrement primary key, did points to the id of table D and hid points to the id of table H. D and H are irrelevant here.
I want to find for any value of hid, the other values of hid that shares did with the original hid. An example:
id | did | hid
===============
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 2 | 1
5 | 2 | 4
6 | 2 | 5
7 | 3 | 2
8 | 3 | 6
For hid = 1 I would thus like to find id = {2,3,5,6} as those are the rows that have did in common with hid = 1.
I can do this by creating some arrays in PHP and running through all possible values of hid and respective did, but this is a quite slow process for large tables. I was wondering if there is a clever kind of JOIN or similar statement that could be used to find the cooccuring values of hid.
If I have understood you correctly:-
SELECT a.hid, GROUP_CONCAT(b.id)
FROM header a
INNER JOIN header b
ON a.did = b.did
AND b.hid != 1
WHERE a.hid = 1
GROUP BY a.hid
SQL fiddle:-
http://www.sqlfiddle.com/#!2/9aa26/1
Maybe this:
SELECT d.id
FROM (
SELECT *
FROM header
WHERE header.hid =1
) AS h
JOIN header AS d ON d.did = h.did
WHERE d.hid !=1