Joining multiple entries to single entry - php

I have 2 tables, one lists Properties (Houses, flats etc) and the other table holds image filenames and an ID to link to the relevant Property.
Now, I need to join these together, but in a way so that I only get the Property information returned once, and then the X number of image records that are linked.
I have had some success using a LEFT JOIN, in that I can select the records, but it is returning duplicate info for the Property e.g. 1 Property linked to 3 images would return 3 results from the LEFT JOIN query and thusly on my output page, print the details 3 times.
The next part after that is how do I print the Property details (which should now be singular) and the multiple image information?
Hope you can help!

You can use a group_concat function to get a list of the file names. By default, this will only allow you to have 1k of data in the resulting group_concat field.
select prop.*, group_concat(filenames) as files
from properties prop
join images on prop.id = images.prop_id
where prop.owner_id = 'something'
group by prop.id
You can tweak the group_concat_max_len property if you need more data in that field.
Then in PHP you would look over resulting query, and you can explode each of the files column, and loop over the resulting array to display the images.

Related

Whitch is faster? PHP explode or mysql join two tables on 2 ids where condition?

Let's say:
first case scenario: one table
products(product_id, title, description, ..., images);
but in field images we have images separated by '; '
Ex:
/23/25/3/flower1.jpg; /23/25/3/flower5.jpg; /23/25/3/flower7.jpg
if I make a simple mysql query,and then use php to explode, then I will have all images in one array and then to echo all images,
$images=explode('; ',$row['images']);
second case 3tables necessary
products(product_id, title, descr, ...,);
images(image_id, imageurl);
products_images(products_images_id, product_id, image_id);
So, to get images for a specified product I need to do a mysql query with a join.
Can you prove witch case is faster to echo link images?
If you always want to fetch the whole list of images, and never want to use SQL to do anything else with that list, then storing them in a semicolon-separated list is fine.
There are legitimate uses for denormalization. But be aware that you are optimizing for one task by making other tasks harder.
Think about how you would do tasks such as the following if the images are stored in a semicolon-separated list:
Search for a specific image
Add a new image to the list
Delete an image from the list
Fetch the list in sorted order
Count how many images in the list for a given product
Support a longer list of images than fits in one string column
Search for products that share a given image
Many other things you might think of doing
Then you should use the normalized table design and store one image per row.

Use data from comma separated row to perform a join

I have been given a project where there are multiple categories stored in a row in a comma delimited list (e.g. 1,2,3). Then I have a categories table that has categories that looks like this:
ID ShowcaseCategory DisplayOrder
1 Member's Work 0
2 Eastern Resorts 1
3 WesternResorts 2 
4 Products 4
5 Nordic Skiing 3
I want to be able to pull the ShowcaseCategory (from above) from the table based on more than one value in the comma delimited list. For example, if the category contained 1,5 I would get Member’s Work and Nordic Skiing.
I think I need to get it in an array, possibly explode it and be able to do some join on the ID to get the (literal) ShowcaseCategory. Or, is there an easier way to do this?
In the end I want to dynamically build a list of checkboxes of Categories so they can be updated.
All help is appreciated
One option uses find_in_set(). Assuming that parameter :categories_list holds the csv list of categories ids, you would go:
select ShowcaseCategory
from mytable
where find_in_set(id, :categories_list)

Is redundant data ok if it saves a having to make a query?

Im trying to create a checklist system eg. a list of items to collect. the user will be able to add the list to their profile and then as they collect the items then can check the box for an item in the list click submit and the checked item will now be marked as collected. I have it coded and working fine but it makes an insane amount of queries to the database to work.
i have 5 tables. a users table (for the registered users username, id..etc), a lists table (containing the list name, description, id ), a list items table (containing the individual items. id, title and listID (to reference the list it belongs to)). userslist table (for the lists the user has added to their profile. userID and listID) and collecteditems table (this has the list items that a person has checked the box for as collected. listItemsID, UserID)
the problem is when i view the mylists.php page it will query the userslist table and return all the ID's for the lists the user has added. then once i have the ID for the list it then queries the list table to find out what the name of the list is (this could mean having to make 10 queries to the list table if i have 10 different lists added). if i added a listname column to the userslist table i would only need to make 1 query for the page and that is to the userslists table and i could construct the page with that 1 query.
First, I wouldn't worry about queries on primary keys. All your tables have a primary key referenced by other tables. These will use joins.
Second, you don't have to get the list names separately. Use a query such as:
select l.listName
from UserLists ul join
Lists l
on ul.listId = l.listId
where userId = $userid
This will return all the names in a single query.
It sounds like you should keep your data normalized (that is, avoid the redundant data) and instead gather all the data you want in a single query, by using a JOIN.

Temporary Table and Left Joins not showing results as expected

