I have tables like this
mainTable
Id | name | country
1 | John | 5
2 | Bill | 7
categoriesTable
other_table_id | category
1 | 6
1 | 12
My question is how can I say
SELECT id FROM mainTable
WHERE country=5
AND WHERE categoriesTable order_table_id=[**THE ID I JUST GOT FROM THE FIRST TABLE**] && category=6 || category=12
Then returns the number of records that match so in this case 1
Thanks!
Doesn't anyone learn how to write JOINs when they learn SQL?
SELECT m.id
FROM mainTable AS m
JOIN categoriesTable AS c ON c.other_table_id = m.id
WHERE c.category IN (6, 12)
AND m.country = 5
Related
I have been studying and learning PHP and MySQL and I have started a system that I'm developing for a friend's little school and to help me to improve my learning. I basically have in this case a table with the names of the students (tb_std) and another with the names of the teachers (tb_tch). The work is to distribute these students among the teachers in a new table, which is the way I think it will work better (tb_final).
I basically need each student to have a randomly chosen teacher so that the distribution is numerically even among the teachers.
In this example, I have 7 teachers and 44 students. Using SELECT query I did the operations to find out how many students would be for each teacher (add/division/mod), but how to make this draw to play in this new table I have no idea where to start.
1) Students (tb_std)
---------
id |std
1 | A1
2 | A2
3 | A3
(...)
44 | A44
2) Teachers (tb_tch)
--------
id |tch
--------
1 | T1
2 | T2
3 | T3
(...)
7 | T7
3) Final Combo (tb_final)
--------------------
id| student | teacher
--------------------
1 | A1 | T1
2 | A2 | T2
3 | A3 | T4
4 | A4 | T6
5 | A5 | T2
(...)
This is what I her in code until now:
<?php
session_start();
include_once("../conn/conexao.php");
$str_std="SELECT COUNT(*) as tstd FROM tb_std ";
$qry_std=mysqli_query($conn,$str_std);
while($res1=mysqli_fetch_assoc($qry_std)){
$v1 = $res1['tstd']; // 44 students
echo "Students: ".$res1['tstd']."<br>";
}
$str_tch="SELECT COUNT(*) as ttch FROM tb_tch ";
$qry_tch=mysqli_query($conn,$str_tch);
while($res2=mysqli_fetch_assoc($qry_tch)){
$v2 = $res2['ttch']; // 7 teachers
echo "Teachers ".$res2['ttch']."<br><br>";
}
$div = number_format($v1 / $v2);
$res = $v1 % $v2;
echo "Stud/Teach: ".$div."<br>"; // 6 std per teach
echo "Mod: ".$res."<br>"; // mod 2
?>
I have found some discussions including instructions CREATE and UNION, but I couldn't find other discussions around here that would help me with this division and distribution between tables to generate this new result.
You can solve this by next (a bit a complicate) query using window functions:
select
s.id, std student, tch teacher
from (
-- get Students with row_numbers
select
id,
row_number() over w as rn,
std
from tb_std
window w as (order by id)
) s
cross join (
-- get Teachers count and join to each student row
select count(*) tcnt from tb_tch
) tcnt
join (
-- select Teachers and join them to students depends row_number
-- divided to teachers count
select
row_number() over w as trn,
tch
from tb_tch
window w as (order by id)
) t on mod(s.rn, tcnt) = t.trn -1;
MySQL 8.0 fiddle
Result:
+====+=========+=========+
| id | student | teacher |
+====+=========+=========+
| 1 | S0 | T2 |
+----+---------+---------+
| 2 | S1 | T3 |
+----+---------+---------+
.........................
+----+---------+---------+
| 20 | S19 | T7 |
+----+---------+---------+
| 21 | S20 | T1 |
+----+---------+---------+
| 22 | S21 | T2 |
+----+---------+---------+
I work with PHP and PDO.
So I have 2 tables like,
Table 1
| id | name | age |
| 1 | John | 25 |
| 2 | Tom | 32 |
| 3 | James| 45 |
Table 2
| id | Comment | Link |
| 1 | some text | 3 |
| 2 | some text | 3 |
| 3 | some text | 1 |
So, Link column numbers represent id's in table1. For example Link = 3s in table 2 represent James in table 1. I need a query which brings all table1's data and also a number of repeated value for related Link column which comes from table2.
For example, the query should give me (let's choose James),
| id | name | age | Value |
| 3 | James | 45 | 2 |
value=2, because there are two 3s in link column which related to James
I tried somethings but got lots of errors.
I think you just need the GROUP BY
SELECT a.id,
a.name,
a.age,
count(*) as value
FROM table1 a
JOIN table2 b ON a.id = b.link
GROUP BY a.id, a.name, a.age
If you really want just one row then add WHERE
SELECT a.id,
a.name,
a.age,
count(*) as value
FROM table1 a
JOIN table2 b ON a.id = b.link
WHERE a.name = 'James'
GROUP BY a.id, a.name, a.age
or use subquery
SELECT a.id,
a.name,
a.age,
(SELECT count(*) FROM table2 b WHERE a.id = b.link) as value
FROM table1 a
WHERE a.name = 'James'
[TABLE 1]
+---------+---------+------------------+
| post_id | user_id | description |
+---------+---------+------------------+
| 1 | 1 | Sample post 1 |
| 2 | 1 | Sample post 2 |
| 3 | 2 | Sample post 3 |
| 4 | 2 | Sample post 4 |
| 5 | 3 | Sample post 5 |
+---------+---------+------------------+
[TABLE 2]
+---------+---------+---------+
| id | user_id | post_id |
+---------+---------+---------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
+---------+---------+---------+
When the USER_ID 1 in TABLE 1 already existed in TABLE 2 with its respected POST_ID, it should not be part of return select query. Instead, it would return POST_ID 2,3,4,5 in TABLE 1.
When the USER_ID 2 in TABLE 1 already existed in TABLE 2 with its respected POST_ID, the expected return select query would be POST_ID 1,3,4,5 in TABLE 1 as well as the other id's.
Thanks in advance guys! :)
Looks like an anti join pattern could achieve the specified result.
SELECT t.post_id
, t.user_id
, t.description
FROM `[Table 1]` t
LEFT
JOIN `[Table 2]` u
ON u.post_id = t.post_id
AND u.user_id = ?
WHERE u.post_id IS NULL
ORDER BY t.post_id
The ? is the placeholder character for specifying a userid.
This essentially says return all rows from Table 1 along with matching rows from Table 2, but (here's the trick) the WHERE clause says to exclude all rows that found a matching row in Table 2. Leaving only rows from Table 1 that don't have a match in Table 2.
The anti join takes a little bit of effort to get your brain wrapped around, but once you get it, it's an invaluable tool to keep handy in the SQL toolbelt.
There are other query patterns that will return an equivalent result, such as a NOT EXISTS (correlated subquery) or the more common NOT IN (subquery). (With the NOT IN, be careful that the subquery doesn't return any NULL values.)
EDIT
Removed the condition u.user_id = t.user_id from the join predicate. Looks like the user_id in [Table 1] is the "author" of the post. And has no relation to whether a user has "viewed" a post or not.
Base on your sample data, because USER_ID 1 in TABLE 1 already existed in TABLE 2 with its respected POST_ID, it should return POST_ID 2,3,4,5.
Query:
SELECT t1.* FROM table2 t2
RIGHT OUTER JOIN table1 t1
ON t2.user_id = t1.user_id AND t2.post_id = t1.post_id
WHERE t2.user_id IS NULL
Final Result:
post_id user_id description
2 1 Sample post 2
3 2 Sample post 3
4 2 Sample post 4
5 3 Sample post 5
Is this what you are looking for?
I have some tables.
titles
id| title
1 | Cars
2 | Computers
3 | Phones
4 | Tvs
entry
id | title_id | user_id | entry | time
1 | 1 | 12 | entry-01 | 1
2 | 2 | 11 | entry-02 | 2
3 | 3 | 12 | entry-03 | 3
4 | 2 | 11 | entry-04 | 4
5 | 3 | 11 | entry-05 | 5
6 | 4 | 12 | entry-06 | 6
7 | 4 | 13 | entry-07 | 7
8 | 4 | 11 | entry-08 | 8
9 | 1 | 10 | entry-09 | 9
10 | 2 | 12 | entry-10 | 10
users
id | username
10 | user-1
11 | user-2
12 | user-3
13 | user-4
friends
id | user_id | friend_id
1 | 10 | 12
2 | 11 | 12
3 | 12 | 10
4 | 10 | 11
I need to filter titles based on friends' entries and sort the results by (entry.time) desc. And I also need to show friends name and count(entry) at the list.
Expected result filtered by user_id=10 is:
result
1 | Computers | user-3, user-2(2)
2 | Tvs | user-2, user-3
3 | Phones | user-2, user-3
4 | Cars | user-3
any ideas?
This problem is complex, but if you get into the habit of breaking problems down into smaller pieces you will catch on pretty quickly. Why not start by getting all friends of user id 10? We can do so like this:
SELECT CASE WHEN user_id = 10 THEN friend_id
WHEN friend_id = 10 THEN user_id END AS userFriends
FROM friends
GROUP BY userFriends
HAVING userFriends IS NOT NULL;
Notice the use of a case statement, because user_id 10 could be in either of the two columns. I use the GROUP BY in case the user/friend pair appears multiple times (like 10 and 12 for your example) and a check for not null to remove the rows that didn't match the case.
Now that you have those, you can join it with the entries and titles tables to get the information you're going to need. Just add in some aggregation to get the number of entries each user has for a title:
SELECT t.title, u.userName, COUNT(*) AS numEntries
FROM titles t
LEFT JOIN entry e ON e.title_id = t.id
JOIN users u ON u.id = e.user_id
JOIN(
SELECT
CASE WHEN user_id = 10 THEN friend_id
WHEN friend_id = 10 THEN user_id END AS userFriends
FROM friends
GROUP BY userFriends
HAVING userFriends IS NOT NULL) f ON f.userFriends = u.id
GROUP BY t.title, u.userName;
Matching your format is going to be very tricky. Typically, you can use GROUP_CONCAT() to get a comma separated list, but you will get something like user3, user2, user2 for your first list. To fix this, I recommend writing a CONCAT() in your select statement to modify the above query to get the number of entries to the side of each user. In addition, use another CASE statement so that this only happens when the COUNT(*) is greater than 1:
SELECT t.title,
CASE WHEN COUNT(*) > 1 THEN
CONCAT(u.userName, ' (', COUNT(*), ')')
ELSE
u.userName
END AS numEntries
FROM titles t
LEFT JOIN entry e ON e.title_id = t.id
JOIN users u ON u.id = e.user_id
JOIN(
SELECT
CASE WHEN user_id = 10 THEN friend_id
WHEN friend_id = 10 THEN user_id END AS userFriends
FROM friends
GROUP BY userFriends
HAVING userFriends IS NOT NULL) f ON f.userFriends = u.id
GROUP BY t.title, u.userName;
And now, I would preform a GROUP_CONCAT() on that query:
SELECT tmp.title, GROUP_CONCAT(tmp.userEntries) AS friendEntries
FROM(
SELECT t.title,
CASE WHEN COUNT(*) > 1 THEN
CONCAT(u.userName, ' (', COUNT(*), ')')
ELSE
u.userName
END AS userEntries
FROM titles t
LEFT JOIN entry e ON e.title_id = t.id
JOIN users u ON u.id = e.user_id
JOIN(
SELECT
CASE WHEN user_id = 10 THEN friend_id
WHEN friend_id = 10 THEN user_id END AS userFriends
FROM friends
GROUP BY userFriends
HAVING userFriends IS NOT NULL) f ON f.userFriends = u.id
GROUP BY t.title, u.userName) tmp
GROUP BY tmp.title;
I apologize for the lengthy response (though I wanted to be clear and cover it all). If you've made it to this point, you'll be happy to know that it works in SQL Fiddle.
I'm trying to do return results from one table based on multiple results on another. Here's the setup:
Table A: "accounts"
id | fname | other_id
1 | test | 500
2 | test2 | 505
3 | test3 | 500
4 | test4 | 540
5 | test5 | 500
Table B: "transactions"
id | account_id |
1 | 1
2 | 4
3 | 2
4 | 1
5 | 3
6 | 2
What I'm trying to accomplish is, return all id's from transactions where account_id = the id in table A WHERE other_id = a certain value.
To do write it out manually it would look like this:
So for example if other_id = 500.
1) get records from accounts where other_id = 500 (will be multiple results, in this case, 1, 3 and 5)
2) get records from transactions where account_id = 1 OR account_id = 3 OR account_id = 5
I've tried a few different subselects but can't seem to come up with what I'm looking for.
I of course could just break this up into a loop using PHP but I'd rather use a single query for efficiency.
No subselect required, just a simple join.
select * from accounts a, transactions t where t.account_id=a.id and other_id=500
select t.id
from accounts as a
inner join transactions as t on a.id = t.account_id
where a.other_id=500
If i understand you right, you want this.
SELECT b.id
FROM accounts AS a
LEFT JOIN transactions AS b ON b.account_id = a.id
WHERE other_id = 500