I have a table that looks like the following
cat_id | name | parent_id | Level
-------------------------------------------------
1 cat1 0 1
2 subcat1 1 2
3 subcat1-subcat 2 3
I am wondering what is the most efficient way to get the parent categories of cat_id 3, so my result set will look like this
cat_id | name | parent_id | Level
--------------------------------------------------
1 cat1 0 1
2 subcat1 1 2
You have to do multiple queries. One for each level up you want to go (or write it as a stored procedure).
The most efficient way is not to use what's called the "adjacency list model" (what you are using) and instead switch to "nested sets".
Googling for "nested set" will give you lots of info. It will take you some time to get used to it though, and to write the code for handling it.
I was having the same issue, but unfortunately there is no way to do this in single query. so you have to either write a function or a stored procedure which can get all parent categories.
algo will be as follows
**childs** = 3
Loop (not done)
Get immediate parents of **childs**,
save(concat) them in a *return* variable
update **childs** = immediate parents
REPEAT
return will contain all parents(flat) of given category
1) FUNCTION : in case of function you will return a string value as function can not return more than 1 values so your result will be some thing like "1,2". but again this will not full fill our purpose as we have to use this result in a query
SELECT * FROM table_name where id IN ("1,2") ORDER BY LEVEL
so instead we will return our result as a regular expression ;) and that is "^(1|2)$"
SELECT * FROM tbl_categories WHERE cat_id REGEXP "^(1|2)$" ORDER BY level;
2) STORED PROCEDURE: in case of stored procedure we can prepare a statement dynamically and upon executing that query we will have our required result.
for more detail on stored procedure pleas have a look on following tutorial.
Get all nested parents of a category – MySQL
Related
Environment: MySQL 5.6
SqlTable name = CategoryTable
Sql Columns
CATEGORY_ID (INT)
CATEGORY_NAME (VARCHAR)
LEVEL (INT)
MOTHER_CATEGORY (INT)
I've tried with
SELECT
CATEGORY_ID, CATEGORY_NAME , LEVEL , MOTHER_CATEGORY
FROM
CategoryTable
But I don't know how to use the ORDER BY in order to get that result.
So the first line here are the columns, and from the second lines, there start the table content:
CATEGORY_ID CATEGORY_NAME LEVEL MOTHER_CATEGORY
1 MainCategory 0 0
2 -SubCategory1 1 1
3 --SubCategory2 2 2
4 ---SubCategory3 3 3
5 2Nd_Main_Category 0 0
6 -SubCategory1 1 5
7 --SubCategory2 2 6
8 ---SubCategory3 3 7
is there a way to achieve something like this with a mysql query?
You aren't very clear in what you are trying to achieve. I'll take a guess that you want to order using a multi-level parent child structure. there are some very complicated ways of handling such a feat within mysql 5.6, a DB that's not really ideal for such a structure, but I have come up with something simple myself that I use in my own apps. you create a special ordering field that creates a path of zero filled ids for each record.
ordering_path_field
/
/0000000001/
/
/0000000001/0000000002
/0000000003
/0000000003/0000000005
/0000000003/0000000005/0000000006
etc
so each record contains a path of each parent up to the root, using zero filled ids. then you can just sort by this field to get them in proper order. the drawbacks being that you'll have to set a max number of levels allowed, so that the ordering fields doesn't overflow, and also, moving a record to a new parent if ever needed would be a big pain.
I have a string S = "1-2-3-4-5-6-7-8"
This is how my database table rows look like:
id
SubSequence
1
1-2-4-5
2
1-3-4-5
3
2-5-7-8
4
5-8-9-10
5
6-7-10-11
and so on ...
I want to write a query that would update (in this example) only the first 3 rows because they're a subsequence of string S.
The current solution I have is to programmatically go thru each row, check if it's a subsequence, and update. But I'm wondering if there's a way to do it at the MySQL level for performance.
Update: I don't mind changing the way data is stored. For example, String S could be an array holding those numbers, and the "SubSequence" column can hold those numbers as an array.
No, there is not a way to do the query you describe with good performance in SQL when you store the subsequences as strings like you have done. The reason is that doing substring comparisons cannot be optimized with indexes, so your query will be forced to do the comparisons row by row.
In general, when you try to store sets of values as a string, but you want to use SQL to treat them as discrete values, it's bound to be awkward, difficult to code, and ultimately have bad performance.
In this case, what I would do is make a two tables, one that numbers your entities, and a second table in which each value in your subsequence is stored on a row by itself.
SubSequences:
id
1
2
SubSequenceElements:
id
SubSequenceElement
1
1
1
2
1
4
1
5
2
1
2
3
2
4
2
5
And so on.
Then you can use relational-division techniques to find cases where every element of this set exists in the set you want to compare it to.
Here's an example:
SELECT s.id
FROM SubSequences AS s
LEFT OUTER JOIN (
SELECT id
FROM SubSequenceElements
WHERE SubSequenceElement NOT IN (1,2,3,4,5,6,7,8)
) AS invalid USING (id)
WHERE invalid.id IS NULL;
In other words, you want to return rows from SubSequences such that no match is found in SubSequenceElements with an element value that is not in the set you're trying to match.
It's a bit confusing, because you have to think about the problem is a double-don't-match-this-set problem. But once you get relational division, it can be very powerful.
If the set can be represented by the numbers 0 through 63 (or some subset of that), then...
Using a column like this
elements BIGINT UNSIGNED NOT NULL DEFAULT '0'
Then "2-5-7-8" could be put into it thus:
UPDATE ...
SET elements = (1<<2) | (1<<5) | (1<<7) | (1<<8);
Then various operations can be done in a single expression:
WHERE elements = (1<<2) | (1<<5) | (1<<7) | (1<<8) -- Test for exactly that set
WHERE (elements ^ ~ ( (1<<2) | (1<<5) | (1<<7) | (1<<8) )) != 0
-- checks to see if any other bits are turned on
This last example is close to what you need. One side of the "and not" would have the 1..8 of your example, the other would have
Your example has S represented as 0x1FE;
WHERE subsequence & ~0x1FE
will be 0 (false) for ids 1,2,3; non-zero (true) for ids 4 and 5.
how to run query select "sub" grouped by "cat" to return something like this:
SQL query:
select sub
from post
where cat = 1
group by id
to return something like:
3,4,9,14,33,22
table "post"
id cat sub
1 1 3,4,9,14
2 2 1,2
3 2 4,5
4 1 33,22
5 3 1,4
thanks,
It is a very bad idea to store lists of things in character strings. For one thing, your ids are integers, but the strings are characters. More importantly, SQL has a great data structure for storing lists -- it is called a table. You should be using a junction table.
But, sometimes you are stuck with the data you have. In that case, you can use group_concat():
select group_concat(sub)
from post
where cat = 1;
After searching a lot here, I could not find solution to my problem. So, I am posting this question.
I have a Database Table which has the structure like this:
folder_id folder_name parent_id
--------------------------------------
1 Private 0
2 Public 0
3 Photos 0
4 December 3
5 Bday 4
6 Celeb 5
In hierarchical form, it will be like a folder structure :
-- Private
-- Public
-- Photos
----- December
-------- Bday
----------- Celeb
Now, I would like to select a Path upto a particular folder, like to Bday or Celeb folder.
Thus, I want a MySQL query which will return me only the rows containing the folders between the path to a specific folder.
For Example, If I want a path to Celeb folder, then the Query should return these rows only :
folder_id folder_name parent_id
--------------------------------------
3 Photos 0
4 December 3
5 Bday 4
6 Celeb 5
Currently, I am stuck with this query and I am not able to make it work. The Query I am currently trying :
SELECT f.*
FROM fd_folders f
LEFT JOIN fd_folders p
ON f.folder_id = p.parent_id
WHERE f.folder_id <=6
ORDER BY f.folder_id;
But, the problem is that it is also returning the two other folders, i.e, Private and Public.
Please help.
Thank You
Best Regards
I'll just post this even though it does not DIRECTLY answer your question.
It looks like you may have to set up your table to have hierarchical data:
http://www.sitepoint.com/hierarchical-data-database-2/
This will take some playing with to get set up correctly, and inserting new data will not be a fun process. Your life may be much easier if you use an additional language that processes the results for you.
If you have the option of using SQL Server instead (not mysql), this whole thing actually becomes quite simple. You just need a Common Table Expression:
;WITH DirectoryTree (folderId, folderName, parentId)
AS
(
SELECT d.folderId, d.folderName, d.parentId
FROM zzz_Directories d
WHERE d.folderId = 4 -- target folderId
UNION ALL
SELECT d.folderId, d.folderName, d.parentId
FROM zzz_Directories d
INNER JOIN DirectoryTree dt ON dt.parentId = d.folderId
)
SELECT dt.folderId, dt.folderName, dt.parentId
FROM DirectoryTree dt
(Semicolon intentional)
Regrettably, mysql doesn't support this. Instead, you may have to do what was suggested in the comments: use a stored procedure. I don't have an environment up to test this, but I imagine such a procedure would have two parts:
if my parent is 0, return me
if my parent is not 0, return me UNION the procedure
the procedure would simply recursively call itself until the parent = 0. I don't know if this is even possible, but it's a place to start.
Firstly I'd like to start by apologizing for the potentially miss-leading title... I am finding it difficult to describe what I am trying to do here.
With the current project I'm working on, we have setup a 'dynamic' database structure with MySQL that looks something like this.
item_details ( Describes the item_data )
fieldID | fieldValue | fieldCaption
1 | addr1 | Address Line 1
2 | country | Country
item_data
itemID | fieldID | fieldValue
12345 | 1 | Some Random Address
12345 | 2 | United Kingdom
So as you can see, if for example I wanted to lookup the address for the item 12345 I would simply do the statement.
SELECT fieldValue FROM item_data WHERE fieldID=1 and itemID=12345;
But here is where I am stuck... the database is relatively large with around ~80k rows and I am trying to create a set of search functions within PHP.
I would like to be able to perform a query on the result set of a query as quickly as possible...
For example, Search an address name within a certain country... ie: Search for the fieldValue of the results with the same itemID's as the results from the query:
'SELECT itemID from item_data WHERE fieldID=2 and fieldValue='United Kingdom'..
Sorry If I am unclear, I have been struggling with this for the past couple of days...
Cheers
You can do this in a couple of ways. One is to use multiple joins to the item_data table with the fieldID limited to whatever it is you want to get.
SELECT *
FROM
Item i
INNER JOIN item_data country
ON i.itemID = country.itemID
and fieldid = 2
INNER JOIN item_data address
ON i.itemID = country.itemID
and fieldid = 1
WHERE
country.fieldValue= 'United Kingdom'
and address.fieldValue= 'Whatever'
As an aside this structure is often referred to as an Entry Attribute Value or EAV database
Sorry in advance if this sounds patronizing, but (as you suggested) I'm not quite clear what you are asking for.
If you are looking for one query to do the whole thing, you could simply nest them. For your example, pretend there is a table named CACHED with the results of your UK query, and write the query you want against that, but replace CACHED with your UK query.
If the idea is that you have ALREADY done this UK query and want to (re-)use its results, you could save the results to a table in the DB (which may not be practical if there are a large number of queries executed), or save the list of IDs as text and paste that into the subsequent query (...WHERE ID in (...) ... ), which might be OK if your 'cached' query gives you a manageable fraction of the original table.