I am making a news website with Codeigniter, and I have an Articles MySQL table like
ID,Title,Body,Categories,Created etc...
In Categories field I have category separated with comma(,) like...
Article 1 Categories : National,Crime,Cinema
Article 2 Categories : National,City,Drama
Article 3 Categories : Funny,International,Cinema
Article 4 Categories : National,Crime,Cinema
I want to fetch article with Specific Category.. like National (1,2,4).
I tried many methods but nothing seems to work.
Please Help Thanks.
you can use FIND_IN_SET method to query your Categories field
FIND_IN_SET('Crime', your_table.Categories)
Your approach has a number of shortcomings, It would def be more scalable in the long run to change your tables relationship to Categories. You can use a manytomany relationship and a join table to more easily query your categories.
FIND_IN_SET will do a full table scan, and using this comma seperated way will be very difficult to aggregate, and get article/category counts.
Is storing a delimited list in a database column really that bad?
Bill Karwin has included this anti pattern as the first chapter in his excellent book.
Related
I am using codeigniter and MySQL to build an ecommerce web application.
This one required three level of categories. So I have created 3 tables. These are-
category
category_id, category_name
subcategory
subcategory_id,subcategory_name,subcategory_category_id
subsubcategory
subsubcategory_id,subsubcategory_name,subsubcategory_subcategory_id
Here they are linked as parent of one another. Finally I have the product table
product
product_id, product_name, product_subsubcategory_id
Now, I need a sql query on this to fetch all product of any specific category.
Something like
$this->Mdl_data->select_products_by_category($category_id);
Please help me on this. I have tried PHP programming to solve this. But it was too slow with lot's of nested loops.
If you need to select all products, that match some specific category, try this request:
SELECT p.product_id, p.product_name, p.subsubcategory_id FROM category c
JOIN subcategory sc ON sc.subcategory_category_id = c.category_id
JOIN subsubcategory ssc ON ssc.subsubcategory_subcategory_id = sc.subcategory_id
JOIN product p ON p.subsubcategory_id = ssc.subsubcategory_id
WHERE c.category_id = 1;
But you should think about changing your database structure to make your requests faster and simpler.
Edit: Answering the comment about how to improve DB.
Current design of database looks correct, according to actual data relations. 1-many for cat-subcat and 1-many for subcat-subsubcat. But this leads to complicated (and possibly slow) queries while usage.
One way I see is to implement many-many relation with additional restriction. You can create additional table cat-subcat, just as you would do if you needed many-many. But in that table you can set unique limitation to subcat_id, so every subcat could belong only to 1 cat and it becomes in fact 1-many relation. But in this case you can move both up- and downwards the hierarchy. This approach will reduce number of JOINs in your query only by 1, but the whole logic of the query would be easier to understand.
Another way. As I understand this is the query for web-store filter. So, new products will be inserted much more seldom, than viewed by category. You can just add subcat_id and cat_id fields to your product, which is not good idea from the point of data structure, but for this particular situation this might be good solution. Every time new product is inserted to DB, you should control the correctness of those 2 fields by PHP or whatever you use on server. But when products are searched by category you will have simple request without JOINs at all.
Both approaches are based on the idea to sacrifice some space for speeding up and simplifying the queries, that are frequently used. Maybe there is even better solution, but I can't find it right now.
I was wondering if mysql has a way to look at a column and only retrieve the results when it finds a unique column once. For example
if the table looks like this:
id name category
1 test Health
2 carl Health
3 bob Oscar
4 joe Technology
As you can see their are two rows that could have the same category. Is their a way to retrieve the result where the array will one only return the category once?
What I am trying to do is get all the categories in the database so I can loop through them later in the code and use them. For example if I wanted to created a menu, I would want the menu to list all the categories in the menu.
I know I can run
SELECT categories FROM dbname
but this returns duplicate rows where I only need the cateogry to return once. Is there a way to do this on the mysql side?
I assume I can just use php's array_unique();
but I feel like this adds more overhead, is this not something MYSQL can do on the backend?
group by worked perfectly #Fred-ii- please submit this as answer so I can get that approved for you. – DEVPROCB
As requested by the OP:
You can use GROUP BY col_of_choice in order to avoid duplicates be shown in the queried results.
Reference:
https://dev.mysql.com/doc/refman/5.5/en/group-by-handling.html
By using database normalization, you would create another table with an unique id and the category name and by that link those two together, like
select * from mytable1
on mytable1.cat = mytable2.id
group by mytable1.cat
You can ofcourse also use group by without multiple tables, but for the structure, I recommend doing it.
You can use select distinct:
SELECT DISTINCT categories
FROM dbname ;
For various reasons, it is a good idea to have a separate reference table with one row per category. This helps in many ways:
Ensures that the category names are consistent ("Technology" versus "tech" for instance).
Gives a nice list of categories that are available.
Ensures that a category sticks around, even if no names currently reference it.
Allows for additional information about categories, such as the first time it appears, or a longer description.
This is recommended. However, if you still want to leave the category in place as it is, I would recommend an index on dbname(categories). The query should take advantage of the index.
SELECT id, name from dbname GROUP BY categoryname
Hope this will help.
You can even use distinct category.
Dear Stackoverflowers,
I have a mysql query which checks if some sub categories are part of a categoryID to show all products from subcategories in the main category.
For example:
Category
Subcategory 1
Subcategory 2
Subcategory 3
For example products are added to subcats but are ALL beeing shown in the head category.
This works fine and i do this with WHERE cID in (' . $subids . ')
But now comes it, the cID used to hold just one value but since it has to be possible to add products to multiple categories i now save the multiple selected ids as comma separated in the cID field.
So im searching for a way to basicly find a match from a comma seperated list within a comma separated list because cID has now become comma separated ID's and FIND_IN_SET does not work with this.
I hope someone can shine some light, thank you all!
Have a read of these:
Many-to-many data model
Junction table
Implementing this, as opposed to comma-separated values, should probably save you and your coworkers a lot of headache and simplify your queries, effectively eliminating the need of complex queries. It should be faster, too.
Add full-text search index on this column in the database and then use following sql query
Ref - http://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html
Storing a CSV in a single field is not a good idea. Searching on that requires use of LIKE clause.
where CID LIKE '$cid1,' or CID LIKE ',$cid1,' or CID LIKE ',$cid1' or
CID LIKE '$cid2,' or CID LIKE ',$cid2,' or CID LIKE ',$cid2' or
.... so on
Also FIND_IN_SET would work too.
where FIND_IN_SET( $cid1, CID ) OR FIND_IN_SET($cid2, CID) .. so on
i now save the multiple selected ids as comma separated in the cID field. That's a very bad approach ,why would you store a CSV in a single field ,you shud change your design ,make a seperate table where each value will have a new row and will be mapped to a FK.
What you really need to do,is work on your DB design,read on Normalization and other related topics,I have so added some links,
MSDN Link on Many to Many
Databae Normalization
I want to store reviews in a flexible system of categories and subcategories, and am currently in the process of designing the database structure for that. I have an idea how to do that, but I'm not entirely sure if it couldn't be done more elegant and/or efficient. These are my thoughts - if anybody can comment on if/how this can be improved I'd be really grateful.
(To keep this post concise, I only list the important field for the tables)
1.) The reviews are stored in the table "reviews". It has the following fields:
id: uniquite ID, auto-incrementing.
title: the title that will show up in <head><title>, etc.
stub: a version of the title without spaces, special chars, etc. so it can be part of the URL/URI
text: the actual content
2.) All categories are in the same table "categories"
id: unique ID, auto-incrementing.
title: the full title/name of the categorie how it will be output on the website
stub: version of the title that will be shown in the URL/URI.
parent_id: if this is a subcategory, here is the categories.id of the parent category. Else this is 0.
order_number: simple number to order the categories by (for display in the navigation menu)
3.) Now I need an indicator which reviews are in what categories. The can be in multiple. My first idea was to add a "review_list" field to the categories and have it contain all reviews.id's that should be in this category. However I think that adding and removing reviews from categories would be a hassle and "unelegant". So my current idea is to have a table "review_in_category" and have an entry for every review-category relation. The structure is:
id: Unique ID, auto-increment.
review_id: the reviews.id
category_id: the categories.id
So if a review is in 3 different categories it would result in 3 entries in the "review_in_category" table.
The idea is, that when a user opens www.mydomain.de/animation/sci-fi/ the wrapper script will break up the URL into its parts. If it finds more than one category with category.stub = "sci-fi", it will check which of those has a parent category with the stub "animation". Once the correct category is identified (most the time the stubs are unique anyway so this check can be skipped) I want to SELECT all review_id's from "review_in_category" where the category_id matches the the one determined by the wrapper script. All the review_id's are put into an array. A loop will iterate through this array and compose the SELECT statement for listing all review titles (and create links to them using the stub values) by "SELECT title, stub FROM reviews WHERE id=review_list[$counter]" and then add "OR id=review_list[$counter]" until the array is completely travelled.
SO my questions are:
- Is the method my creating a single SELECT statement with potentially a large number of "OR id=" parts an "elegent" and/or efficient way to handle this situation or are there better variants?
- Does using a "taxonomy"-style table (review_in_category) make sense or would it be better to store the "membership"/"relation" directly in the reviews or category tables?
- Any other thoughts... I just started to learn this stuff and appreciate any feedback.
Thank you
Your design looks sound.
To retrieve all reviews in a category, you should use a join:
SELECT reviews.title, reviews.stub FROM reviews, review_in_category WHERE reviews.id = review_in_category.review_id AND category_id = $category
Let's say I have 10 books, each book has assigned some categories (ex. :php, programming, cooking, cookies etc).
After storing this data in a DB I want to search the books that match some categories, and also output the matched categories for each pair of books.
What would be the best approach for a fast and easy to code search:
1) Make a column with all categories for each book, the book rows would be unique (categs separated by comma in each row ) -> denormalisation from 1NF
2) Make a column with only 1 category in each row and multiple rows per book
I think it is easier for other queries if I store the categories 1 by 1 (method 2), but harder for that specific type of search. Is this correct?
I am using PHP and MySQL.
PPS : I know multi relational design, I prefer not joining every time the tables. I'm using different connection for some tables but that's not the problem. I'm asking what's the best approach for a db design for this type of search: a user type cooking, cookies, potatoes and I want to output pairs of books that have 1,2 more or all matched categs. I'm looking for a fast query, or php matching technique for this thing... Tell me your pint of view. Hope I'm understood
Use method 2 -- multiple rows per book, storing one category per row. It's the only way to make searching for a given category easy.
This design avoids repeating groups within a column, so it's good for First Normal Form.
But it's not just an academic exercise, it's a practical design that is good for all sorts of things. See my answer to Is storing a comma separated list in a database column really that bad?
What you want to do is have one table for books, one table for categories, and one table for connecting books and categories. Something like this:
books
book_id | title | etc
categories
category_id | title | etc
book_categories
book_id | category_id
This is called a many-to-many relationship. You should probably google it to learn more.
This relationship is a Many-To-Many (a book can have multiple categories and a category can be used in several books).
Then we have the following:
Got it?
=]
I would recommend approach number 2. This is because approach 1 requires a full text search of the category column.
You may have some success by splitting it up into two tables: One table has one line per book and a unique id (call the table books), and the other has one line per book per category and references the book id from the first table (call the table bookcategories). Then if you only need book data you use table books, where if you need categories you join both tables.