How to join these two tables? - mysql

I have:
TABLE: USERS
UID | NAME
1 | Bob
2 | John
And I have:
TABLE: HITS
HITID | UID
1 | 1
2 | 2
3 | 2
4 | 1
5 | 2
6 | 2
I want:
UID | HITS
1 | 2
2 | 4
Seems simple enough, but I can't seem to do it?

Try this:
SELECT UID, COUNT(UID) HITS FROM HITS
GROUP BY UID;

This Might help you
DECLARE #USERS TABLE(UID INT, NAME VARCHAR(20))
INSERT INTO #USERS (UID,NAME) VALUES ('1','Bob'),('2','John')
DECLARE #HITS TABLE(HITID INT,UID INT)
INSERT INTO #HITS (HITID,UID) VALUES('1','1'),('2','2'),('3','2'),('4','1'),('5','2'),('6','2')
Use JOIN if you want to use Both USERS Table and HITS Table
SELECT U.UID,COUNT(H.HITID) AS HITS FROM #USERS AS U INNER JOIN #HITS AS H ON U.UID = H.UID GROUP BY U.UID
OR Use Simple Query If you want to use only HITS Table
SELECT UID, COUNT(UID) HITS FROM #HITS GROUP BY UID
I also Created Temp tables in this.

SELECT b.UID, COUNT(b.UID) HITS FROM HITS a, USERS b
WHERE a.UID=b.UID
GROUP BY UID
This should work

This would do the trick
SELECT H.UID, COUNT(*) AS 'HITS'
FROM HITS H
GROUP BY H.UID

if you want to have the name of the users, then you need to join it
SELECT a.Name, COUNT(*) totalCount
FROM users a
INNER JOIN HITS b
ON a.UID = b.UID
GROUP BY a.UID
SQLFiddle Demo

Related

Select top X records for each group or default

I have the following schema:
users:
id email
1 'user.one#test.com'
2 'user.two#test.com'
video_group:
id title
1 'Group 1'
2 'Group 2'
videos:
id group_id rank title
1 1 1 'Group 1 - Video 1'
2 1 2 'Group 1 - Video 2'
3 2 1 'Group 2 - Video 1'
user_video_play_times:
video_id user_id time last_update
2 1 12 01-02-2018
1 1 120 01-01-2018
I need to get the time, user_id, video_id, and group_id of the last video played by a user in specific groups, but if there's no records on user_video_play_times for a group, the video with the lowest rank should be returned. For example:
user_id group_id video_id time
1 1 2 12 -- user.one + group 1
1 2 3 0 -- user one + group 2
This is the query I have so far:
SELECT
pt.user_id user_id,
v.id video_id,
g.id group_id,
pt.time time
FROM
videos v
INNER JOIN video_groups g ON g.id = v.group_id
LEFT JOIN user_video_play_times pt ON
pt.video_id = v.id AND
pt.user_id = 1
LEFT JOIN (
SELECT
g.id AS g_id,
MAX(pt.last_update) AS pt_last_update
FROM
user_video_play_times pt
INNER JOIN videos v ON v.id = pt.video_id
INNER JOIN video_groups g ON g.id = v.group_id
WHERE
pt.user_id = 1 AND
g.id IN (1, 2)
GROUP BY
g.id
) lpt ON lpt.g_id = g.id AND lpt.pt_last_update = pt.last_update
WHERE
g.id IN (1, 2)
GROUP BY
g.id
It is sort of working, but...
Adding v.title to the column selection messes the results for some reason, making everything return only videos with rank 1. Any idea why?
Could this query be optimized, or is there another slicker way to achieve the same results?
Any help with this is really appreciated!
DB fiddle here
Update 1:
This issue seems to only happen when the column os of type text.
Since your db<>fiddle is for MariaDB version 10.3; I am presuming that you have Window Functions available.
We can use Row_number() function over a partition of group_id to get row number values, as per the defined rules. Video with latest last_update value will have Row number of 1 and so on. If there is no video played, then the one with least value of Rank will have Row number = 1.
We can use this result-set as a Derived Table, and consider only those rows where Row number = 1.
SELECT
dt.user_id,
dt.group_id,
dt.video_id,
dt.video_title,
dt.time
FROM
(
SELECT
pt.user_id AS user_id,
g.id AS group_id,
v.id AS video_id,
v.title AS video_title,
pt.time AS time,
ROW_NUMBER() OVER(PARTITION BY v.group_id
ORDER BY pt.last_update DESC,
v.`rank` ASC) AS row_num
FROM videos AS v
INNER JOIN video_groups AS g
ON g.id = v.group_id AND
g.id IN (1,2)
LEFT JOIN user_video_play_times AS pt
ON pt.video_id = v.id AND
pt.user_id = 1
) AS dt
WHERE dt.row_num = 1
View on DB Fiddle
Result:
| user_id | group_id | video_id | video_title | time |
| ------- | -------- | -------- | ----------------- | ---- |
| 1 | 1 | 2 | Group 1 - Video 2 | 12 |
| | 2 | 3 | Group 2 - Video 1 | |
PS: Note that Rank is a Reserved Keyword, and you should really avoid using it as column/table name.

