Link to multiple foreign rows from one row - mysql

I really don't know how this is called so I couldn't find an answer.
For example I have the following tables:
Table products Table users
+----------+----------+----------+ +----------+----------+
| id | name | price | | username | products |
+----------+----------+----------+ +----------+----------+
| 1 | Bread | 1.5 | | James | 1, 2 |
+----------+----------+----------+ +----------+----------+
| 2 | Cookies | 2.0 | | David | 2, 3 |
+----------+----------+----------+ +----------+----------+
| 3 | Milk | 1.2 | | Paul | 3, 1 |
+----------+----------+----------+ +----------+----------+
products.id is the foreign key for users.products.
I would like to get the name and price of all David's products using a MySQL query. This should return the following:
+----------+----------+
| name | price |
+----------+----------+
| Cookies | 2.0 |
+----------+----------+
| Milk | 1.2 |
+----------+----------+
The JOIN function would be the best to use I guess, but David can have just 1 or 100 products. Creating 100 columns to fit all products doesn't sound very efficient.
How can I realize this?
Currently I'm just fetching everything and filter it using PHP, this is not a problem at the moment but as the tables will grow I guess this will be very inefficient.

This could be solved by changing your data model.
users
+----------+----------+
| id | username |
+----------+----------+
| 1 | Fred |
+----------+----------+
| 2 | John |
+----------+----------+
products
+----------+----------+----------+
| id | name | price |
+----------+----------+----------+
| 1 | Bread | 1.5 |
+----------+----------+----------+
| 2 | Cookies | 2.0 |
+----------+----------+----------+
| 3 | Milk | 1.2 |
+----------+----------+----------+
And here comes the magic: You could connect the two tables using a third table:
user_procuct_connections
+----------+----------+------------+
| id | user_id | product_id |
+----------+----------+------------+
| 1 | 1 | 2 | -> Fred has Cookies
+----------+----------+------------+
| 2 | 1 | 3 | -> Fred also has Milk
+----------+----------+------------+
| 3 | 2 | 1 | -> John has Bread
+----------+----------+------------+
If you want a user to be able to own a single product only, then you can remove the id column, an make the user_id and product_id primary key together.
Then when you want to get for example all of Freds products then just
SELECT
*
FROM
products
WHERE
id IN (
SELECT
product_id
FROM
user_procuct_connections
WHERE
user_id = 1
)

You could try this:
SELECT * FROM products pt
where FIND_IN_SET(pt.id,(select us.prices from users us
WHERE us.username = "David"));
Working fiddle: http://sqlfiddle.com/#!2/4f78d/2

The design for the 'Users' table is wrong. Instead of working around this bad design, please change the design.
So effectively, you design could be as such:
Table Products :
ID Name Price
1 Bread 1.5
2 Cookies 2.0
3 Milk 1.2
Table users :
Username Products
James 1
James 2
David 2
David 3
Paul 3
Paul 1
You see, you can have multiple rows for each user i.e.one row for each product in the Users table. You can maintain another Boolean field to identify which ones are currently 'active' or 'applicable'. Joins' would be much easier in that case. Also, updating records would also be easier. In case, in future you'd want to retrieve & analyse data from historic records, that would be possible too!!!
Another thing with the current design is that times and again you'd have to work around the infamous "comma-seperated values". Let's say you have a record as such:
Username Products
James 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
If you need to sort this data into rows, you'd have to work-around the 'Comma-seperated' values so many times. Imagine doing that for 'n' number of records!!
Hope this helps!!!

If you join the tables like:
SELECT name, price FROM products AS p INNER JOIN user_products AS up ON up.id = p.product_id AND up.user_id = <davids id>
You will get an array where first column will be the name and the second the price. You can then do what you want with it in PHP and change the array. You don't have to filter anymore.

Related

Mysql - Compare int field with comma separated field from another table

I have two tables in a MySQL database like this:
User:
userid |userid | Username | Plan(VARCHAR) | Status |
-----------+------------+--------------+---------------+---------+
1 | 1 | John | 1,2,3 |1 |
2 | 2 | Cynthia | 1,2 |1 |
3 | 3 | Charles | 2,3,4 |1 |
Plan: (planid is primary key)
planid(INT) | Plan_Name | Cost | status |
-------------+----------------+----------+--------------+
1 | Tamil Pack | 100 | ACTIVE |
2 | English Pack | 100 | ACTIVE |
3 | SportsPack | 100 | ACTIVE |
4 | KidsPack | 100 | ACTIVE |
OUTPUT
id |userid | Username | Plan | Planname |
---+-------+----------+------------+-------------------------------------+
1 | 1 | John | 1,2,3 |Tamil Pack,English Pack,SportsPack |
2 | 2 | Cynthia | 1,2 |Tamil Pack,English Pack |
3 | 3 | Charles | 2,3,4 |English Pack,Sportspack, Kidspack |
Since plan id in Plan table is integer and the user can hold many plans, its stored as comma separated as varchar, so when i try with IN condition its not working.
SELECT * FROM plan WHERE find_in_set(plan_id,(select user.planid from user where user.userid=1))
This get me the 3 rows from plan table but i want the desired output as above.
How to do that.? any help Please
A rewrite off your query what should work is as follows..
Query
SELECT
all columns you need
, GROUP_CONCAT(Plan.Plan_Name ORDER BY Plan.planid) AS Planname
FROM
Plan
WHERE
FIND_IN_SET(Plan.plan_id,(
SELECT
User.Plan
FROM
user
WHERE User.userid = 1
)
)
GROUP BY
all columns what are in the select (NOT the GROUP_CONCAT function)
You also can use FIND_IN_SET on the ON clause off a INNER JOIN.
One problem is that the join won't ever use indexes.
Query
SELECT
all columns you need
, GROUP_CONCAT(Plan.Plan_Name ORDER BY Plan.planid) AS Planname
FROM
User
INNER JOIN
Plan
ON
FIND_IN_SET(Plan.id, User.Plan)
WHERE
User.id = 1
GROUP BY
all columns what are in the select (NOT the GROUP_CONCAT function)
Like i said in the comments you should normalize the table structures and add the table User_Plan whats holds the relations between the table User and Plan.

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.

mysql primary key starts with pattern

I have 2 tables books, mobiles in a Mysql database.
Tables structure being,
1. book_id|book_name
2. mobile_id|mobile_name
A third table products with the following structure is also created,
product_id|product_name
Now, I want the book_id to start with a pattern in mysql.
For example,
book_ids -> 11,12,13,14,15,16,17,18,19,110,111,112,...
mobile_ids -> 21,22,23,24,25,26,27,28,29,210,211,212,...
Now, in my products table I can have,
product_id -> 11,21,16,26, if this starts with pattern is not set,
then the problem would be,
product_id -> 1,1,6,6..its not unique.
How to handle this..?
Edited stuff below:
So, are you meaning this..?
category_table
category_id | category_name
1 | books
2 | mobiles
book_table
book_id | book_name
1 | da vinci code
2 | alchemist
mobile_table
mobile_id | mobile_name
1 | nokia
2 | samsung
product_table
id | product_name | product_id | category_id
1 | da vinci code | 1 | 1
2 | alchemist | 2 | 1
3 | nokia | 1 | 2
4 | samsuing | 2 | 2
You seem to be duplicating information without a reason.
Just create a products table with:
id | name | type (e.g. 0 book, 1 mobile, etc) | price | ....