I have expertise table that have user id and expertise id. Each user might have multiple expertise. Now I want to get all users that must have all expertise in range, like (1,2,3,4,5,......so on).
IN condition is only used for OR operation so how can I get expected result lets say by using IN condition with AND operation. Or is there any other operator or trick that can be used.
EDIT:
Let me add bit more explanation. Customer select expertise like physics, math, chemistry ...... and so on. So I have expertise and I need to get all user ids from expertise table that satisfy all selected expertise. So I need user who is well versed in math, physics, chemistry and so on.
All data is in one expertise table.
Current query is like this
Select user_id from expertise where expertise_id IN (1,2,3,4)
but this is true for all user who have one of these expertise but I need all users who have all these expertise.
If I understand correctly you want all the user_id returning which have all the expertise (and possibly more) that are in the IN clause.
If so select the users_id, GROUP BY the user id and COUNT the DISTINCT expertise id that match. Return the users_id for those where the count is the same as the number of ids you are searching for.
SELECT user_id
FROM expertise
WHERE expertise_id IN (1,2,3,4)
GROUP BY user_id
HAVING COUNT(DISTINCT expertise_id) = 4
You could verify the number of expertises that match your list, and require that this number equals the size of your list. Here is an example, for when your list of required expertises has 5 elements:
SELECT *
FROM users
WHERE 5 = (SELECT COUNT(DISTINCT expertise_id)
FROM user_expertises
WHERE user_id = users.user_id
AND expertise_id IN (1, 2, 3, 13, 18))
The DISTINCT expertise_id can be replaced by a simple * if it is certain that the user_expertise table has no duplicate (user_id, expertise_id) pairs.
Of course, you'll have to use the proper table names and columns names. This is just a template.
Related
Suppose I have a table storing say student information
id | first_name | last_name | dob | score
where score is some non-unique numeric assessment of their performance. (the schema isn't really relevant, I'm trying to go as generic as possible)
I'd like to know, for any given student, what their score-based overall ranking is. ROW_NUMBER() or any equivalent counter method doesn't really work since they're only accounting for returned entries, so if you're only interested in one particular student, it's always going to be 1.
Counting the number of students with scores greater than the current one won't work either since you can have multiple students with the same score. Would sorting additionally by a secondary field, such as dob work, or would it be too slow?
You should be able to JOIN into a subquery which will provide the ranks of each student across the entire population:
SELECT student.*, ranking.rank
FROM student
JOIN (
SELECT id, RANK() OVER (ORDER BY score DESC) as rank
FROM student
) ranking ON student.id = ranking.id
I suppose the scale of your data will be a key determinant of whether or not this is a realistic solution for your use case.
I am studying for SQL exam, and I came across this fact, regarding subqueries:
2. Main query and subquery can get data from different tables
When is a case when this feature would be useful? I find it difficult to imagine such a case.
Millions of situations call for finding information in different tables, it's the basis of relational data. Here's an example:
Find the emergency contact information for all students who are in a chemistry class:
SELECT Emergency_Name, Emergency_Phone
FROM tbl_StudentInfo
WHERE StudentID IN (SELECT b.StudentID
FROM tbl_ClassEnroll b
WHERE Subject = 'Chemistry')
SELECT * FROM tableA
WHERE id IN (SELECT id FROM tableB)
There is plenty of reasons why you have to get data from different tables, such as select sth from main query, which is based on subquery/subqueries from another tables. The usage is really huge.
choose customers from main query which is based on regions and their values
SELECT * FROM customers
WHERE country IN(SELECT name FROM country WHERE name LIKE '%land%')
choose products from main query which is greater or lower than average incoming salary of customers and so on...
You could do something like,
SELECT SUM(trans) as 'Transactions', branch.city as 'city'
FROM account
INNER JOIN branch
ON branch.bID = account.bID
GROUP BY branch.city
HAVING SUM(account.trans) < 0;
This would for a company to identify which branch makes the most profit and which branch is making a loss, it would help identify if the company had to make changes to their marketing approach in certain regions, in theory allowing for the company to become more dynamic and reactive to changes in the economy at any give time.
I have a table Mbr that contains 3 fields, GroupType, LeaderID, and MemberID. Basically, all the members in an organization are divided up into these groups, identified by their leader's unique ID (LeaderID). Each member record also has their own MemberID, and the leaders themselves have a unique MemberID as well. The GroupType just designates whether the group a member is in which is considered a Large, Small, or Individual group.
I need to find out how many groups of each GroupType contain a certain number of members.
For example:
How many Large groups contain 6 members, 7 members, 8 members, 9 and so on.
How many Small Groups contain 2 members, 3 members, 4 members and 5 members
How many Individual groups there are.
Is it possible make a query to get a Count of the unique MemberID's for each group, and then get a COUNT of how many LeaderID's have a certain number of members associated to them?
Note: Since you are not specifying which DBMS you are using, I tried to do a basic query. In SQLServer or Oracle this can be much more elegant.
I'm assuming that a given Member can be Leader leader of only one Group if that is correct,
Question #1:
SELECT GroupType, NumberOfMembers, COUNT(LeaderID) AS NumberOfGroups
FROM (
SELECT GroupType, LeaderID, COUNT(*) AS NumberOfMembers
FROM MyTable
GROUP BY GroupType, LeaderID
) AS InnerGrouping
GROUP BY GroupType, NumberOfMembers
ORDER BY GroupType, NumberOfMembers
Question #2:
SELECT UniqueMemberIDPerGroup, COUNT(LeaderID) AS NumberOfLeaderID
FROM (
SELECT LeaderID, COUNT(DISTINCT MemberID) AS UniqueMemberIDPerGroup
FROM MyTable
GROUP BY LeaderID
) AS InnerGrouping
GROUP BY UniqueMemberIDPerGroup
I'm sure you can write some complex query with several subqueries to create a query to give you what you want, but I personally like more straightforward methods. In this case, it would be using some temp tables to store intermediate values. I would first group by several columns (that you are going to use as criterias) with count being the value for the query. I would then store these into a temp table and finally create a query to utilize the temp table to give you the results that you are looking for.
Please forgive my ignorance here. SQL is decidedly one of the biggest "gaps" in my education that I'm working on correcting, come October. Here's the scenario:
I have two tables in a DB that I need to access certain data from. One is users, and the other is conversation_log. The basic structure is outlined below:
users:
id (INT)
name (TXT)
conversation_log
userid (INT) // same value as id in users - actually the only field in this table I want to check
input (TXT)
response (TXT)
(note that I'm only listing the structure for the fields that are {or could be} relevant to the current challenge)
What I want to do is return a list of names from the users table that have at least one record in the conversation_log table. Currently, I'm doing this with two separate SQL statements, with the one that checks for records in conversation_log being called hundreds, if not thousands of times, once for each userid, just to see if records exist for that id.
Currently, the two SQL statements are as follows:
select id from users where 1; (gets the list of userid values for the next query)
select id from conversation_log where userid = $userId limit 1; (checks for existing records)
Right now I have 4,000+ users listed in the users table. I'm sure that you can imagine just how long this method takes. I know there's an easier, more efficient way to do this, but being self-taught, this is something that I have yet to learn. Any help would be greatly appreciated.
You have to do what is called a 'Join'. This, um, joins the rows of two tables together based on values they have in common.
See if this makes sense to you:
SELECT DISTINCT users.name
FROM users JOIN conversation_log ON users.id = converation_log.userid
Now JOIN by itself is an "inner join", which means that it will only return rows that both tables have in common. In other words, if a specific conversation_log.userid doesn't exist, it won't return any part of the row, user or conversation log, for that userid.
Also, +1 for having a clearly worded question : )
EDIT: I added a "DISTINCT", which means to filter out all of the duplicates. If a user appeared in more than one conversation_log row, and you didn't have DISTINCT, you would get the user's name more than once. This is because JOIN does a cartesian product, or does every possible combination of rows from each table that match your JOIN ON criteria.
Something like this:
SELECT *
FROM users
WHERE EXISTS (
SELECT *
FROM conversation_log
WHERE users.id = conversation_log.userid
)
In plain English: select every row from users, such that there is at least one row from conversation_log with the matching userid.
What you need to read is JOIN syntax.
SELECT count(*), users.name
FROM users left join conversion_log on users.id = conversation_log.userid
Group by users.name
You could add at the end if you wanted
HAVING count(*) > 0
I have a database where users enter their interests. I want to find people with matching interests.
The structure of the interest table is
interestid | username | hobby | location | level | matchinginterestids
Let's take two users to keep it simple.
User Joe may have 10 different interest records
User greg may have 10 different interest records.
I want to do the following algorithm
Take Joe's interest record 1 and look for matching hobbies and locations from the interest database. Put any matching interest id's in the matches field. Then go to joe's interest record 2 etc..
I guess what I need is some sort of for loop that will loop through all of joe's intersts and then do an update each time it finds a match in the interest database. Is that even possible in MySQL?
Further example:
I am Dan. I have 3 interests. Each interest is composed of 3 subjects:
Dan cats,nutrition,hair
Dan superlens,dna,microscopes
Dan film,slowmotion,fightscenes
Other people may have other interests
Joe:
Joe cats,nutrition,strength
Joe superlens,dna,microscopes
Moe
Moe mysql,queries,php
Moe film,specialfx,cameras
Moe superlens,dna,microscopes
Now I want the query to return the following when I log in as Dan:
Here are your interest matches:
--- is interested in cats nutrition hair
Joe is interested in cats and nutrition
Joe and Moe are interested in superlens, dna, microscopes
Moe is interested in film
The query needs to iterate through all Dan's interests, and compare 3,2,1 subject matches.
I could do this in php from a loop but it would be calling the database all the time to get the results. I was wondering if there's a crafty way to do it using a single query Or maybe 3 separate queries one looking for 3 matches, one for 2 and one for 1.
This is definitely possible with MySQL, but I think you may be going about it in an awkward way. I would begin by structuring the tables as follows:
TABLE Users ( userId, username, location )
TABLE Interests( interestId, hobby )
TABLE UserInterests( userId, interestId, level )
When a user adds an interest, if it hasn't been added before, you add it to the Interests table, and then add it to the UserInterests table. When you want to check for other nearby folks with similar interests, you can simply query the UserInterests table for other people who have similar interests, which has all that information for you already:
SELECT DISTINCT userId
FROM UserInterests
WHERE interestId IN (
SELECT interestId
FROM UserInterests
WHERE userId = $JoesID
)
This can probably be done in a more elegant fashion without subqueries, but it's what I thought of now.
As per special request from daniel, although it's kind of duplicate but never mind.
The schema explained
TABLE User (id, username, location )
TABLE Interests(id, hobby )
TABLE UserInterest(userId, interestId, level )
Table users has just user data and a primary key field at the start: id.
The primary key field is a pure link field, the other fields are info fields.
Table Interest again has a primary key that is use to link against and some info field
(ehm well just one, but that's because this is an example)
Note that users and interests are not linked in any way whatsoever.
That's odd, why is that?
Well there is a problem... One user can have multiple intrests and intrests can belong to multiple people.
We can solve this by changing to users table like so:
TABLE users (id, username, location, intrest1, intrest2, intrest3)
But this is a bad, really really bad idea, because:
This way only 3 interests per user are allowed
It's a waste of space if many users have 2, 1 or no interests
And most important, it makes queries difficult to write.
Example query for linking with the bad users table
SELECT * FROM user
INNER JOIN interests ON (user.intrest1 = interests.id) or
(user.intrest2 = interests.id) or
(user.intrest3 = interests.id);
And that's just for a simple query listing all users and their interests.
It quickly gets horribly complex as things progress.
many-to-many relationships
The solution to the problem of a many to many relationship is to use a link table.
This reduces the many-to-many relationship into two 1-to-many relationships.
A: 1 userinterest to many user's
B: 1 userinterest to many interests
Example query using a link-table
SELECT * FROM user
INNER JOIN userInterest ON (user.id = userInterest.userID) //many-to-1
INNER JOIN interest ON (interest.id = userInterest.InterestID); //many-to-1
Why is this better?
Unlimited number of interests per user and visa versa
No wasted space if a user has a boring life and few if any interests
Queries are simpler to maintain
Making it interesting
Just listing all users is not very fun, because then we still have to process the data in php or whatever. But there's no need to do that SQL is a query language after all so let's ask a question:
Give all users that share an interest with user Moe.
OK, lets make a cookbook and gather our ingredients. What do we need.
Well we have a user "Moe" and we have other user's, everybody but not "Moe".
And we have the interests shared between them.
And we'll need the link table userInterest as well because that's the way we link user and interests.
Let's first list all of Moe's Hobbies
SELECT i_Moe.hobby FROM interests AS i_Moe
INNER JOIN userInterests as ui2 ON (ui2.InterestID = i_Moe.id)
INNER JOIN user AS u_Moe ON (u_Moe.id = ui2.UserID)
WHERE u_Moe.username = 'Moe';
Now we combine the select for all users against only Moe's hobbies.
SELECT u_Others.username FROM interests AS i_Others
INNER JOIN userinterests AS ui1 ON (ui1.interestID = i_Others.id)
INNER JOIN user AS u_Others ON (ui1.user_id = u_Others.id)
/*up to this point this query is a list of all interests of all users*/
INNER JOIN Interests AS i_Moe ON (i_Moe.Hobby = i_Others.hobby)
/*Here we link Moe's hobbies to other people's hobbies*/
INNER JOIN userInterests as ui2 ON (ui2.InterestID = i_Moe.id)
INNER JOIN user AS u_Moe ON (u_Moe.id = ui2.UserID)
/*And using the link table we link Moe's hobbies to Moe*/
WHERE u_Moe.username = 'Moe'
/*We limited user-u_moe to only 'Moe'*/
AND u_Others.username <> 'Moe';
/*and the rest to everybody except 'Moe'*/
Because we are using INNER JOIN's on link fields only matches will be considered and non-matches will be thrown out.
If you read the query in english it goes like this.
Consider all users who are not Moe, call them U_others.
Consider user Moe, call him U_Moe.
Consider user Moe's Hobbies, call those i_Moe
Consider other users's Hobbies, call those i_Others
Now link i_Others hobbies to u_Moe's Hobbies
Return only users from U_Others that have a hobby that matches Moe's
Hope this helps.