Check if ID from a table exists in another table, and if so, how many times

Let's say I have two tables
Table a
some_ID
1
2
3
4
Table b
some_ID
1
2
1
4
Now what I would like to receive is a table like
id amount
1 | 2
2 | 1
I tried with a following query:
SELECT COUNT(a.some_id) as id
FROM Table_a
INNER JOIN Table_b
ON Table_a.some_id = Table.b.some_id
but that only returned how many id rows there are in both tables.
Any help?
Do the grouping on table_b and then join that result set on table_a
SELECT b.* FROM
(
SELECT id, COUNT(*) AS Cnt
FROM Table_b
GROUP BY id
) b
INNER JOIN Table_a a ON a.id = b.id
SQLFiddle
If you want the zero counts:
SELECT a.some_id AS id, count(b.some_id) as amount
FROM a LEFT JOIN b ON a.some_id = b.some_id
GROUP BY a.some_id
Result:
id | amount
1 | 2
2 | 1
3 | 0
4 | 1
If not:
SELECT a.some_id AS id, count(*) as amount
FROM a INNER JOIN b ON a.some_id = b.some_id
GROUP BY a.some_id
Result:
id | amount
1 | 2
2 | 1
4 | 1
The difference is the join type. Once left outer join. Then inner join. Note that in the first case it is important to count with count(b.some_id). With count(*) the rows with missing b entries would be counted as 1. count(*) counts the rows. count(expression) counts the non-null values.
If I understand correctly, you want a histogram of histograms:
select cnt, count(*) as num_ids
from (select id, count(*) as cnt
from b
group by id
) b
group by cnt;

Find the same sets of pairs

