MySQL - Reference data from one table to another table - mysql

I am trying to connect tables by listing an id from another table into a specific field in a row in another table.
This is what I'm trying to do;
Table Items
Id Name Price
1 Drink 5
2 Food 3
Table Character
Id Name Cash Inventory
1 Foo 10 1
2 Bar 10 2
3 Stu 10 1, 2
I am trying to reference the Items so that when I pull data concerning the Character 'Stu'
It pulls the data associated with Items '1 & 2' because he has them in his Inventory.
I've read up on Normalization and Foreign Keys but haven't been able to find a way to tie them together in the fashion I want. I've also found the INT field will not accept multiple comma separated integers.

This should be easy using FIND_IN_SET looking at your table strructure here is the solution
SELECT
*
FROM `characters` as c
LEFT JOIN items as i on FIND_IN_SET(i.id, c.Inventory)
WHERE c.name= 'Stu'
EDIT :
Here is the edited solution means one line solution.
SELECT
c.* ,
group_concat(i.name) as ItemName,
group_concat(i.price) as ItemPrices
FROM `characters` as c
LEFT JOIN items as i on FIND_IN_SET(i.id, c.Inventory)
WHERE c.name= 'Stu'
group by c.name
If you are using php you can explode cell with php explode function and then loop through the cell to access all the cell values.
EDIT :
SELECT
c.* ,
group_concat(i.id) as ItemIds,
group_concat(i.name) as ItemName,
group_concat(i.price) as ItemPrices
FROM `characters` as c
LEFT JOIN items as i on FIND_IN_SET(i.id, c.Inventory)
WHERE c.name= 'Stu'
group by c.name

You are trying to build one to many relationship. You need to place the foreign key into table items, i.e., CharacterId . Then you can pull data with JOIN.

Related

Select rows where item is only in certain categories

So I have a somewhat complicated mysql query question. I have 3 tables. One is a table of items. One is a table of categories. And one is a linking table that just has 2 fields, itemID and categoryID. It is a many to many relationship, so one item can be in multiple categories and each category can have multiple items. Now two of the fields in the category table are isactive and ismain. They are just bools of 1 or 0. I want to grab all items that only belong to categories where at either isactive=0 or ismain=0 or both.
I took some time and set up a sql fiddle for someone to play around with. http://sqlfiddle.com/#!9/b03842/2
Solution using subquery:
SELECT DISTINCT i.* FROM cart_item i
JOIN cart_item_category ic ON i.itemref = ic.itemref
WHERE ic.catid IN (
SELECT id FROM cart_category WHERE active = 0 OR ismain = 0
)

Sql: choose all baskets containing a set of particular items

Eddy has baskets with items. Each item can belong to arbitrary number of baskets or can belong to none of them.
Sql schema to represent it is as following:
tbl_basket
- basketId
tbl_item
- itemId
tbl_basket_item
- pkId
- basketId
- itemId
Question: how to select all baskets containing a particular set of items?
UPDATE. Baskets with all the items are needed. Otherwise it would have been easy task to solve.
UPDATE B. Have implemented following solution, including SQL generation in PHP:
SELECT basketId
FROM tbl_basket
JOIN (SELECT basketId FROM tbl_basket_item WHERE itemId = 1 ) AS t0 USING(basketId)
JOIN (SELECT basketId FROM tbl_basket_item WHERE itemId = 15 ) AS t1 USING(basketId)
JOIN (SELECT basketId FROM tbl_basket_item WHERE itemId = 488) AS t2 USING(basketId)
where number of JOINs equals to number of items.
That works good unless some of the items are included in almost every basket. Then performance drops dramatically.
UPDATE B+. To resolve performance issues heuristic is applied. First you select frequency of each item. If it exceeds some threshold, you don't include it in JOINs and either:
apply post-filtering in PHP
or just don't apply filter by particular itemId, giving a user approximate results in a resonable amount of time
UPDATE B++. Seems that current problem have no nice solution in MySQL. This point raises one question and one solution:
(question) Does PostgreSQL have some advanced indexing techniques which allows to solve this problem without doing a full scan?
(solution) Seems that it could be solved nicely in Redis using sets and SINTER command to get an intersection.
I think the best way is to create a temporary table with the set of needed items (procedure that takes the item ids as parameters or something along those lines) and then left join it with all of the above tables joined together.
If for a given basketid you have NO nulls on the right side of the left join, the basket contains all the needed items.
-- the table definitions
CREATE TABLE basket ( basketid INTEGER NOT NULL PRIMARY KEY);
CREATE TABLE item ( itemid INTEGER NOT NULL PRIMARY KEY);
CREATE TABLE basket_item
( basketid INTEGER NOT NULL REFERENCES basket (basketid)
, itemid INTEGER NOT NULL REFERENCES item (itemid)
, PRIMARY KEY (basketid, itemid)
);
-- the query
SELECT * FROM basket b
WHERE NOT EXISTS (
SELECT * FROM item i
WHERE i.itemid IN (1,15,488)
AND NOT EXISTS (
SELECT * FROM basket_item bi
WHERE bi.basketid = b.basketid
AND bi.itemid = i.itemid
)
);
If you are going to provide the list of items, then edit id1, id2, etc. in below query:
select distinct t.basketId
from tbl_basket_item as t
where t.itemID in (id1, id2)
will give all baskets containing a set of items. No need to join any other tables as your requirements don't need them.
The simplest solution is to use HAVING clause.
SELECT basketId
FROM tbl_basket
WHERE itemId IN (1,15,488)
HAVING Count(DISTINCT itemId) = 3 --DISTINCT in case we have duplicate items in a basket
GROUP BY basketId

