Multiple JOIN, with one table needed two times - sql

i am trying to make up this situation in my app: there are users who can select the languages they speak (such as english, spanish, japanese and so on) and users who can confirm the languages selected by others users (like in LinkedIn, where you can confirm the skill of another person).
I have 4 tables in a DBRM: Users, Languages, Users_Language and Users_Confirmations.
The bodies:
Users
id name lastname ...
Languages
id name
Users_Languages
user_id language_id
Users_Confirmations
user_id language_id other_id ------> here other_id refers to Users table, like user_id. They both refer to Users table, since an user can confirm another user
For example:
John Smith speaks English, Spanish and Danish.
Luke Red and Jennifer Brown confirmed English on John Smith.
Now, I want to get all Users who, for example, have confirmed the user with id "4", and show them in a HTML list. And I would want to get it with both PURE SQL and Laravel (Eloquent or Query Builder).
Thank you in advance!

Here you have to change your table attributes to avoid repetition, use just
Users_Languages_ID as a foreign key to refer to the Users_Langages table
-- Users_Confirmations
confirmation_id user_language_id
You can just use Users_Confirmations, and for each confirmation added, you will insert a row that contains user_language_id
Returning to the question(to get users who confirmed the user with id "4")
SELECT Users.name, Users.lastname, Languages.name
FROM Users_Confirmations
JOIN Users_Languages ON Users_Languages.id = Users_Confirmations.user_language_id
JOIN Languages ON Languages.id = Users_Languages.language_id
JOIN Users ON Users.id = Users_Languages.user_id
WHERE Users_Languages.user_id = 4

Related

Storing user created folders in SQL database

I am a high school student building an app for students in my school to store their grades and help with their organisation overall. In this app the users can create folders and add grades to these folders. I am using a SQL database for storing the grades. My database currently looks something like this:
I have two tables to store the information, the first one is for the subjects like this:
UserID
ID
DisplayName
ParentID
abcd
1
English
null
defg
2
Vocabulary
1
...
...
...
...
Note that the ParentID is either the ID of another folder to create nested folders, or null.
A table for the grades:
UserID
ID
FolderID
Grade
DisplayName
abcd
1
1
A
Grammar Test
defg
2
2
B
Vocabulary pages 12-13
...
...
...
...
...
Here the FolderID refers to a folder from the table above.
As well as a table for the users:
UserID
FirstName
LastName
Email
Password
abcd
Alice
Smith
alice.smith#example.com
$$
defg
Bob
Brown
bob.brown#example.com
$$
...
...
...
...
...
Upon creating an account the user can decide to either chose a configuration template or skip this step. When he chooses a template he is prompted to choose his subjects from a few options (for example you can choose the second language between German and Italian). And these folders then get created and added to the database.
I now want to store the choices the user made to do some data analysis with it. The problem is that I want the user to be able to edit the folder name if he wants to... A nice bonus would be if I could translate the folders using i18n.
I have thought about creating a new table with all possible subjects, each getting a new id which is a prime number so I can multiply them to store the users unique configuration as well as creating a new column in the table for the SubjectID in the tables and query all the folders from a user. Would this be a good idea?
Do you have any other suggestions for me? (this is my first project of this size and I am happy for all feedback)
I would change the approach. I would have a table for subjects of the format of:
subjects(id, parentID, displayName)
and there would be a many-to-many relation for subject-user, like
user_subjects(id, userID, subjectID, displayName)
that would represent that a user is learning a given subject. Finally, I would create a custom_subjects table of the format of
custom_subjects(id, subjectID, userID, displayName)
so, if a user has a custom displayName, then that would be used and the value would fall back to the user_subjects's displayName, a common pattern in queries would be the following:
SELECT COALESCE(custom_subjects.displayName, subjects.displayName)
FROM users
JOIN user_subjects
on users.id = user_subjects.userID
JOIN subjects
on user_subjects.subjectID = subjects.id
LEFT JOIN custom_subjects
ON subjects.id = custom_subjects.subjectID AND users.id = custom_subjects.userID
This way you will have a default to fall back to and you could override it for each user. I'm unsure about your intention about i18n, you have provided some information about that, but not enough for me to include ideas about it into my answer.