I have such scheme in mysql:
TableA (id integer PK, pid integer, mid integer)
Ex. data:
id | pid | mid
1 | 2 | 2
2 | 2 | 4
3 | 3 | 4
4 | 4 | 2
5 | 4 | 4
6 | 3 | 2
7 | 3 | 5
I have pid with some mid's and want to find all pid's with the same set of mid's. In example for pid=2 answer is 2,4
group_concat is not suitable for me
I think it should be simple, but the answer eludes me
UPD:
I have tried group_concat:
SELECT DISTINCT(b.pid) FROM (SELECT pid, group_concat(mid) as concated FROM TableA where pid=100293) as a, (select pid, group_concat(mid) as concated, COUNT(1) as count FROM TableA group by pid) as b where a.concated=b.concated;
Since you are working with integers, instead of group_concat you could generate a bitmask on distinct mid values for each pid and join on that. Then it's just math all the way down:
SELECT DISTINCT pid
FROM (SELECT pid, sum(pow(2,mid)) as midmask FROM (SELECT distinct pid, mid FROM tableA) as t1a GROUP BY pid) as t1
INNER JOIN (SELECT pid, sum(pow(2,mid)) as midmask FROM (SELECT distinct pid, mid FROM tableA) as t2a GROUP BY pid) as t2
ON t1.midmask = t2.midmask
IF mid is already distinct for each pid then you can get rid of the inner-inner subqueries.
Using #GordonLinoff's excellent single-subquery approach where GROUP_CONCAT is only used on the main query (where it won't be so expensive). Instead of the group_concat on the inner query we use the bitmask approach that may be quicker.
SELECT midmask>>1, group_concat(pid)
FROM (SELECT pid, sum(pow(2,mid)) as midmask FROM (SELECT distinct pid, mid FROM tableA) as t1a GROUP BY pid) as t1
GROUP BY midmask;
Results:
+---------+-------------------+
| midmask | group_concat(pid) |
+---------+-------------------+
| 10 | 2,4 |
| 26 | 3 |
+---------+-------------------+
Obviously that midmask in the result set isn't super necessary, but you can pick out the values from the bitmask if you want to see the mid values that contributed to the match if you like.
I'm using the bit right-shift operator to insure that the proper bit is set in the midmask result otherwise you'll be off by one. If you don't care about the output of the midmask, then don't bother with the >>1 portion of the query.
You can use this query. It will give you comma separated pids.
select `mid`, group_concat(`pid`) from `tableA` group by `mid`;
In MySQL, I would approach this using group_concat():
select mids, group_concat(pid)
from (select pid, group_concat(mid order by mid) as mids
from t
group by pid
) t
group by mids;
This solves the general problem, for all pids. Solving for 1 pid is a bit tricky in MySQL (no window functions), but you can try:
select t.pid, t2.pid, count(*)
from t join
t t2
on t.mid = t2.mid and t2.pid = 2
group by t.pid, t2.pid
having count(*) = (select count(*) from t where t.pid = t.pid) and
count(*) = (select count(*) from t where t.pid = t2.pid);
For this, you want indexes on t(mid, pid) and t(pid).

MySQL multiple SubQueries on same table