Self inner join to get single record

I have an SQL table data as follow
I want to display single record for product
example
90792 Amlaan-Hi-Power .............. Show only 1 record when there are 2 record
90793 Amlaan-Neutral .............. show only 1 record when there are 2 record
90794 Amlaan-Phosphate free .........show only 1 record when there are 2 record
90801 Acetone .......................show only 1 record when there are 2 record
90901 Acetanilide ...................show only 1 record when there is 1 record
Can I do this using Inner join
I know
select distinct product from product ORDER BY `product`.`product` DESC
will select distinct (unique) product code and that to only one field i.e. product but confused how to get other information using SQL statement
but results in duplicate records or same table...........................
It looks like your duplicate rows vary by the quantity of product in the package.
You can display just the product and name with
SELECT DISTINCT product, name
FROM product
If you want to deal with the quantity as well, that's a little trickier. This might work: it will put all product codes on one line.
SELECT product,
GROUP_CONCAT(product_code ORDER BY product_code) product_codes,
name
FROM product
GROUP BY product, name
Self join doesn't make a whole lot of sense for this application.
Use group by option for such purposes.
SELECT product,GROUP_CONCAT(product_code SEPERATOR '|') AS product_code,name FROM Table GROUP BY NAME
It will show only one record for duplicate names.
The multiple enteries of product code will seperated by | .

MYSQL query with comma seperated field value into array

is there any way, i can get a field values (comma seperate) into an array or temporary table.
ex: i have field following with values 3,7,23,45
i want get them into an array without using PHP, or into a temporary table.
as i need to do some joint queries based on those values.
any help is appreciated
thanks
my table name is: shoes
field name is following
sample table values are like these
+----------+-----------+
userId following
+----------+-----------+
1 5,7,8,12
2 5,2,1,67
now, when i search for userId 1, i want to get values 5,7,8,12 into an array or temp table.
It is possible although not particularly quick. The best solution is to redesign the database to move the comma separated list into a different table, with one row for each comma separated element.
However if you want an SQL way to do it then something like the following will do it (this relies on having a table called integers with a single column called i, with 10 rows with the values 0 to 9).
SELECT DISTINCT shoes.userId , substring_index(substring_index(shoes.FollowingIdString, ',', anInt), ',', -1) AS SplitField
FROM shoes,
(SELECT a.i+b.i*10+c.i*100 AS anInt
FROM integers a, integers b, integers c) Sub1
HAVING SplitField IS NOT NULL
Assuming you have a table PersonList which has multiple columns and this is the table where you want to join to get the name of the followers list. eg
PersonTable
ID Name
1 AAA
2 BBB
there is a mysql function called FIND_IN_SET, eg
SELECT a.userID, GROUP_CONCAT(b.Name)
FROM Follower a
INNER JOIN Person b
ON FIND_IN_SET(b.id, a.following) > 0
GROUP BY a.userID
SQLFiddle Demo

How to convert list field into many-to-many table in MySQL

I have inherited a database in which a person table has a field called authorised_areas. The front end allows the user to choose multiple entries from a pick list (populated with values from the description field of the area table) and then sets the value of authorised_areas to a comma-delimited list. I am migrating this to a MySQL database and while I'm at it, I would like to improve the database integrity by removing the authorised_areas field from the person table and create a many-to-many table person_area which would just hold pairs of person-area keys. There are several hundred person records, so I would like to find a way to do this efficiently using a few MySQL statements, rather than individual insert or update statements.
Just to clarify, the current structure is something like:
person
id name authorised_areas
1 Joe room12, room153, 2nd floor office
2 Anna room12, room17
area
id description
1 room12
2 room17
3 room153
4 2nd floor office
...but what I would like is:
person
id name
1 Joe
2 Anna
area
id description
1 room12
2 room17
3 room153
4 2nd floor office
person_area
person_id area_id
1 1
1 3
1 4
2 1
2 2
There is no reference to the area id in the person table (and some text values in the lists are not exactly the same as the description in the area table), so this would need to be done by text or pattern matching. Would I be better off just writing some php code to split the strings, find the matches and insert the appropriate values into the many-to-many table?
I'd be surprised if I were the first person to have to do this, but google search didn't turn up anything useful (perhaps I didn't use the appropriate search terms?) If anyone could offer some suggestions of a way to do this efficiently, I would very much appreciate it.
While it is possible to do this I would suggest that as a one off job it would probably be quicker to knock up a php (or your favorite scripting language) script to do it with multiple inserts.
If you must do it in a single statement then have a table of integers (0 to 9, cross join against itself to get as big a range as you need) and join this against your original table, using string functions to get the Xth comma and from that each of the values for each row.
Possible, and I have done it but mainly to show that having a delimited field is not a good idea. It would likely be FAR quicker to knock up a script with multiple inserts.
You could base an insert on something like this SELECT (although this also comes up with a blank line for each person as well as the relevant ones, and will only cope with up to 1000 authorised areas per person)
SELECT z.id, z.name, x.an_authorised_area
FROM person z
LEFT OUTER JOIN (
SELECT DISTINCT a.id, SUBSTRING_INDEX( SUBSTRING_INDEX( authorised_areas, ",", b.ournumbers ) , ",", -1 ) AS an_authorised_area
FROM person a, (
SELECT hundreds.i *100 + tens.i *10 + units.i AS ournumbers
FROM integers AS hundreds
CROSS JOIN integers AS tens
CROSS JOIN integers AS units
)b
)x ON z.id = x.id