How to get all users which have more than 100 referrals? - mysql

I have a service which has a referrer system. There's table users in service's database which contains id and referrers columns. Column referrer contains a string of referrer ids that separated by ,. Here's an example data from this table:
+------+---------------+
| id | referrers |
+------+---------------+
| 1 | 1 |
| 7 | 1 |
| 8 | 1 |
| 9 | 1 |
| 10 | 7,1 |
| 11 | 1 |
| 12 | 7,1 |
| 13 | 1 |
| 14 | 9,1 |
| 20 | 7,1 |
I need smt like: SELECT id, login FROM users u WHERE id IN (SELECT id FROM users u1 WHERE *referrers count more than 1000*), but I don't understand what should I use in *..*. To see this situation more clearly, I need to get the current user id from the first query and add it to the subquery into the %LIKE% statement, but how? Like:
`SELECT
id, login
FROM users u
WHERE id IN
(SELECT id FROM users u1 WHERE count((SELECT * FROM users u2 WHERE refer LIKE %u1.id%) > 1000)`
But how to do it in right way?

You can use string functions for this:
select id, login
from users
where char_length(referrers) - char_length(replace(referrers, ',', '')) >= 99
This works by counting how many , can be found in the referrers column: 99 commas means that there are 100 values.
Bottom line, you should fix your data model: instead of using delimited lists, you should have a separate table to store the user/referrer tuples, with one tuple per row. Storing delimited lists in a table column is typical antipattern in relational databases that should generally be avoided: you can read more about this in this famous SO post.

Related

MySQL - How to order duplicate rows in a key value pair table based on multiple columns?

So I have the following key/value pair table, where users submit data through a form and each question on the form is added to the table here as an individual row. Submission_id identifies each form submission.
+----+---------------+--------------+--------+
| id | submission_id | key | value |
+----+---------------+--------------+--------+
| 1 | 10 | manufacturer | Apple |
| 2 | 10 | model | 5s |
| 3 | 10 | firstname | Paul |
| 4 | 15 | manufacturer | Apple |
| 5 | 15 | model | 5s |
| 6 | 15 | firstname | Paul |
| 7 | 20 | manufacturer | Apple |
| 8 | 20 | model | 5s |
| 9 | 20 | firstname | Andrew |
+----+---------------+--------------+--------+
From the data above you can see that the submissions with id of 10 and 15 both have the same values (just different submission id). This is basically because a user has submitted the same form twice and so is a duplicate.
Im trying to find a way to order these table where the any duplicate submissions appear together in order. Given the above table I am trying to build a query that gives me the result as below:
+---------------+
| submission_id |
+---------------+
| 10 |
| 15 |
| 20 |
+---------------+
So I want to check to see if a submission where the manufacturer, model and firstname keys have the same value. If it does then these get the submission id and place them adjacently in the result. In the actual table there are other keys, but I only want to match duplicates based on these 3 keys (manufacturer, model, firstname).
I’ve been going back and forth to the drawing board quite some time now and have tried looking for some possible solutions but cannot get something reliable.
That's not a key value table. It's usually called an Entity-Attribute-Value table/relation/pattern.
Looking at the problem, it would be trivial if the table were laid out in conventional 1st + 2nd Normal form - you just do a join on the values, group by those and take a count....
SELECT manufacturer, model, firstname, COUNT(DISTINCT submission_id)
FROM atable
GROUP BY manufacturer, model, firstname
HAVING COUNT(DISTINCT submission_id)>1;
Or a join....
SELECT a.manufacturer, a.model, a.firstname
, a.submission_id, b.submission_id
FROM atable a
JOIN atable b
ON a.manufacturer=b.manufacturer
AND a.model=b.model
AND a.firstname=b.firstname
WHERE a.submission_id<b.submission_id
;
Or using sorting and comparing adjacent rows....
SELECT *
FROM
(
SELECT #prev.submission_id AS prev_submission_id
, #prev.manufacturer AS prev_manufacturer
, #prev.model AS prev_model
, #prev.firstname AS pref_firstname
, a.submission_id
, a.manufacturer
, a.model
, set #prev.submission_id:=a.submission_id as currsid
, set #prev.manufacturer:=a.manufacturer as currman
, set #prev.model:=a.model as currmodel
, set #prev.firstname=a.forstname as currname
FROM atable
ORDER BY manufacturer, model, firstname, submission_id
)
WHERE prev_manufacturer=manufacturer
AND prev_model=model
AND prev_firstname=firstname
AND prev_submission_id<>submission_id;
So the solution is to simply make your data look like a normal relation....
SELECT ilv.values
, COUNT(ilv.submission_id)
, GROUP_CONCAT(ilv.submission_id)
FROM
(SELECT a.submission_id
, GROUP_CONCAT(CONCAT(a.key, '=',a.value)) AS values
FROM atable a
GROUP BY a.submission_id
) ilv
GROUP BY ilv.values
HAVING COUNT(ilv.submission_id)>1;
Hopefully the join and sequence based solutions should now be obvious.

