Table categories:
id, name,
-------------
1 category1
2 category2
3 category3
4 category4
Table posts:
id, category, title
--------------------
1 1, 3 title1
2 4, 2 title2
3 1, 4 title3
I would like to have a show categories and counter posts in this category. In the category column in the table posts I IDs of categories, which are separated by ', '. How to do that is searched when category = category ID and show counter with a minimum of SQL queries.
You should fix your data structure to have one row per post and category. A comma-separated list is not a SQLish way to store data for many reasons. You should change this to a junction table.
Here are some reasons why:
Numbers should be stored as numbers and not strings.
Foreign key relationships should be properly defined, and you can't do that with a string to a number.
An attribute should contain one value.
MySQL doesn't have very good support for string processing functions.
Queries using the column cannot be optimized using indexes.
You should have a table called PostCategories for this information.
Sometimes, we are stuck with other peoples bad design decisions. If so, you can use a query such as:
select c.id, count(*)
from posts p join
categories c
on find_in_set(c.id, replace(p.category)) > 0
group by c.id;
Related
This question already has answers here:
Is storing a delimited list in a database column really that bad?
(10 answers)
Closed 1 year ago.
id
name
category
1
shoes
Nike, Adidas, Jordan
How can I have multiple categories for "shoes" like the example up there. It doesn't mean to be separated with commas.
The table has to be in MySQL
You can make a mapping table with categories and products like each product id can be associated with multiple category ids and then at view time use MySQL group_concat function to get comma separated category names.
ProductID
Category Id
1
1
1
2
Use query as
Select products.title,group_concat(categories.title) as product_categories from products inner join
product_categories on products.id=product_categories.product_id
inner join categories on product_categories.category_id=categories.id
group by products.id
I believe you are looking for many to many relationship.
You have to create 3 new tables,
Products(pid,...)
Categories(cid,...) and
Product_Category(pid,cid,..)
Where pid is a foreign key to Products table and category_id is a foreign key to Categories table.
I have 2 Tables:
1 - cat
Id|category
2 - groups
Id|groupname|category
I want to sort the data from the cat table by the number of rows in the category (group) table.
For example in groups table, category row have the following data:
Education - 20
Fun - 50
Nature - 30
I wanna show cat as:
Fun
Nature
Education
How can I do it?
Can't you just count the amount of rows in the one table and order your results by the resulting count?
SELECT
*,
(SELECT COUNT(*) FROM category WHERE category.cat_id=cat.id) as count
FROM cat
ORDER BY count DESC;
Note To get a better answer, you'll need to be more specific about your table structure and what you would like to achieve.
I have a MYSQL DB with three tables
Table 1 - Business
Has 200,000 plus records.
Two of the 20 fields in this table are category and subcategory
Both fields store numbers that reference the tables 2 and 3 respectively.
Table 2 and 3 - Category and Subcategory
Each of these tables stores two fields a number Id and the text name for the category.
For example Category or Subcategory table - ID:32 /Name:Pet Supplies
I need to process table 1 so that i check each records category and subcategory, grab the text name reference from both category and subcategory tables and put the output into a new field in table 1.
I hope someone can help guide me in the right direction.
UPDATE business, category
SET business.categoryName = category.name
WHERE business.categoryID = categoy.ID;
Repeat for subcategory.
Better, delete the strings from the business table and get the category like this when you need it
SELECT b.name, b.whatever, c.name, s.name
FROM business AS b, category AS c, subcategory AS s
WHERE b.categoryID = c.ID AND b.subcategoryID = s.ID;
I'm not really sure how to phrase the question, so let me just give an example of the problem:
Suppose there's a table which maps items to categories. Each item can have any number of categories, and each category can of course hold any number of items. So you have a table that looks like this:
items_categories
id item_id category_id
The problem is, I want to select all item id's which have specific category id's. For example, select all item_id's with category_id's of 1 and 2: I want to find all items that are associated with categories both 1 and 2. Obviously I can't use an AND statement, and an OR statement would return all item_id's with either category, but not necessarily both.
Here is my solution and the best thing I can think of: select all item_ids with category_id equal to 1 OR 2; iterate through the results in PHP and keep track of how many item_ids are associated with a category_id; and then unset all item_ids in the results that don't have the specified number of categories. Here's a snippet of my code:
// assume $results is an array of rows from the db
// query: SELECT * FROM items_categories WHERE category_id = 1 OR category_id = 2;
$out = array();
foreach ($results as $result)
{
if (isset($out[$result['item_id']]))
$out[$result['item_id']] ++;
else
$out[$result['item_id']] = 1;
}
foreach ($out as $key=>$value)
{
if ($value != 2)
unset($out($key));
}
return array_keys($out); // returns array of item_ids
Obviously if you have lots of different categories, you're selecting and processing way more information than you should theoretically need to. Any ideas?
Thanks!
Edit: Here's an example of a table and the information I want from it:
id item_id category_id
1 1 1
2 1 2
3 2 1
4 3 2
So say I'm interested in getting all of the items with categories 1 and 2. How do I get item #1 from my example table, given that I want only items with categories #1 and #2? If I select everything with categories 1 or 2 (as in my example above), I have to select the whole table in this case and "manually" remove item_id's 2 and 3, since they aren't associated with both category 1 and category 2. Hope this helps clarify a little.
Final edit: I figured it out, despite my apparent inability to describe what I'm trying to do, heh. Here's the query I came up with, for the record:
SELECT *
FROM
(
SELECT item_id, COUNT(*) as count
FROM items_categories
WHERE category_id IN (1, 2)
GROUP BY item_id
) table_count
WHERE count = 2;
In this case, the "(1, 2)" could be replaced with "(category_id1, category_id2, ...)", and the "2" at the end would be replaced with the number of categories I'm searching for.
So it finds out how many categories match the criteria for each item, and since I only want items where ALL the categories match, it only selects those where the number of categories equals the number of categories I'm looking for. This is of course assuming there are no duplicate categories or anything like that.
Thanks for the responses!
It seems that what is troubling you is that you are forced to do a linear search which of course takes O(n) time, but if you select elements from your database in sorted order, then can't you just use a binary search in O(lg n) time?
I hope this helps, If not, then maybe I misunderstood your question and I'd like you to clarify it a little bit.
SELECT
foo
FROM
bar
WHERE
foo IN (1,2)
is this what you are looking for?
This is something you should be getting the database to do rather than PHP.
SELECT item_id # We want a list of item ids
FROM cat_items # Gets the item ID list from the cat_items table
WHERE cat_id IN (1, 2, 7, 11) # List of categories you want to search in
GROUP BY item_id; # As the same item can appear in more than one category this line will eliminate duplicates
This query does assume that the data in cat_items is accurate, in other words that the category and item IDs point to valid entries in the categories and items tables respectively. If you're using a database with foreign key support (The InnoDB engine for MySQL, Postgres, etc) enforcing foreign keys is not difficult.
To get a list of IDs in each category in the format you want, that's easily done on the SQL side too.
SELECT *
FROM cat_items
WHERE cat_id IN (1, 2, 7, 11)
GROUP BY cat_id, item_id;
If you just want a count of how many items are in each category you can also do that in SQL
SELECT cat_id, COUNT(item_id) AS items
FROM cat_items
WHERE cat_id IN (1, 2, 7, 11)
GROUP BY cat_id;
If you need more data than just the ID then you can join against the table you need the data from.
SELECT items.*
FROM cat_items
JOIN items ON cat_items.item_id = items.id
WHERE cat_id IN (1, 2, 7, 11)
GROUP BY item_id;
SELECT item_id FROM items_categories WHERE category_id = 1 AND item_id IN (SELECT item_id FROM items_categories WHERE category_id = 2)
I have a set of data that models a hierarchy of categories. A root category contains a set of top-level categories. Each top-level category contains a set of sub-categories.
Each sub category has a set of organizations. A given organization can appear in multiple sub categories.
The leaf nodes of this hierarchy are organizations. An organization can potentially appear in multiple sub-categories.
The data is stored in three SQL tables:
organizations
organization_id organization_name
1 Org A
2 Org B
3 Org C
4 Org D
5 Org E
6 Org F
categories
category_id parent_id category_name
0 NULL Top Level Category
1 0 First Category
2 0 Second Category
3 1 Sub Category A
4 1 Sub Category B
5 1 Sub Category C
6 2 Sub Category D
organizations_categories -- Maps organizations to sub_categories
organization_id category_id
1 3
2 3
2 6
3 4
4 4
5 4
6 5
6 4
7 6
8 6
I would like to be able to select a list of all unique organizations under a given category or sub-category.
The way I'm doing it right now involves first figuring out which sub categories have been requested and then looping through each sub_category in code and performing a select to get all organizations mapped to that category. The results of each select are appended to an array. This array contains duplicates whenever an organization appears in multiple sub categories.
I would love to replace this kludge with a query that can efficiently select a list of distinct organizations given an id of one of the categories in the hierarchy.
I am devloping this solution using PHP and MySQL.
Thanks for your time and suggestions.
Assuming that your hierarchy is always exactly 3 levels deep:
SELECT DISTINCT
O.organization_id,
O.organization_name
FROM
Categories CAT
INNER JOIN Categories SUB ON
SUB.parent_id = CAT.category_id
INNER JOIN Category_Organizations CO ON
CO.category_id = SUB.category_id
INNER JOIN Organizations O ON
O.organization_id = CO.organization_id
WHERE
CAT.category_id = #category_id
You can modify that by one level to allow you to pass a sub category id. If you don't know at the time whether or not you have a category id or a sub category id then you can do the following:
SELECT DISTINCT
O.organization_id,
O.organization_name
FROM
Categories CAT
LEFT OUTER JOIN Categories SUB ON
SUB.parent_id = CAT.category_id
INNER JOIN Category_Organizations CO ON
CO.category_id IN (CAT.category_id, SUB.category_id)
INNER JOIN Organizations O ON
O.organization_id = CO.organization_id
WHERE
CAT.category_id = #category_id
If your hierarchy may have an unknown number of levels (or you think it might in the future) then check out Joe Celko's Trees and Hierarchies in SQL for Smarties for alternative ways to model a hierarchy. It's probably a good idea to do that anyway.
Not sure if your data model will allow it, but you can use a single index column and a Binary Tree to easily store this information in a single 'OrganizationTree' table. Also has the benefit you use a single query with no modifications to search at the category, subcategory, or organization levels (E.g. give me all results of X subcategory)
Hope this helps.
Adam.