I'm really hoping someone can help me with this. I have a number of product attribute types that users can select from to refine the products that are returned to them on screen. What I'm trying to do is, for each product attribute type, I want to list all attributes that relate to either the selected category or search term, then once they've made their selections, I still want to display each of the attributes that relate to the category or search term, but only display a clickable link if the product count for that particular attribute is greater than 1 and for those that have a product count of zero, I want to list them, but make them unclickable. An example of what I'm trying to achieve can be found on the ASOS website, in the left hand menu
http://www.asos.com/Women/Dresses/Cat/pgecategory.aspx?cid=8799#state=Rf961%3D3340%2C3341%40Rf-200%3D20&parentID=Rf-300&pge=0&pgeSize=20&sort=-1
Initially I tried using just joins to achieve this, but I wasn't able to do it, successfully. So I decided to create a temporary table for each attribute type which held a list of all the attributes that related to the main query and then created a refined query, with a left join. Here's my code:
CREATE TEMPORARY TABLE temp_table
SELECT su_types.id, type AS item FROM su_types
INNER JOIN su_typerefs ON su_types.id=su_typerefs.id
INNER JOIN su_pref ON su_typerefs.mykey = su_pref.mykey
WHERE wp_category_id =40 GROUP BY su_typerefs.id
$sudb->query($query);
if ($sudb->affected_rows > 0) {
SELECT temp_table.id,item,COUNT(su_typerefs.mykey) AS product_count FROM temp_table
LEFT JOIN su_typerefs ON temp_table.id=su_typerefs.id
LEFT JOIN su_pref ON su_typerefs.mykey = su_pref.mykey
LEFT JOIN su_stylerefs ON su_pref.mykey = su_stylerefs.mykey
LEFT JOIN su_productrefs ON su_pref.mykey = su_productrefs.mykey
WHERE wp_category_id =40 AND su_stylerefs.id in (91) AND su_productrefs.id in (54) AND su_typerefs.id in (159) GROUP BY su_typerefs.id
if ($itemresults = $sudb->query($query)) {
while($itemresult = $itemresults->fetch_array(MYSQLI_ASSOC)) {
$id=$itemresult['id'];
$item=$itemresult['item'];
$product_count=$itemresult['product_count'];
build_link($list_type, $item, $product_count, $id);
}
}
In the above example the first query selects all the product types that relate to a particular category, say dresses. And the second query is based on the refinements the user has made on the category, in this example this is product, product type and style. A user can also refine their search by colour, fit, fabric and design.
There are a couple of issues with this:
1) The number of results returned in the second query do not match the results of the first. Using the above as an example, I wish to list all products that relate to the chosen category, then using the second query return the product count for each of these products as I described above. So if the temporary table returns, trousers, jeans and skirts. I expected these three items to be displayed on screen based on the conditions applied in the second query, however my results may only show trousers and jeans, if there is not a match for skirts in the second query. I thought that using a left join would mean that all the results of the temporary table would be displayed.
2)Also I wonder if I'm doing this the most efficient way. I have a total of 8 attribute groups, and therefore need to do the above 8 times. If the user choses to refine the results using all 8 attribute groups then in addition to the temp table join, there will be a total of 9 joins for each type. It's taking a while to execute, is there a better way to do this? There are approximately 1/2 million products in the table, and this will probably be 5 times this, once my site goes live.
I really hope all that I have written makes sense and I'd really appreciate the stackoverflow community's help with this, if anyone can help. I apologise for the essay ;). Thanks in advance
To answer your first question; yes, a LEFT JOIN will indeed keep all data from the initial table. That, however, isn't the problem.
The reason why you lose empty categories, is most likely (I say this because I don't fully know your db structure) because of the where condition filtering out all results based on the data in the joined tables.
If for a category all items get filtered out (possibly including the NULL joined values), you will not get this category back from that query anymore. Also the GROUP BY is done on a joined column, that might also effectively wipe out your other categories.
As for the second question, you already state it's taking long; so it's probably not the way to go if you want things to work fast ;) (okay, obvious answer, low hanging fruit, etc). What you might want to do, is get a collection of keys from the filterable categories first, and use that data to select items.
This prevents that you have to join up your entire products table in a temp table (at least, that's what I think you're doing), which of course will take long with the given number of entries. Selecting a list of matching IDs from the given attributes also gives you the advance of using your indexes (more), which a temp-table probably won't have. If this is possible and feasible mainly depends on your schema's structure; but I hope it might lead you to the direction you want to go :)

Joining rows as array from another table for each row

I am coding a movie archive script using codeigniter for myself with PHP. I am getting movie's information from IMDb and adding them to my database. I'm adding links for movies I selected using another table called links.
This is the query that I am using to get the movies from database:
$movies = $this->db->query("SELECT *
FROM movies
ORDER BY ".$order['by']." ".$order['type']."
LIMIT ".$limit.", " . $this->config->item('moviePerPage')
);
and I am fetching it in view file like this:
foreach ($movies->result() as $row) {
echo $row->name;
}
Now links must be shown for each movie with the matched IDs (movies could have more than one link). My links table has these columns: 'id', 'movieid', 'name', 'link'
How can I get links for each movie with single MySQL query? Is is posible to get all links that related to current $row movie and bind them to a single variable as array? with this I can loop it with foreach() and show links for each movie.
btw: movies.movieid and links.movieid columns have the same data.
I think you need mysql's GROUP_CONCAT
Do something like this:-
SELECT
movies.*,
group_concat(links.link ', ') as links
FROM movies
LEFT JOIN links
ON links.movieid = movies.movieid
GROUP BY movies.movieid
You will get a comma separated list of links for every movie.
Which you can extract like this:-
foreach ($movies->result() as $row) {
$linksArray = explode(",",$row->links);
}
Updates
I think this is the only way you can get the results without having multiple result rows for a single movie with multiple links.
Just be careful of the maximum length of characters you can get in the result - by default 1024 characters. Read this
Mysql group_concat_max_length and Group concat max length to know how to override the limit.
And like Dan Grossman has poined out, if you feel the links may contain comma, use a different or uncommon delimiter.
JOIN the two tables, just as your question name implies.
SELECT
movies.*,
links.*
FROM movies
LEFT OUTER JOIN links
ON links.movieid = movies.movieid
...
You will get one row per movie-link pair. If there are no links for a movie, you'll get a row with just the movie information and NULLs in the columns corresponding to the links table.
You can loop over these results and put the links for each movie in an array keyed by the movie, if you'd like.

Categories