Mysql sum count occurrences in multiple columns from another table - mysql

I need to find the sum of occurrences of ids in a join table. The id could be present in two different columns (id_type_1 and id_type_2).
Table types
id | name
1 | Test1
2 | Test2
3 | Test3
Table products
id | name | id_type_1 | id_type_2
1 | Product1 | 1 | 2
2 | Product2 | 3 | 1
3 | Product3 | 1 | 3
I need to get a result like this:
Type | Total
Test1 | 3
Test2 | 1
Test3 | 2
Here's my query, but it takes several seconds to execute:
SELECT t.name,
(SELECT COUNT(p.id) FROM products p WHERE p.id_type_1 = t.id || p.id_type_2 = t.id) AS total
FROM types t
WHERE 1
ORDER BY total DESC
Is there a more effective way to achieve the result?

Join the tables and aggregate:
select t.id, t.name,
sum((t.id = p.id_type_1) + (t.id = p.id_type_2)) Total
from types t inner join products p
on t.id in (p.id_type_1, p.id_type_2)
group by t.id, t.name
If there is no case for the id to exist in both id_type_1 and id_type_2 in the same row then:
select t.id, t.name,
count(*) Total
from types t inner join products p
on t.id in (p.id_type_1, p.id_type_2)
group by t.id, t.name
See the demo.
Results:
> id | name | Total
> -: | :---- | ----:
> 1 | Test1 | 3
> 2 | Test2 | 1
> 3 | Test3 | 2

Related

How to calculate max values of groups?

I have a table like so (I'm not sure how to format tables)
Category / Products / Purchases
1 | A | 12
1 | B | 13
1 | C | 11
2 | A | 1
2 | B | 2
2 | C | 3
Expected output:
1 | B | 13
2 | C | 3
However I keep on getting
1 | A | 13
2 | A | 3
ie. It just selects the first occurrence of the second column.
Here is my code:
SELECT Category, Products, MAX(Purchases) FROM myTable GROUP BY Category;
Use filtering in the where clause:
select t.*
from t
where t.purchases = (select max(t2.purchases) from t t2 where t2.category = t.category);
With NOT EXISTS:
select m.* from myTable m
where not exists (
select 1 from myTable
where category = m.category and purchases > m.purchases
)
See the demo.
Results:
| Category | Products | Purchases |
| -------- | -------- | --------- |
| 1 | B | 13 |
| 2 | C | 3 |
You can use row_number() to identify max purchase for each group or replace rownumber() to rank() if there are ties of max purchases for each group
Select Category, Products,
Purchases from (Select Category,
Products,
Purchases,
row_number() over (partition by
category, products order by
purchases desc) rn from table) t
where t.rn=1
)

Select two items with maximum number of common values

I have the following table:
+----+-----------+-----------+
| id | teacherId | studentId |
+----+-----------+-----------+
| 1 | 1 | 4 |
| 2 | 1 | 2 |
| 3 | 1 | 1 |
| 4 | 1 | 3 |
| 5 | 2 | 2 |
| 6 | 2 | 1 |
| 7 | 2 | 3 |
| 8 | 3 | 9 |
| 9 | 3 | 6 |
| 10 | 1 | 6 |
+----+-----------+-----------+
I need a query to find two teacherId's with maximum number of common studentId's.
In this case teachers with teacherIds 1,2 have common students with studentIds 2, 1, 3, which is greater than 1,3 having common students 6.
Thanks in Advance!
[Edit]: After several hours I've had the following solution:
SELECT * FROM (
SELECT r1tid, r2tid, COUNT(r2tid) AS cnt
FROM (
SELECT r1.teacherId AS r1tid, r2.teacherId AS r2tid
FROM table r1
INNER JOIN table r2 ON r1.studentId=r2.studentId AND r1.teacherId!=r2.teacherId
ORDER BY r1tid
) t
GROUP BY r1tid, r2tid
ORDER BY cnt DESC
) t GROUP BY cnt ORDER BY cnt DESC LIMIT 1;
I was sure that there must exist more short and elegant solution, but I could not find it.
You would do this with a self-join. Assuming no duplicates in the table:
select t.teacherid, t2.teacherid, count(*) as NumStudentsInCommon
from table t join
table t2
on t.studentid = t2.studentid and
t.teacherid < t2.teacherid
group by t.teacherid, t2.teacherid
order by NumStudentsInCommon desc
limit 1;
If you had duplicates, you would just replace count(*) with count(distinct studentid), but count(distinct) requires a bit more work.
select t.teacherId, t2.teacherId, sum(t.studentId) as NumStudentsInCommon
from table1 t join
table1 t2
on t.studentId = t2.studentId and
t.teacherId < t2.teacherId
group by t.teacherId, t2.teacherId
order by NumStudentsInCommon desc

MySQL - Selecting single entry per category