join two tables in mysql and get records

I have two tables "contacts" and "users". Users table storing data with "," separated. Need to distinct data in "Contacts" column from "Contacts" table. And need to join with "Users" table, and get the records.
Contacts Table
--------------------------
id | user_Id | contats
--------------------------
1 | 2147483647 | 90123456789,90123456789,90123456789,90123456789
2 | 2147483647 | 90123456789,90123456789,90123456789,90123456789
3 | 919444894154 | 90123456789,90123456789,90123456789,90123456789
Users Table
-----------------------------
id | username | email | phone
-----------------------------
1 | bhavan | bhavanram93#gmail.com | 90123456789
2 | bhavan | bhavanram93#gmail.com | 90123456789
3 | prince | prince#gmail.com | 1234567980
4 | bhavan | bhavanram93#gmail.com | 90123456789
5 | hello | hello#gmail.com | 1234567890
6 | bhavan | bhavanram93#gmail.com | 90123456789
Your table Contacts shouldn't be constructed this way.
Since you want 1 Users table containing all the data about a user, and 1 Contacts table containing links between different users, you'd rather do this kind of table structure :
Contacts table
id | user_id | contact_id
-------------------------
1 | 1 | 2
2 | 1 | 3
3 | 2 | 3
That'll allow you to do something like :
SELECT *
FROM Users
JOIN Contacts ON (Users.id = Contacts.contact_id)
WHERE Contacts.user_id = 1
Which will return all the data of the contacts of the user 1.
Your current structure is a huge ongoing mess, it's the opposite of being flexible.
You should restructure your db to a normalized format as Steve suggest.
But if you cant:
SELECT *
FROM Users
JOIN Contacts
ON CONCAT(',', Contacts.contacts, ',') like
CONCAT('%,', Users.phone, ',%')
WHERE Contacts.user_id = 1
the idea is you convert your contacts to
, <numbers> ,
,90123456789,90123456789,90123456789,90123456789,
and try to match with
%,90123456789,%
Note this approach cant use any index so will have bad performance with many
rows. if you are in the order of 1k-10k rows may be ok. More than that you need consider restructure your db.

Split SQL field containing array into new table/rows

