Optimising table design or optimising the query - mysql

I am trying to decide which one is better: to design a table that wastes a lot of space and has a simple query OR to write a very tight table but then the process of finding what I am looking for would be very processing intense.
The actual problem is this:
Imagine you have a very simple table. 1st column for the ID number the 2nd is a list of names and the 3rd is a list of names too. The 2nd column is a list of people who owe to the people in the 3rd column.
The search should do the following:
I search for a name in the 3rd column and see who owes this person in the 2nd column. A name or multiple names come up, then I want to see who owes them, again a bunch of names come up, and so on to level 5.
Maybe this is a well known scheme for which there is a well known simple answer in table design or MySQL circles. Could anybody suggest a MySQL query or perhaps an appropriate table design where I can use a simple query?
Example
ID owes owned to
1 Peter John
2 John George
3 Abdul George
4 George Anna
So I could design a wasteful table like this
ID 1 2 3 4 5
1 Anna George Abdul
2 Anna George John Peter
3 George Abdul
4 George John Peter
5 John Peter
But this would be very wasteful and bad bad design but it would be very easy to access the data along with the hierarchy and the owing chain.

Something like this seems suitable:
people
+----+--------+
| id | name |
+----+--------+
| 1 | Marty |
| 2 | Steven |
| 3 | John |
+----+--------+
With the table building the relationships between people owed and owing:
loans
+-----------+-------------+
| lender_id | borrower_id |
+-----------+-------------+
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
+-----------+-------------+
You could get all the people owing a given lender with something as simple as:
SELECT people.id, people.name
FROM loans
INNER JOIN people ON people.id = loans.borrower_id
WHERE loans.lender_id = X
Where X is the id of the lender. Given the lender_id of 1 (Marty) for example would yield:
+----+--------+
| id | name |
+----+--------+
| 2 | Steven |
| 3 | John |
+----+--------+
You can repeat this process for each of the resulting people until there are no results (no one being owed).

Related

MySQL: Group users by cars owned

I'll be honest, MySQL is really not my forte, but I'm learning and knowledge is what I'm seeking here :) I am trying to figure out how to churn out a combined grouped data which eventually will be output as nested JSON (JSON part not necessary for now). So..
I've got a table of people...
user_info:
-----------
id | name
---+-------
1 | Joh
2 | Doe
3 | Bob
along with a table of cars...
cars:
-----------
id | cars
---+-------
1 | Toyota
2 | Honda
3 | Mazda
and a table that groups them, with their registration numbers...
user_cars:
-----------------------------
id | user_id | car_id | reg
---+---------+--------+------
1 | 1 | 2 | AB1
2 | 2 | 3 | BC2
3 | 3 | 1 | CB2
4 | 3 | 1 | AC3
A person can have more than one car and I would like to generate a grouped table as such:
---------------
user | car1
| car2
------+--------
user | car1
| car2
------+--------
I tried the following query:
SELECT
user_info.id,
user_info.name,
user_cars.car_id,
user_cars.reg
FROM
user_info
RIGHT OUTER JOIN user_cars ON user_cars.user_id = user_info.id;
But that's not really what I want. It gives me duplicate of names which doesn't help. Any enlightenment would be very much appreciated.
I'm pretty sure this might have been asked on SO, and I'm using the wrong keywords to search most probably. Like I mentioned, knowledge is what I seek. If it's a redirect to another existing post, that would be very much appreciated too.

Error in Mysql query with LIKE %...%

