MySQL query for two users with common responses to a survey - mysql

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.

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.

How to properly join two tables to use alternative ORDER BY

Two tables...
people (personid, name, mainordering)
entries (userid, personid, altordering)
"personid" is the common field. My app displays a draggable list users can move around. When done, they click to "lock" in their order.
Table : people
+----------+---------+--------------+
| personid | name | mainordering |
+----------+---------+--------------+
| 1 | Bob | 2 |
| 2 | Charlie | 4 |
| 3 | Jim | 1 |
| 4 | Doug | 3 |
+----------+---------+--------------+
So using mainordering, it would display:
Jim
Bob
Doug
Charlie
entries table might have (for user 16):
+--------+----------+-------------+
| userid | personid | altordering |
+--------+----------+-------------+
| 16 | 1 | 3 |
| 16 | 2 | 1 |
| 16 | 3 | 2 |
| 16 | 4 | 4 |
+--------+----------+-------------+
So if user 16 has already submitted his entry BUT NOT LOCKED IT IN, I want to display his list using altordering. i.e.
Charlie
Jim
Bob
Doug
I'm struggling with the proper join to use. Here is what I tried and isn't working (it's simply ordering by mainordering still)...
$sql = "SELECT * from entries
WHERE userid=".$_SESSION['userid']."
LEFT JOIN people ON entries.personid = people.personid
ORDER BY altordering";
Any thoughts would be much appreciated. Thank you...
Are you sure you don't get an error when using WHERE before JOIN?
It should work like this:
SELECT people.*
FROM people
JOIN entries ON entries.personid = people.personid
WHERE entries.userid={$_SESSION['userid']}
ORDER BY entries.altordering
I assume entries.personid will always have a matching person in people, so you should use an INNER JOIN. You would use FROM entries LEFT JOIN people if you wanted to retrieve altordering even for non-existing people.

Application specific MySQL table Structure

I have a question about my DB table structure. I want to know if i'm on the right track or if I have missed a good alternative. Here is the case:
To make it easy to read, I haven't pasted the full contents as my question is only about the structure.
2 tables:
1: id (AI), task
2: id, name, task
Table 1 presents dynamic check-boxes which can be altered by an admin panel so the contents would be like this
1 task1
2 task2
5 task5
(3 & 4 are missing cause the administrator deleted those records).
In table number two are the people who should do the tasks from table 1. And the goal is that the tasks wich are not checked will be displayed.
So the contents of table 2 would be:
1 Name1 1,5
2 Name2 1,2
3 Name3 1,2,5
The numbers in table 2 represent the checked boxes from table 1. So with a query i can compare the numbers from table 2 with the id's from table 1 and display the missing ids as "todo".
In my opinion this looks very overdone, and there must be an easier way to create dynamic options which can be compared and stored as a todo.
Suggestions are welcome!
I suggest you to use basic structure for many-to-many relationship:
tasks users user_tasks
+----+-----------+ +----+-------+ +---------+---------+
| id | name | | id | name | | user_id | task_id |
+----+-----------+ +----+-------+ +---------+---------+
| 1 | Buy milk | | 1 | John | | 1 | 2 |
| 2 | Get drunk | | 2 | Tim | | 3 | 2 |
| 3 | Have fun | | 3 | Steve | | 2 | 4 |
| 4 | Go home | +----+-------+ | 3 | 4 |
+----+-----------+ +---------+---------+
And you can fetch unassigned tasks using following query:
SELECT
tasks.*
FROM
tasks
LEFT JOIN
user_tasks
ON (tasks.id = user_tasks.task_id)
WHERE
user_tasks.user_id IS NULL
You also can fetch users who have no assigned tasks:
SELECT
users.*
FROM
users
LEFT JOIN
user_tasks
ON (users.id = user_tasks.user_id)
WHERE
user_tasks.user_id IS NULL
Hope this will help you.

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.

mutual non-mutual friend query mysql

Hello everyone I have been trying this for ages now.
I have read many questions here and tried adapting the varied solutions to my needs but without results.
History:
for an event there are many participants.
the participants all meet one another at the event and give out "likes" to all the other participants they actually like.
At the end of the event the admin inserts all the likes for each participant of THAT event, and the system will find the mutual likes (friendship)
Problem:
While inserting the likes i would like (pun) the system to detect weather a friendship is already established (from other events also) and if so avoid to display that user name when setting the likes.
Here are the tables that I'm using (mysql)
wp_fd_users
id | user_name | user_gender | .. etc
wp_fd_matches
id | event_id | event_user_id | event_user_match_id | ... etc
Example of the match table
1 | 1 | 1 | 3 | ...
2 | 1 | 1 | 4 | ...
3 | 1 | 2 | 6 | ...
4 | 1 | 3 | 1 | ...
where you can clearly see that 1 <-> 3 have a mutual relationship and 1 likes 4 but not mutually.
I would need a query that returns all results that AVOID relationships that have been established in one single event.
An occurance like this:
1 | 1 | 1 | 3 | ...
2 | 1 | 1 | 4 | ...
3 | 1 | 2 | 6 | ...
4 | 2 | 3 | 1 | ...
would not trigger the like because it happens in two separate events
Hope it's clear
YOur question is a little unclear. I am going by: "I would need a query that returns all results that AVOID relationships that have been established in one single event."
The following self join accomplishes this:
select m1.*
from wp_fd_matches m1 left outer join
wp_fd_matches m2
on m1.event_id = m2.event_id and
m1.event_user_id = m2.event_user_match_id
m1.event_user_match_id = m2.event_user_id
where m2.id is null
It looks for the matching record. However, by using a left outer join, it is getting all records. It then filters out the ones with a match.