I need a list of user IDs (course_user_ids) that is currently stored in a single field of a larger table.
I have a table called courses that contains course information with course_id and course_students as such:
-----------------------------------------------------------
| course_id | course_students |
----------------------------------------------------------
| 1 | a:3:{i:0;i:12345;i:1;i:22345;i:2;i:323456;} |
-----------------------------------------------------------
| 2 | a:32:{ … } |
-----------------------------------------------------------
The course_students part contains 3 chunks of information:
the number of students (a:3:{…) -- not needed
the order/key for the array of each student ({i:0;… i:1;… i:2; …}) -- also not needed
the course_user_id (i:12345; … i:22345;… i:32345;)
I only need the course_user_id and the original course_id, resulting in a new table that i can use for joins/subqueries like this:
------------------------------
| course_id | course_user_id |
------------------------------
| 1 | 12345 |
------------------------------
| 1 | 22345 |
------------------------------
| 1 | 323456 |
------------------------------
(ideally able to continue to break out values for other course_ids and course_user_ids, but not a priority:)
| … | … |
------------------------------
| 2 | … |
------------------------------
| 2 | … |
------------------------------
| 97 | … |
------------------------------
| 97 | … |
------------------------------
| … | … |
------------------------------
Note: the course_user_id can vary in length (some are 5 digits, some are 6)
Any ideas would be much appreciated!
Update
My user table does have user_id which can be mapped to course_students or course_user_id, so that is a very helpful observation from below.
I also think I need to use a LEFT JOIN because some students are registered in multiple courses, and I'd like to see each instance/combo.
Let us assume that you have a table name users which contains all users data along with user_id.
Now you can join table courses and table users in following manner:
select c.course_id,u.user_id
from
courses c
join users u
on u.user_id=if(instr(c.course_students,concat(":",u.user_id,";"))>0,u.user_id,c.course_students)
You get the result as per your requirement.
Verify at http://sqlfiddle.com/#!9/3667d/2
Note: The above query works fine if no overlapping between user_id and array index. In case of overlapping, kindly filter data using where-clause
If I got your goal correctly you have users table. And {i:0;i:12345;i:1;i:22345;i:2;i:323456; equal users.id=12345,users.id=22345 etc.
If my guess is correct you can try this solution:
http://sqlfiddle.com/#!9/cfef27/5
SELECT * FROM courses
LEFT JOIN users u
ON courses.course_students LIKE CONCAT('%i:',u.id,';%')

query to get most matched likes first in mysql

I have table like:
user :
uid | course_id | subjects
---------------------------
1 | 1 | html,php
2 | 1 | java,html,sql
3 | 1 | java
4 | 1 | fashion,html,php,sql,java
I want to run a query which can return most liked subjects in query and then second most and so on...
For Example :
select * from user where subjects like '%java%' or '%php%' or '%html%';
this query will return data like this:
uid | course_id | subjects
---------------------------
2 | 1 | java,html,sql
3 | 1 | java
4 | 1 | fashion,html,php,sql,java
but i want output like this :
uid | course_id | subjects
---------------------------
4 | 1 | fashion,html,php,sql,java
2 | 1 | java,html,sql
1 | 1 | html,php
3 | 1 | java
so the most matched subjects 1st then 2nd most matched subjects and so on....
Is there any modification in my query so that i can get this type of sorted output.
Never, never, never store multiple values in one column!
Like you see now this will only give you headaches. Normalize your user table. Then you can select normally.
It should look like this
uid | course_id | subjects
---------------------------
1 | 1 | html
1 | 1 | php
2 | 1 | java
2 | 1 | html
2 | 1 | sql
3 | 1 | java
...
or better introduce an new table subjects and then make a mapping table called course_subjects
subject
id | name
------------
1 | html
2 | sql
3 | java
...
course_subjects
uid | course_id | subject_id
---------------------------
1 | 1 | 1
1 | 1 | 2
...
Based on the way you want your results, it looks like you want to order by the number of subjects (or tags) within subject. This can be accomplished by counting the number of , (commas).
The way to count the number of occurances of a character is to subtract the original length by the length when the character is removed.
Example:
SELECT *
FROM USER
WHERE subjects LIKE '%java%'
OR '%php%'
OR '%html%'
ORDER BY ( Length(subjects) - Length(Replace(subjects, ',', '')) ) DESC;
SQLFiddle: http://sqlfiddle.com/#!2/cc793/4
Result:
UID COURSE_ID SUBJECTS
4 1 fashion,html,php,sql,java
2 1 java,html,sql
3 1 java
Note:
As juergen says it is a bad idea to store multiple values in one column.
With MyISAM storage engine you can do match against.
The simplest example:
SELECT *,
MATCH (subjects) AGAINST ('java php html') AS relevance
FROM `user`
WHERE MATCH (subjects) AGAINST ('java php html')
ORDER BY relevance DESC
In MySQL 5.6 full-text search is available with InnoDB too but needs a bit extra to make it work. For more info checkout the following post: http://www.mysqlperformanceblog.com/2013/03/04/innodb-full-text-search-in-mysql-5-6-part-2-the-queries/

retrieving data from two mysql tables where the second table depends on the first

I have two MySql tables:
users(id_user, name, age, gender ).
ways(#id_user,id_way, start, end, date).
What I want is to retrieve all the ways with their corresponding users details.
So my result would be like this:
id_way | start | end | date | id_user | name | age | gender
---------------------------------------------------------------------------
2 | place1 | place2 | 12/06/2013 | 145 | john | 28 | m
Have you tried JOIN?
SELECT ways.id_way, ways.start, ways.end, ways.date, users.*
FROM ways JOIN users USING (id_user)