I got a table votes that indicates me if a user voted for a specific movie. It also shows me how many movies a user has voted for.
id_film | id_user | voting
----------------------------
1 | 1 | 7
1 | 33 | 5
3 | 1 | 9
4 | 7 | 7
4 | 2 | 8
4 | 1 | 6
6 | 1 | 6
... | ... | ...
I want to get a list of id_film's which are related to id_user's in this way:
Get all id_film's from a specific id_user like
SELECT id_film FROM votes WHERE id_user = 1
Grab every id_user which is related
SELECT DISTINCT v.user FROM votes v WHERE id_film IN ( id_film's )
Then SELECT id_film's FROM votes v WHERE user IN ( "user list from previous query" ) except id_film's from first query.
This was my first attempt:
SELECT id_film, film.title, film.originaltitle, COUNT(*)
FROM votes v
INNER JOIN film ON v.id_film = film.id
WHERE user IN
(
SELECT DISTINCT v.user
FROM votes v
WHERE id_film IN
(
SELECT id_film
FROM votes v
WHERE user = 1
)
)
AND
id_film NOT IN
(
SELECT id_film
FROM votes v
WHERE user = 1
)
GROUP BY id_film
It doesn't work. MySQL took too long for a result and I restarted XAMPP.
So I tried another SELECT, this time with JOINS:
SELECT DISTINCT v.id_film AS vFilm, v1.user AS v1User, v2.id_film AS v2Film
FROM votes v
LEFT OUTER JOIN votes v1 ON v1.id_film = v.id_film
LEFT OUTER JOIN votes v2 ON v1.user = v2.user
WHERE v.user = 1
AND v1.user != 1
AND v2.id_film NOT
IN
(
SELECT id_film
FROM votes
WHERE user = 1
)
GROUP BY v2.id_film
Also doesn't work, but when I tried it without the NOT IN condition in the end it works! (It took appr. 13 sec.) :-(
Here is the working query.
SELECT DISTINCT v2.id_film AS v2Film
FROM votes v
LEFT OUTER JOIN votes v1 ON v1.id_film = v.id_film
LEFT OUTER JOIN votes v2 ON v1.user = v2.user
WHERE v.user = 1
AND v1.user != 1
With Output
v2Film
---------
1
13
14
58
4
...
But this query doesn't except id_film's from first query.
Because I know that user 1 already voted for id_film 1.
So, am I totally wrong with my logic or is my code too complex for this?

Select every field that fullfil the condition

I have a table call production
factory_id | factory_name | product_id
1 | A | 1
1 | A | 2
1 | A | 3
2 | B | 3
3 | C | 1
3 | C | 2
3 | C | 3
3 | C | 4
3 | C | 5
I'm trying to develop a query that will return two factory name pair such that every product of factory1 is produced by factory2, result looked like:
factory_name_1 | factory_name_2
A | C
B | A
B | C
I have some nested self join and renames, but I can't wrap my head around how I can apply EXISTS or IN for this scenario that does "for each product produced by factory X do condition". Thanks to any help in advanced.
Update:
Sorry that I forgot to paste my query:
select t0.fname0, t1.fname1
from (
select factory_id as fid0, factory_name as fname0, product_id as pid0, count(distinct factory_id, product_id) as pnum0
from production
group by factory_id
) t0
join
(
select factory_id as fid1, factory_name as fname1, product_id as pid1, count(distinct factory_id, product_id) as pnum1
from production
group by factory_id
) t1
where t0.fid0 <> t1.fid1
and t0.pnum0 < t1.pnum1
and t0.pid0 = t1.pid1;
Update 2: production is the only table. Expected output factory1 and factory2 are just the rename of factory_name attribute.
You need to JOIN the table for each factory pairing to make sure they "join" on the same product_ids, otherwise you might end up with similar counts for DISTINCT product_ids but these will not necessarily refer to the same product_ids.
This is my take on it:
SELECT bfna,afna, pcnt FROM (
SELECT a.factory_name afna, b.factory_name bfna, COUNT(DISTINCT b.product_id) commoncnt
FROM tbl a LEFT JOIN tbl b ON b.factory_name!=a.factory_name AND b.product_id=a.product_id
GROUP BY a.factory_name, b.factory_name
) c
INNER JOIN (
SELECT factory_name fna, COUNT(DISTINCT product_id) pcnt
FROM TBL GROUP BY factory_name
) d ON fna=bfna AND commoncnt=pcnt
ORDER BY bfna,afna
You can find a demo here: https://rextester.com/JJGCK84904
It produces:
bfna afna commoncnt
A C 3
B A 1
B C 1
For simplicity I left out the column factory_id as it does not add any information here.
Fun fact: as I am using only "bare-bone" SQL expressions, the above code will run on SQL-Server too without any changes.
You can do it this way:
select A as factory_name_1 , B as factory_name_2
from
(
select A, B, count(*) as Count_
from
(
select a.factory_name as A, b.factory_name as B
from yourtable a
inner join yourtable b
on a.product_id = b.product_id and a.factory_id <> b.factory_id
)a group by A, B
)a
inner join
(select factory_name, count(*) as Count_ from yourtable group by factory_name) b
on a.A = b.factory_name and a.Count_ = b.Count_
Order by 1
Output:
factory_name_1 factory_name_2
A C
B A
B C
The other solutions just seem more complicated than necessary. This is basically a self-join with aggregation:
with t as (
select t.*, count(*) over (partition by factory_id) as cnt
from tbl t
)
select t1.factory_id, t2.factory_id, t1.factory_name, t2.factory_name, count(*)
from t t1 join
t t2
on t1.product_id = t2.product_id and t1.factory_id <> t2.factory_id
group by t1.factory_id, t2.factory_id, t1.factory_name, t2.factory_name, t1.cnt
having count(*) = max(t1.cnt);
Here is a db<>fiddle.