I am trying to update a table using another table. I am not able to determine how to use LIKE %…% feature here. Please help me out.
Table1:
TableToBeUpdated:
id | location | value
------------
1 | california | I am going to be here soon.
2 | Nill | Hello I love playing chess and yes.
3 | Nill | my hotel room is just shitty!
4 | Nill | Why on earth God doesn’t live on earth!
5 | Nill | friends of friends and their dogs.
Table2:
TableToCheckFrom :
uniqueid | location | keyword
---------------------
1 | Texas | Why on earth
2 | NewYork | friends and their
3 | Washington | love playing chess
4 | NewYork | their dogs
The result should be:
id | location | value
------------
1 | California | I am going to be here soon.
2 | Washington | Hello I love playing chess and yes.
3 | Nill | my hotel room is just shitty!
4 | Texas | Why on earth God doesn’t live on earth!
5 |NewYork| friends of friends and their dogs.
——
I am using this formula but its giving me constant error:
UPDATE TableToBeUpdated, TableToCheckFrom
SET TableToBeUpdated.location = TableToCheckFrom.Location
WHERE TableToBeUpdated.Value LIKE %TableToCheckFrom.Keyword%
Thanks in Advance!
You need to put the % in quotes and concatenate them to the keyword. And you need to join the two tables so you can refer to columns from both of them.
UPDATE TableToBeUpdated AS u
JOIN TableToCheckFrom AS c ON u.Value LIKE CONCAT('%', c.Keyword, '%')
SET u.location = c.Location
You query is wrong (you never tell the DBM that you want to access the TableToCheckFrom table).
For an idea on how write such queries look at this question
Update mysql table with data from another table
If you find the like %% as an additional difficult strip it: first prepare a working query with a simply condition and then add the like one.

MySQL query for two users with common responses to a survey

I have a MySQL table with users who have completed a survey - in some cases, they have complete the survey multiple times. So it looks like this:
users|survey_attempt|question_num|response
---------------------------------------------
john | 1 | 1 | cat
john | 1 | 2 | dog
john | 1 | 3 | frog
john | 2 | 1 | dog
john | 2 | 2 | frog
john | 2 | 3 | dog
jim | 1 | 1 | frog
jim | 1 | 2 | bat
jim | 1 | 3 | bat
jim | 2 | 1 | cat
jim | 2 | 2 | frog
jim | 2 | 3 | bat
In this case, how would I find users who had common responses within the same attempt at the survey? So for instance, if I wanted to know who answered "frog" and "cat" within a unique attempt at the survey (regardless of which specific question the answer was for)?
In general, the database layout has flaws. I would suggest to use unique survey submission IDs. Because right now, you need to check user name AND survey attempt to determine if two or more rows belong to the same submission.
Anyways, you would need to self join the table and check for the answer you want but disregard the question:
SELECT A.users, A.survey_attempt
FROM table A
INNER JOIN table B ON A.users = B.users AND A.survey_attempt = B.survey_attempt
WHERE A.response = 'frog'
AND B.response = 'cat';
The table is matched with itself, in each result table you'll have all columns two times. Then the query will only select these rows where both user names and survey attempt numbers are equal. Finally, the WHERE statement checks for the answers you wanted. Nowhere, the question number is checked as you wanted to get the result regardless of specific questions.

How to store multiple values in single column where use less memory?

