having a fairly low level in sql, I come to seek your help because I am blocked.
So I have a database containing my 2 tables, an article table and a category table. In my article table, I have a field that contains the IDs of the categories to which it belongs in the JSON format.
Schematically, the categories table looks like this:
| Name | id |
| Cat1 | 1 |
| Cat2 | 2 |
| Cat3 | 3 |
And the table articles:
| Title | id | Categories |
| Title1 | 1 | [1,2] |
| Title2 | 2 | [1,3] |
| Title3 | 3 | [2,3] |
Currently, I retrieve my articles as follows:
SELECT *
FROM articles a
JOIN categories c
ON JSON_CONTAINS(a.categories, CAST(c.id AS CHAR))
Then in php I group them by categories. But I find that this solution is not very clean.
So I wanted to know if a SQL query could retrieve a list of articles already grouped by categories in this way:
Cat1
- Article 1
- Article 2
cat2
- Article 1
- Article 3
Cat3
- Article 2
- Article 3
It is better database design to make a new table ArticleCategories that lists each articles categories:
ArticleID, CatID
Where an ArticleID can have multiple CatID...
Now you can associate multiple categories to one article and easily search this new database with SQL for correlations between categories and articles.
SELECT c.category, STRING_AGG(a.title, ',')
FROM articles a
LEFT JOIN articleCategories ac
ON a.id = ac.articleID
LEFT JOIN categories c
ON c.id = ac.categoryID
GROUP BY c.category
Related
I have tree tables:
table user_products
user_id | product_specific_id | order_no
1 | 1 | 1
1 | 2 | 1
1 | 3 | 2
table products_library
product_specific_id | product_id
1 | 3
2 | 3
3 | 1
table product_names
product_id | name
1 | prod1
2 | prod2
3 | prod3
Every product in the database does have unique product_id. But when user does order any product, he can modify it, so I created product_specific_id that I'm using in user_products, and table products_library where I can translate every unique product_specific_id to the base product_id.
Now every product_specific_id does have name of related product_id, that I do store in table product_names.
Now I need to display the name of every product_specific_id for specific user_idand order_no.
Expected result should look like this:
user_id | order_no | product_specific_id | name
1 | 1 | 1 | prod3
1 | 1 | 2 | prod3
I'm able to fit in two queries: first I'm selecting list of product_specific_id from user_products, and than I'm nesting SELECT like
SELECT name
FROM product_names
WHERE product_id IN (SELECT product_id
FROM products_library
WHERE product_specific_id IN (...)
But is it possible to fit everything it in one query? I have no idea how such thing could be achieved, if at all. Also, I'm not sure if nesting queries like this is good or not in the first place. Perhaps is it just fine to get it in two queries and nesting queries too much is bad idea?
You can try the below way using just JOIN
select user_id,order_no,u.product_specific_id,name
from user_products u join products_library p on u.product_specific_id=p.product_specific_id
join product_names pn on p.product_id=pn.product_id
where user_id=1 and order_no=1
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
Please I need some help in writing an SQL statement for a PHP application that involves four tables. Here is the situation.
Each Art in Table B has a list of categories in Table D
Each PlaceID in Table A has a list of categories in Table C
I need to select Art from Table B to fill a PlaceID in Table A, but both Art and PlaceID must have at least one category in common.
I will appreciate any help that I can get.
TABLE A
PlaceID |
1 |
2 |
TABLE B
ArtID | Art
1 | Art1
2 | Art2
3 | Art3
TABLE C
ID | PlaceID | Category
1 | 1 | Cat1
2 | 1 | Cat2
3 | 2 | Cat3
4 | 2 | Cat1
5 | 3 | Cat1
TABLE D
TabID | ArtID | Category
1 | 1 | Cat1
2 | 1 | Cat2
3 | 1 | Cat3
4 | 2 | Cat1
5 | 2 | Cat2
For a given myPlaceID you can find possible Art values as follows:
SELECT DISTINCT B.Art
FROM B
INNER JOIN D
ON D.ArtID = B.ArtID
INNER JOIN C
ON C.Category = D.Category
WHERE C.PlaceID = :myPlaceID;
You don't need table A in this query.
Here is a fiddle. Note that for the existing values for PlaceID (1, 2, 3) you will always get 2 records as result (Art1, Art2) with the example data you provided. So you might want to use some other data to get more variation in your results.
Assuming the two tables tablec and tabled are related with Category, then you can do this:
SELECT
b.Art,
a.PlaceId,
COUNT(DISTINCT c.Category) AS TotalCategories
FROM tableA AS a
INNER JOIN tablec AS c ON a.placeID = c.PlaceID
INNER JOIN tabled AS d ON d.Category = c.Category
INNER JOIN tableB AS b ON b.ArtID = d.ArtID
GROUP BY b.Art, a.PlaceId
HAVING COUNT(DISTINCT c.Category) > 0;
This will give you only the places and arts that has at least one category in common, with count of categories in common.
SQL Fiddle Demo
This will give you:
| Art | PlaceID | TotalCategories |
|------|---------|-----------------|
| Art1 | 1 | 2 |
| Art1 | 2 | 2 |
| Art2 | 1 | 2 |
| Art2 | 2 | 1 |
As you can see this returned only art 1, 2 with places 1, 2 as they are the only arts and places have more than one category in common.
Side note: In your table designs, you are missing the many to many junction table between arts and places. So, iF the category is just to relate tablec with tabled, then you can get rid of either tablec or tabled, So that you will have both placeid and artid in the same table that will act as many to many junction table.
Before I explain my problem, I will quickly go over how the table structure is:
Type: MySQL
Posts/topic Table:
int int** UNIX Time Int Int
--------------------------------------------------
| id | category | postdate | topic_id | is_topic |
--------------------------------------------------
| 1 | a | 12345678 | 1 | 1 |
--------------------------------------------------
| 2 | a | 12345678 | 1 | 0 |
--------------------------------------------------
| 3 | b | 12345678 | 3 | 1 |
--------------------------------------------------
| 4 | b | 12345678 | 3 | 0 |
--------------------------------------------------
| 5 | c | 12345678 | 5 | 1 |
--------------------------------------------------
| 6 | c | 12345678 | 5 | 0 |
--------------------------------------------------
**I'm using letters to make is easier to read the table
I am trying to retrieve the 4 newest rows for each category, and I am able to get the 1 newest from each category with GROUP BY, but I have no idea how to get multiple for each category.
I have tried something like this:
SELECT *
FROM posts p
WHERE NOT EXISTS
(
SELECT *
FROM posts
WHERE category = p.category
LIMIT 4
)
I also tried working with some of the other answers people supplied for some other answers here on SO, but I did not seem to be able to make them fit my purpose.
I am completely lost here, as mysql is not a strong side of mine when it comes to these more complex queries.
Any help or pointers in the right directions would really be appreciated!
Thanks!
UPDATE: Please note that the number of categories is not be static, and will change.
Something like this would probably do :
select * from
(select
#rank:=CASE WHEN #ranked <> category THEN 1 ELSE #rank+1 END as rank,
id,
category,
postdate,
topic_id,
is_topic
#ranked:=category
from
(select #rank := -1) a,
(select #ranked :=- -1) b,
(select * from posts order by category, postdate desc) c
) ranked_posts
where ranked_posts.rank <= 4
Basically what is happening here is that I am trying to create a "ranking" function present in other engines (MS SQL comes in mind).
The query goes through all the posts, ordered by category, postdate and adds a "ranking" number to every row resetting the rank when the category changes. Like:
rank | category
1 a
2 a
...
100 a
1 b
2 b
1 c
2 c
3 c
You do that inside a "sub query" to mimic a table then just select the rows that have rank <= 4, (the ones you need). If you need more or less you can adjust that number.
One thing to keep in mind is that the ordering is important or the ranks will get all screwed up. The categories have to be "grouped", hence the ORDER BY category and then the groups ordered by your criteria postdate desc.
I am new to the world of mysql and I'm having some trouble getting the data I need from a database.
The 2 tables I have are...
Results
ID | TITLE | LOTS OF OTHER DATA |
1 | res1 | |
2 | res2 | |
3 | res3 | |
4 | res4 | |
5 | res5 | |
Categories
ID | RESULT_ID | CATEGORY NAME |
1 | 1 | purchase |
2 | 1 | single_family |
3 | 1 | conventional |
4 | 2 | usda |
5 | 3 | somecategory |
I'm trying to create a query that will select results that belong to all of the categories provided in the query. For example a query for purchase & single_family & conventional in this example would return the first result in the results table.
Does that make sense? Is there a query that will do this or is this more of a problem with my database structure?
Thanks a lot!
Try something like this:
SELECT * FROM Results r
INNER JOIN Categories c on r.ID = c.RESULT_ID
WHERE c.name in ('purchase', 'single_family', 'conventional')
GROUP BY r.ID
HAVING COUNT(c.ID) = 3
The basic select with join will get you three rows only for result 1.
Edit: To make sure your code won't break if you change your database you should always select the fields you want explicitly: SELECT r.ID, .. FROM ..
So you're basically doing a simple join with all the category table for all categories where the category name is one of the names in the list. Try to run the 3 first lines manually to see the result you get.
Next you group by the result id. This means that you are aggregating all the rows sharing the same result id into one row. The last line means that we are filtering the aggregated columns that are aggregated by 3 rows. That means that you will only return results that have 3 matching categories.
So the only problem with this approach is if you have duplicate result_id, categoryname in your Categories table.
I designed a SQL structure to represent categories and their subcategories.
I have 3 tables:
articles
articles_categories
categories
Articles table:
id,title,content
Categories table:
id, title, parent_id
articles_categories:
id,article_id,category_id
No problem with SQL, but now - lets say i'm on article id 5
article id 5 has - 3 categories, that 2 of them has parents, and the main has '0' as parent.
How do I fetch them all efficiently? (lets say for - breadcrumbs).
thanks!
Unless the depth of the category hierarchy is fixed, you cannot do this in MySQL with your current model (adjacency list). You'd have to traverse the hierarchy using several SQL statements in a loop.
If the category hierarchy is fairly static, you can "precompute" the tree by using:
Path enumeration
Nested sets
Closure table
All of the above, trades write performance for read performance.
Google or search SO for any of the above and you will find examples of how to implement it.
Quite often, I find that storing the data in a adjacency list (because of best matches the data model) and caching a copy of the tree in the application is good enough, but that depends on your requirements of course :)
This should do the job:
select * from articles_categories
left join categories on categories.id = articles_categories.category_id
where article_id=1;
+------+------------+-------------+------+--------+-----------+
| id | article_id | category_id | id | title | parent_id |
+------+------------+-------------+------+--------+-----------+
| NULL | 1 | 1 | 1 | first | 0 |
| NULL | 1 | 2 | 2 | second | 1 |
| NULL | 1 | 3 | 3 | third | 2 |
+------+------------+-------------+------+--------+-----------+
Additionally, I would remove the "id" column from associative table articles_categories.