Filtering out records in SQL with a join

I am creating an app that makes guest lists for greek life events at universities.
The two tables I am working with are 'student' table and 'participant' table.
The fields in the student table are: student_id, student_name, university, and chapter.
Students with chapter id's are considered members, and students without chapter id's are considered guests when making guest lists(participant table).
The participant table fields are: participant_id, member(which is related to student_id), guest (which is also related to student_id), and event.
When trying to add guests to the guest list for an event, I wrote the following sql query to filter out students from different universities and that aren't in chapters and weren't already on the list:
$student = getColumn("SELECT guest FROM participant WHERE event = '$event'");
$university = getSqlValue("SELECT university FROM student WHERE student_id = '$member'");
$f->setOption('filter',
"SELECT student_name
FROM `student`
LEFT JOIN participant ON student.student_id = participant.guest
WHERE student.chapter = ''
AND student.university = '$university'
AND participant.guest != '$student'");
So, I know this isn't going to work, because I have a whole array for $student, but even if I try it with one student id, the query doesn't work. It returns empty. If I remove the last AND particpant.guest!= $student, then the query returns all the students at the university that are not members of a chapter.
My question has two parts:
Why wouldn't that query work with one value for student?
Can someone think of a better way to go about doing this?

SQL in vs intersection

I want to select results from a table
USER HOBBY
John Sport
Ann Piano
Lee Reading
Ann Sport
Lee Piano
And I want to search people who have more than one common hobbies. Which query has better performance,
select user from table where hobby = "sport"
intersect
select user from table where hobby = "piano"
Or
select user from table where user in (select user from table where hobby = "piano") and where hobby = "sport"?
best performance is from
select user from table where hobby = "sport" or hobby = "piano"
but if this is just a teorical excample, then many test have been made, here some:
http://www.execsql.com/post/intersectexcept-versus-innot-in
BTW there isn't a better solution than try out by yourself the execution time of the query
In Oracle, assuming (USER, HOBBY) is unique, you could use a GROUP BY query:
SELECT user
FROM user_hobby
WHERE hobby IN ('piano', 'sport')
GROUP BY user
HAVING COUNT(*) = 2
This will make Oracle perform at most a single pass on data whereas the INTERSECT would treat each query separately and would therefore need two passes.

sql query with "with and in clause"

i have a table which store user name, hobby and city .hobby field contain different hobby joined using "," operator eg swimming, basket, cricket. I want to search user name who match at least one hobby according to my search criteria.
You should not have multiple attributes in one column. That's one of the number one rules of 3nf database design. Now you have to figure out ways to parse this data. This issue only gets worse and worse each and every day. Seperate the hobbies as multiple rows in your database.
I agree with #JonH that there shouldn't be more than one piece of information in a column. It stops the row being truly atomic.
But you are where you are, and you can use the LIKE clause to return rows that match a substring within a column.
Something like:
select hobbycolumn from hobbytable where hobbycolumn like '%swimming%'
for example
To do this properly you need to restructure your tables if possible. For what you are looking for a possible way would be to have 3 tables. I'm not sure who the city belongs to, so I put it with the user.
1 for user with the following cols:
id
name
city
A table for for hobbies:
id
name
And a user_hobbies join table that allows each user to have multiple hobbies, and each hobby to have multiple users:
id
user_id (foreign key)
hobby_id (foreign key)
Then searching for a user with a certain hobby is:
SELECT user.id, user.name FROM user
INNER JOIN 'user_hobbies' on user_hobbies.user_id=user.id
INNER JOIN 'hobbies' on hobbies.id = user_hobbies.hobby_id
WHERE hobbies.name LIKE "query";

where do i store label info in a contacts database for mailings

I am trying to setup a contacts database for mailings and I am trying to fully automate the labels but can't figure out how.
Where in the database would I store the name that would appear on the top of a mailing label:
mr & mrs joe thomson
dr. and mrs james berry
Schwartz family
This seems like it would have to be a calculated field based on a number of different pieces of data.
Any suggestions on how you have a mailings database and generate names for labels directly?
Building off the data model I suggested in your previous question, I'll update the CONTACTS table to include:
SALUTATION (Mr, Mrs, Dr, etc)
I would determine to use "Thompson Family" versus Mr. Joe Thompson and Mrs. Terry Thompson and Joe and Billy based on the number of personal contacts = 2+ for the same address, with the same last name.
References:
salutation
A fairly normalized design would look something like:
Location(addr_id,primary_contact_id, street_addr, city, post_code, country)
Contact(contact_id,first_name, last_name, title);
LivesAt(contact_id,addr_id)
MarriedTo(contact_id_1,contact_id_2)
ChildOf(parent_id,child_id)
These would basically be your tables. Then you could create views:
1.Family. Assume a family is at least one parent and a child living at the same address, and they share the same surname (in the case of a parent and child having different surnames, you will address the letter to them both by their full names).
CREATE VIEW Family AS
SELECT UNIQUE last_name, addr_id, street_addr, city, post_code, country FROM
(SELECT p1.contact_id, p1.first_name, p1.last_name FROM Contact AS p1)
INNER JOIN
(SELECT p2.contact_id, p2.first_name, p2.last_name FROM Contact AS p2)
ON (p2.last_name = p1.last_name AND p2.contact_id IN ChildOf
AND p1.contact_id IN ChildOf)
INNER JOIN
Location AS l
ON (p1.contact_id = l.primary_contact_id)
OR (p2.contact_id = l.primary_contact_id)
Format as you see fit.
2.Married couples with no children.
CREATE VIEW Couple AS
SELECT * FROM
(SELECT C.contact_id, C.last_name, C.title FROM Contact AS C
INNER JOIN MarriedTo AS M
ON (M.contact_id_1=C.contact_id)
INNER JOIN
SELECT D.contact_id, D.last_name, D.title FROM Contact as D
ON (M.contact_id_2=D.contact_id)
INNER JOIN Location AS L
ON
L.addr_id NOT IN Family
AND (L.primary_contact_id = M.contact_id_1)
OR (L.primary_contact_id = M.contact_id_2)
And so on.
I personally don't like encoding these kind valid values in the database column definition. You'd have a large administration overhead. Managing these values in a distinct table is better, but having a foreign key into another table you to read the label just didn't seem right to me. Last time I needed to do something similar I added the column for the label as a simple varchar column.
But what do you do to avoid duplicates and very similar labels (e.g. "Mr" and "Mr.")? I added an index on the column and added an AJAX query in the frontend to list all distinct labels available and perfom an autocompletion. This works really awesome because
the user is not forced to scroll through a long list of possible values
you don't need to administrate the values yourself
This way you'd allow any label including "Mr. and Mrs." or "Prof. Dr. Dr.".
Given your clarifications - I would create an additional table.
I'm assuming you have a "contacts" table which contains a distinct list of people.
You could also have a "household" table which contains a list of last names or households. I would put the address in THIS table.
Then, the each contact person would have a field linking them to a household (even if there is only one person per household.)
Each contact person would also have a field for "primary contact" containing a 1/0 value.
Then, you could have query logic something like the following:
if count(*) of contacts per household = 1 then
label = contact.title & contact.nameinfo
if count(*) of contact per household = 2 and both of those contacts are primary contacts then
label = primarycontact.title & name plus primarycontact2.title & name
else
label = household.lastname & "family"
You'll want to play with the logic to get it perfect, but the real key is having a household table with a separate address and a contact table with the people within that address.