I have a table of users where 1 column stores user's "roles".
We can assign multiple roles to particular user.
Then I want to store role IDs in the "roles" column.
But how can I store multiple values into a single column to save memory in a way that is easy to use? For example, storing using a comma-delimited field is not easy and uses memory.
Any ideas?
If a user can have multiple roles, it is probably better to have a user_role table that stores this information. It is normalised, and will be much easier to query.
A table like:
user_id | role
--------+-----------------
1 | Admin
2 | User
2 | Admin
3 | User
3 | Author
Will allow you to query for all users with a particular role, such as SELECT user_id, user.name FROM user_role JOIN user WHERE role='Admin' rather than having to use string parsing to get details out of a column.
Amongst other things this will be faster, as you can index the columns properly and will take marginally more space than any solution that puts multiple values into a single column - which is antithetical to what relational databases are designed for.
The reason this shouldn't be stored is that it is inefficient, for the reason DCoder states on the comment to this answer. To check if a user has a role, every row of the user table will need to be scanned, and then the "roles" column will have to be scanned using string matching - regardless of how this action is exposed, the RMDBS will need to perform string operations to parse the content. These are very expensive operations, and not at all good database design.
If you need to have a single column, I would strongly suggest that you no longer have a technical problem, but a people management one. Adding additional tables to an existing database that is under development, should not be difficult. If this isn't something you are authorised to do, explain to why the extra table is needed to the right person - because munging multiple values into a single column is a bad, bad idea.
You can also use bitwise logic with MySQL. role_id must be in BASE 2 (0, 1, 2, 4, 8, 16, 32...)
role_id | label
--------+-----------------
1 | Admin
2 | User
4 | Author
user_id | name | role
--------+-----------------
1 | John | 1
2 | Steve | 3
3 | Jack | 6
Bitwise logic allows you to select all user roles
SELECT * FROM users WHERE role & 1
-- returns all Admin users
SELECT * FROM users WHERE role & 5
-- returns all users who are admin or Author because 5 = 1 + 4
SELECT * FROM users WHERE role & 6
-- returns all users who are User or Author because 6 = 2 + 4
From your question what I got,
Suppose, you have to table. one is "meal" table and another one is "combo_meal" table. Now I think you want to store multiple meal_id inside one combo_meal_id without separating coma[,]. And you said that it'll make your DB to more standard.
If I not getting wrong from your question then please read carefully my suggestion bellow. It may be help you.
First think is your concept is right. Definitely it'll give you more standard DB.
For this you have to create one more table [ example table: combo_meal_relation ] for referencing those two table data. May be one visible example will clear it.
meal table
+------+--------+-----------+---------+
| id | name | serving | price |
+------+--------+-----------+---------+
| 1 | soup1 | 2 person | 12.50 |
+------+--------+-----------+---------+
| 2 | soup2 | 2 person | 15.50 |
+------+--------+-----------+---------+
| 3 | soup3 | 2 person | 23.00 |
+------+--------+-----------+---------+
| 4 | drink1 | 2 person | 4.50 |
+------+--------+-----------+---------+
| 5 | drink2 | 2 person | 3.50 |
+------+--------+-----------+---------+
| 6 | drink3 | 2 person | 5.50 |
+------+--------+-----------+---------+
| 7 | frui1 | 2 person | 3.00 |
+------+--------+-----------+---------+
| 8 | fruit2 | 2 person | 3.50 |
+------+--------+-----------+---------+
| 9 | fruit3 | 2 person | 4.50 |
+------+--------+-----------+---------+
combo_meal table
+------+--------------+-----------+
| id | combo_name | serving |
+------+--------------+-----------+
| 1 | combo1 | 2 person |
+------+--------------+-----------+
| 2 | combo2 | 2 person |
+------+--------------+-----------+
| 4 | combo3 | 2 person |
+------+--------------+-----------+
combo_meal_relation
+------+--------------+-----------+
| id | combo_meal_id| meal_id |
+------+--------------+-----------+
| 1 | 1 | 1 |
+------+--------------+-----------+
| 2 | 1 | 2 |
+------+--------------+-----------+
| 3 | 1 | 3 |
+------+--------------+-----------+
| 4 | 2 | 4 |
+------+--------------+-----------+
| 5 | 2 | 2 |
+------+--------------+-----------+
| 6 | 2 | 7 |
+------+--------------+-----------+
When you search inside table then it'll generate faster result.
search query:
SELECT m.*
FROM combo_meal cm
JOIN meal m
ON m.id = cm.meal_id
WHERE cm.combo_id = 1
Hopefully you understand :)
You could do something like this
INSERT INTO table (id, roles) VALUES ('', '2,3,4');
Then to find it use FIND_IN_SET
As you might already know, storing multiple values in a cell goes against 1NF form. If youre fine with that, using a json column type is a great way and has good methods to query properly.
SELECT * FROM table_name
WHERE JSON_CONTAINS(column_name, '"value 2"', '$')
Will return any entry with json data like
[
"value",
"value 2",
"value 3"
]
Youre using json, so remember, youre query performance will go down the drain.

Select rows which do not have the first column matching any other rows

I need to select the rows that do not have the first column matching. For example, from the data below;
Person | Room
---------------------------------------
ben | 1
jake | 3
jake | 1
steven | 2
james | 1
james | 2
james | 3
The query would only return these rows:
Person | Room
---------------------------------------
ben | 1
jake | 3
steven | 2
james | 1
It doesn't matter what value the room column returns.
And it needs to be able to work with increasing room numbers and different names.
I've had no look searching for a answer and can't figure out how to do it, however it might be my current mindset and it might be really easy to do.
SELECT Person, MIN(Room) AS Room
FROM YourTable
GROUP BY Person
And if you can guarantee that the ONLY_FULL_GROUP_BY setting will always be turned off, the following is also possible in MySql:
SELECT Person, Room
FROM YourTable
GROUP BY Person
But I'd use the MIN() function just to be safe...