Sorting results of mysql query over multiple columns - mysql

I have a table for a list of people allowed in a building. Each resident is allowed to give 10 nonresidents access.
resident guest1 guest2 ... guest10
I want to search over all the guests, and then display the results in alphabetical order. Currently I have:
$result = mysql_query("SELECT * FROM residentlist WHERE guest1 LIKE 'searchquery' OR guest2 LIKE 'searchquery' ... OR guest10 LIKE 'searchquery'";
But I end up with extra guests that aren't like 'searchquery', so I need to do a bunch more if checks.
I'm new to mysql - am I missing something simple?
Also, I'm open to restructuring the table / adding tables if that's better.

You should restructure the tables. Create a new table called access or something with the fields accessId, residentId and guest.
accessId is the primary key, it is AUTO_INCREMENT INTEGER.
resident is the foreign key to the residentlist table.
guest is whatever you now search using your LIKE condition.
So for your 10 guests per resident, you will have 10 records in that access table.
Once you have that, you can use the query:
SELECT * FROM residentlist LEFT JOIN access ON access.residentId = residentlist.resident WHERE guest LIKE '<search query>';

Your current select statement will return all guests for a resident if a single one matches your search query. So if Bob allows Jack and Jane and you search for Jane, it'll return the entire row of Jack and Jane.
Yes, this should be redesigned to be a separate table that is simply Resident, Guest. The 10 maximum should be enforced on the application side rather than the database. See Shi's answer for an excellent explanation on how to structure it moving forward.
If you need to keep your current structure, you can obtain the desired results through the UNION operator.
SELECT guest1 FROM residentlist WHERE guest1 LIKE 'searchquery'
UNION SELECT guest2 FROM residentlist WHERE guest2 LIKE 'searchquery'
...
UNION SELECT guest10 FROM residentlist WHERE guest10 LIKE 'searchquery'

I feel like the database could be better organized. As a general rule, if you have a field name that is blah1, blah2, ... blahX, that is usually a red flag.
Anyways, sounds like you have two objects: a guest and a resident, and then a relationship between them.
So, you can have a resident table that has information about the resident and a unique identifier for the resident like residentId. So you have:
ResidentId
Name
Address ....
Then you have a guest table that specifies information about the guests:
GuestId
Name
whatever else...
Then you have a third table that actually relates the guest and resident (guestToResidentTable). This will have two fields:
ResidentId
GuestId
you might also want a unique id for each of these relationships - making three fields in this table
Both id fields are foreign keys from your other tables.
This way every guest will be related to each resident and like Derek said, you enforce the 10 guest limit in your application (not the database).
Then you can search the guest table for all guests like your search query. If you then want information about the guest and what resident he is related to, you just join on the other tables.

Related

SQL - Hard time building a "complex" request

I've got three tables :
conversations (contains private conversations)
conversations-members - structure : convId | userId (contains all ids of members participating to a conversation so multiple people can talk together. One user participating to a conversation equals one row)
users (users table, classic)
What I am trying to do is :
Users have friends. So, the user browsing my application can open a conversation from his friends' ids.
So, first, I want to look up in conversations-members if there is an existing conversation ONLY between user's Id and his friend's Id and then, pick up the conversation id in conversations table that conversations-members gave me.
Is it possible to do this in one request? If I have to do two requests, I don't even know how to build the first one (find the rows that contain user's Id and friend's Id that have conversation Id in common).
My first idea was to make a single conversations table which would also contain member's Id in the form of a string like "55,105,85,22" and then parse it to get an array, but I think the way I want to do it gives me more options and could be simpler if I manage to handle the SQL requests I need.
To find conversations involving just two particular users, you can do something like this.
SELECT convId, COUNT(*)
FROM `conversations-members`
WHERE userId IN (FIRST_USER, SECOND_USER)
GROUP BY convId
HAVING COUNT(*) = 2
The HAVING line filters out any conversation with any users besides the two you want.
You can use that as a subquery:
SELECT whatever
FROM conversations
WHERE convId IN (
SELECT convId
FROM `conversations-members`
WHERE userId IN (FIRST_USER, SECOND_USER)
GROUP BY convId
HAVING COUNT(*) = 2
)
If you wanted conversations with your two users and any other users, you could change the subquery to HAVING COUNT(*) >= 2.
Pro tip: SQL thinks hyphens - mean subtraction. Avoid them in column and table names.
It's not recommended to use a column with multiple values.
When you have a relation 1-N, normally you need to use a table with two keys, for example, ConvID - UserID. Your index are created by these two columns, for don't allow to generate a duplicate (an UserID two times on the same ConvID, for example).
But, in response to your question, yes, it's possible to do that search. You need to search for an entry on your SQL table where two different IDs are on the same line. You can do it with LIKE clause, but it's not a good option (I think it's the worst possible WHERE clause).
The syntax will be as follows:
SELECT convId FROM conversations-members WHERE userId LIKE '%id1%' AND userId LIKE '%id2%'
For some additional recommendation, if your conversations are always a pair of people, use two new fields on your "conversations" table. If your conversation can have from one to N members, use the "conversations-members" as suggested at the start of the answer.

Mysql query for select data from multiple table with comparision

I have a three tables namely profile, academic,payment and these tables having two same columns that are username and status.
my problem is how to select username from the tables where status=1 in all the tables
Typically it works like this:
SELECT * FROM profile
LEFT JOIN academic ON profile.username=academic.username
LEFT JOIN payment ON profile.username=payment.username
WHERE profile.status=1 AND academic.status=1 AND payment.status=1
As a note having username as a key is usually a bad thing, often super bad since if someone's able to change their name you need to update N other tables. You may have a circumstance where you forget to update one or more tables, then subsequently someone registers with the former name and "inherits" this data.
It's also typically very inefficient to use a string INDEX key when a user_id integer value would suffice.

Mysql: is it better to split tables if possible?

To make you understand my question I'll give you an example:
I have a chat web app with many rooms, let's say 5 rooms.
People can choose to stay only in one room and they choose it at login.
When they choose the room I have to retrieve the people already in the room, so I can structure my db in two ways:
each room one table with the people being records;
all the rooms in one table, people are the records and a column indicating the room they are in;
In the first case the query would be:
SELECT * FROM 'room_2' WHERE 1
In the second case the query would be:
SELECT * FROM 'rooms' WHERE room = 'room_2'
Which is the best?
I think the only parameter to consider is performance, right?
In this example, no, because people are all 'like' objects and should therefore be in the same table.
All people and rooms in one table with a primary key on people, in this simple example.
Table Rooms(pk_person, personName, table_id)
But I want to talk about a structure that you will want to consider as your website grows. You’ll want three tables, one for each object (chat rooms, people) and one for the relationships.
Chat_Rooms(pk_ChatId, ChatName, MaxOccupants, other unique attributes of a chat room)
People(pk_PersonID, FirstName, LastName, other unique attributes of a person)
Room_People_Join(pk_JoinId, fk_ChatId, fk_PersonID, EnterDateTime, ExitDateTime)
This is a “highly normalized” structure. Each table is a collection of like objects, the join allows for many to many relationships, and object rows are not duplicated. So, a Person with all their attributes (name, gender, age) is never duplicated in the person table. Also, the person table never defines which chat rooms a person is in, because a person could be in one, many, none, or may have entered and exit multiple times. The same concept applies to a chat room. A chat rooms features, such as background color, max occupants, etc. have nothing to do with people.
The Room_People_Join is the important one. This has a unique primary key for which chat rooms a person is in and when they were there. This table grows indefinitely, but it tracks usage. Including the relationship table is what logically normalizes your database.
So how do you know which users are currently in chat room 1? You join your people and rooms to the join table with their respective Primary and Foreign keys in your FROM clause, ask for the columns you want in your SELECT clause, and filter for chat room 1 and people who haven’t yet left.
SELECT p.FirstName, p.LastName, r.ChatName
FROM Room_People_Join j
JOIN People p ON j.fk_PersonID = p.pk_PersonID
JOIN Chat_Rooms r ON j.fk_ChatId = r.pk_ChatId
WHERE r.ExitDateTime IS NOT NULL
AND pk_ChatId = 1
Sorry that’s long winded, but I extrapolated your question for database growth.
The answer is very simple and strongly recommended - one database table for all rooms for sure! What if you will later like to create rooms dynamically!? For sure you would not create new tables dynamically.

How to store customer data in mysql (2 tables vs 1 table)

I was thinking that I would have two tables for mysql. One for storing login information and the other for shipping address. Is that the conventional way or is everything store in one table?
For two tables... is there a way where it automatically copies a column from table A to table B, so that I can reference the same id to grab their shipping address...
If its a single address and if it is going to be updated everytime , then you can have it in a single table something like
**Customer**
customer_id [pkey]
customer_name
login_id
password
shipping_address
whereas if you want to store all the shipping addresses for a single customer(across multiple visits) then it would be a good design to have another table customer_shipping_address
**Customer**
customer_id [pkey]
customer_name
login_id
password
**Customer_Shipping_Address**
customer_id [fkey to customer]
shipping_address
This is my answer to your question regarding using 1 table or 2 tables. This decision depends on may factors. But i would suggest that you should use 2 separate tables. Because the log-in information is something that you will be retrieving very often compare to shipping information. Now if you have all the info in one table then table size will be huge and you will have to query this huge table everytime you need login information of user.
I think using two tables is better way to go. then just join them when you want to do the shipping.
The SQL for that would be like this.
SELECT
table1.id, table2.id, table2.somethingelse, table1.somethingels
FROM
table1 INNER JOIN table2
ON table1.foreignkey = table2.primarykey
WHERE
(some conditions is true)
The code above would need to be run on the shiping page itself.

MySQL - Best performance between 2 solutions

I need and advice about MySQL.
I have a user table that have id, nickname, numDVD, money and table DVD that have idDVD, idUser, LinkPath, counter.
Now I belive that I could have max. 20 user and each user has about 30 DVD.
So when I insert a DVD I should have idDVD(auto-Increment), idUser (same idUser of User table), LinkPath (generic String), and counter that it is a number from 1 to 30 (unique number) (depends from number or DVD) for each user.
The problem is handle the last column "counter", because I would select for example 2 3 random DVD from 1 to 30 that have the same UserId.
So I was thinking if it's the best solution in my case and hard to handle (for me I never used MySQL) OR it's better create 20 tables (1 for each user) that contains the ID and DVDname etc.
Thanks
Don't create 20 tables! That'd be way overkill, and what if you needed to add more users in the future ? It'd be practically impossible to maintain and update reliably.
A better way would be like:
Table users
-> idUser
-> other user specific data
Table dvd
-> idDvd
-> DVDname
-> LinkPath
-> other dvd specific data (no user data here)
Table usersDvds
-> idUser
-> idDvd
This way, it's no problem if one or more users has the same DVD, as it's just another entry in the usersDvds table - the idDvd value would be the same, but idUser woudl be different. And to count how many DVDs a user has, just do a SELECT count(*) FROM usersDvds WHERE userId = 1
You don't need a table per user, and doing so will make the subsequent SQL programming basically impossible. However with these data volumes practically nothing you do is going to cause or relieve bottlenecks. Very probably the entire database will fit into memory so access via any schema will be practically instantenous.
If I understand your requirements clearly, you should be able to accomplish that by creating a compound index for you to be able to select efficiently.
If there is too much of data that is being handled in that table, then it would help to clear up some historical data.