My apologies. I have edited the below into 2 table, Im just having a bit of confusion.
I have a tables very similar as the ones below and I wanted to show all the table2-class 1 but only 1 random item per each table1-category
Sample Item Table1
+---------+---------------+---------------+
| ID | Item Name | Category |
+---------+---------------+---------------+
| 01 | Item A | Cat 1 |
| 02 | Item B | Cat 1 |
| 03 | Item C | Cat 2 |
| 04 | Item D | Cat 2 |
| 05 | Item E | Cat 3 |
| 06 | Item F | Cat 3 |
+---------+---------------+---------------+
Sample Item Table2
+---------------+---------------+
| Category | Class |
+---------------+---------------+
| Cat 1 | 1 |
| Cat 2 | 1 |
| Cat 3 | 2 |
+---------------+---------------+
I wanted to show all the table2-class 1 but only 1 random item per each table1-category
Desired Result
+---------+---------------+---------------+
| 02 | Item B | Cat 1 |
| 03 | Item C | Cat 2 |
+---------+---------------+---------------+
(This is within my PHP script)
Thanks in advance
You can do something like this
SELECT t.id, itemname, category
FROM
(
SELECT
(
SELECT id
FROM table1
WHERE category = t.category
ORDER BY RAND()
LIMIT 1
) id
FROM table1 t
GROUP BY category
) q JOIN table1 t
ON q.id = t.id
Note: using RAND() is very costly
Output:
| ID | ITEMNAME | CATEGORY |
|----|----------|----------|
| 1 | Item A | Cat 1 |
| 3 | Item C | Cat 2 |
| 6 | Item F | Cat 3 |
Here is SQLFiddle demo
Try something like this:
SELECT id, itemname, category FROM (
SELECT id, itemname, category FROM sample_table
ORDER BY RAND()
) AS tmp
GROUP BY category
Note that this query is totally valid in MySQL
http://dev.mysql.com/doc/refman/5.0/en/group-by-extensions.html
The safest way to do this is with a correlated subquery. To get the item_id:
select category,
(select item_id from sample s where s2.category = s.category order by rand() limit 1) as item_id
from sample s
group by category;
To get the rest of the item information, join that back in:
select s.*
from (select category,
(select item_id from sample s where s2.category = s.category order by rand() limit 1) as item_id
from sample s
group by category
) c join
sample s
on s.item_id = c.item_id;
Prior to the above edited scenario, I used the below query and it works fine except that it doesn't randomize the entry of each category:
SELECT * FROM Table1,Table2
WHERE Table2.Class = '1'
AND Table1.Category = Table2.Category
GROUP BY Table1.Category ORDER BY RAND()

How to make cross select in one table?

Consider this table:
I need to make a SQL query which returns highlighted rows. In other words: rows which are linked to each other by idContactTo.
Example:
1 has contact to 2, 2 has contact to 1 - they are linked and should be in result table. But even 1 has contact to 3 it doesn't mean that 3 has contact to 1 - they aren't linked.
You can do it via INNER JOIN,
SELECT a.*
FROM tableName a
INNER JOIN tableName b
ON a.idContantOwner = b.idContactTo AND
b.idContantOwner = a.idContactTo
SQLFiddle Demo
Another way to do it
SELECT t.*
FROM
(
SELECT MiN(id) min_id, MAX(id) max_id
FROM Table1
GROUP BY LEAST(idContactOwner, idContactTo),
GREATEST(idContactOwner, idContactTo)
HAVING COUNT(*) = 2
) q JOIN Table1 t
ON t.id IN(q.min_id, q.max_id)
Output:
| ID | IDCONTACTOWNER | IDCONTACTTO |
|----|----------------|-------------|
| 1 | 1 | 2 |
| 2 | 2 | 1 |
| 4 | 3 | 4 |
| 5 | 4 | 3 |
Here is SQLFiddle demo

Count multiple columns with same WHERE condition

I have a users table with columns: user_id, teacher_id1, teacher_id2, teacher_id3
and
teachers table with id
Each user can have the same id's for teacher_id1, teacher_id2, teacher_id3
I would like to count how many users have same teacher.
User table
+----------------------------------------+
| user_Id teacher_id1 teacher_id2 teacher_id3 |
+----------------------------------------+
| 1 1 1 1 |
| 2 2 1 3 |
| 3 2 3 3 |
| 4 2 2 2 |
+----------------------------------------+
Teacher table
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
Count for $id1 is: 2
Count for $id2 is: 3
Count for $id3 is: 2
I tried something like this, but it is not correct!
SELECT COUNT(*) FROM users WHERE concat_ws('',teacher_id1 OR teacher_id2
OR teacher_id3) like '{$id}' ";
You have data in three different columns. You need to combine it into one column, to get the distinct counts that you want. For this, you can use union all. Then the count is simply count(distinct):
select teacher_id, COUNT(distinct USER_ID)
from ((select user_id, teacher_id1 as teacher_id
from t
) union all
(select user_id, teacher_id2
from t
) union all
(select user_id, teacher_id3
from t
)
) s
group by teacher_id;
Try this query
select b.id, count(*)
from
tbl1 a
inner join
tbl2 b
on b.id = teacher_id1 or b.id = teacher_id2 or b.id = teacher_id3
group by b.id
SQL FIDDLE:
| ID | COUNT(*) |
-----------------
| 1 | 2 |
| 2 | 3 |
| 3 | 2 |