This question already has answers here:
How to create a MySQL hierarchical recursive query?
(16 answers)
Closed 4 years ago.
I have a MYSQL table called collections when viewed and implemented as a table could be something like this:
I needed to know whether one mysql query will be able to get all the products under a collection type entry (a given) which could have collections under it. For example, if I select 10, it should return 14, 12, 13, and 15.
I implemented a solution that involves a do..while loop...
$concatted = 10;
$products = [];
do {
$sql = "SELECT id, type FROM collections WHERE parent IN ($id_concatted)";
$result = $mysqli->query($sql);
if($result) {
while($row = $result->fetch_object()){
if($row->type == 'product') {
apply_changes_to_product($row->id);
} elseif ($row->type=='collection'){
$collections[] = $row->id;
}
}
}
if(count($collections) > 0){
$id_concatted = implode($collections, ",");
$continue = true;
$collections = [];
} else {
$continue = false;
}
} while ($continue);
I think that the above code is not efficient. I think it is doable with one query but I don't know how.
UPDATE: I mark this as a duplicate of How to create a MySQL hierarchical recursive query although in that post there is NO accepted solution. I got myself this solution based on one reply there (Mysql 5.6):
SELECT id, `type` FROM (
select id, `type`
from (select * from collections
order by parent, id) products_sorted,
(select #pv := '10') initialisation
where find_in_set(parent, #pv)
and length(#pv := concat(#pv, ',', id))
) products
WHERE
products.`type` = 'product'
The fiddle is http://sqlfiddle.com/#!9/ea214f/2.
yes, you may need to use subquery and first fetch id where parent = selectedId and type = 'collection' and then select id where parent in the subquery id and type = 'product'
Like below:
SELECT id, type FROM collections WHERE parent IN (select id from collections where
parent = $id_concatted and type = 'collection') and type = 'product'
For Multiple level, Use Recursive feature of MySql. Like below:
WITH RECURSIVE COLLECTIONS_PRODUCTS (ID, TYPE, PATH)
AS
(
SELECT ID, TYPE, CAST(ID AS CHAR(200))
FROM COLLECTIONS
WHERE PARENT IN ($id_concatted)
UNION ALL
SELECT S.ID, S.TYPE, CONCAT(M.PATH, ",", S.ID)
FROM COLLECTIONS_PRODUCTS M JOIN COLLECTIONS S ON M.ID=S.PARENT
)
SELECT * FROM COLLECTIONS_PRODUCTS WHERE TYPE = 'product' ORDER BY PATH;
Related
I am trying to rank the rows based on when was it lasted updated partitioned by category name.
$query = "SELECT
category_name,
topic_title,
updated_ts,
#topic_rank := IF(#current_category = category_name, #topic_rank + 1, 1) AS topic_rank,
#current_category := category_name AS current_category
FROM topic_master
ORDER BY category_name, updated_ts DESC
";
$data = $this->db->query($query);
if($this->db->affected_rows() > 0)
{
return $data;
}
else
{
return false;
}
This query runs perfectly fine in MySQL and gives me the topic_rank as 1,2,3 and so on.
When I run this in CodeIgniter, I get topic_rank as 1 for all records.
What could be the issue ?
Try to use codeigniger database methods result_array():
$q = $this->db->query($query);
$data = $q->result_array();
Found an alternate way to solve this problem. No idea why the user defined variable was not working.
select category_name, topic_title as last_topic, updated_ts as last_activity, topic_pri_key as last_topic_id from
(
select
a.category_name, a.topic_title,
a.updated_ts,
a.topic_pri_key,
count(b.updated_ts)+1 as rank
from
topic_master a
left join
topic_master b
on a.updated_ts < b.updated_ts and a.category_name = b.category_name
group by 1,2,3,4
) a
where rank = 1
Is there a way to resort a mySQL sql after the results are already generated.
I have a sql that gets the results I want to display basically but the way I want to sort them depends on the result themselves. Have provided some pseudo code for clarity.
$sql = "SELECT * FROM post_info WHERE poster = 'login_user' OR replier = 'login_user'";
if ('login_user' == $row['poster']) { //sort by one column }
else { //sort by a different column }
You can sort conditionally from within the query with a CASE statement.
ORDER BY (CASE
WHEN poster = 'login_user' THEN col1
ELSE col2
END)
$sql = "SELECT *, if(poster='login_user', 1, 0) as idx FROM post_info WHERE poster = 'login_user' OR replier = 'login_user' order by idx desc";
or
$sql = "SELECT * FROM post_info WHERE poster = 'login_user' UNION SELECT * FROM post_info WHERE replier = 'login_user'";
I need to add WHERE, SELECT, ORDER BY etc...like a chain SQL by means of PHP. I use https://github.com/greenlion/PHP-SQL-Parser to spilt up the SQL in Array and then to form the SQL
i need a intermediate code. i can to add SELECT,WHERE, ORDER BY, ETC.. and i can test if exist in the table, fields, in array of PHPSQLParser.
Example:
$sSql = 'Select a, b, c from table1 inner join table2 on (table1.a = table2.a) where b = 900';
# Return array SQL
$aParser = new PHPSQLParser( $sSql );
$aParser = $aParser->parsed;
// I need to intermediate code add fields, conditions, etc. //
// Create the SQL through the array
$creator = new PHPSQLCreator( $this->aParser );
// Show SQL
echo $creator->created;
The package you are using is missing methods you need to work with the parsed query. If you write your own "query builder" package, you can use this package as a starting point so you don't have to write the code that parses the sql. Or better yet, you can use a different package altogether that has both query parsing and building included.
Here's one: https://github.com/jasny/dbquery-mysql
It can be used in the way you are asking:
<?php
use Jasny\DB\MySQL\Query;
$query = new Query("SELECT * FROM foo LEFT JOIN bar ON foo.bar_id = bar.id WHERE active = 1 LIMIT 25");
if (isset($_GET['page'])) $query->page(3);
$filter = isset($_POST['filter']) ? $_POST['filter'] : array(); // array('type' => 'bike', 'price between ? and ?' => array(10, 20))
foreach ($filter as $field => $value) {
$query->where($field, $value);
}
$result = $mysqli->query($query); // SELECT * FROM foo LEFT JOIN bar ON foo.bar_id = bar.id WHERE (active = 1) AND (`type` = "bike") AND (`price` between 10 and 20) LIMIT 25 OFFSET 50
i have a table with 3 columns id,name and parent_id representing categories. The root categories(categories with no parents) have parent_id 0. All other categories have parent_id as the id of their immediate parent. There is no limit on the depth of categories i mean that a category can be 3,4 or even 10 levels down from a root category. What i need now is a PHP multi-dimensional array that contains all the root categories at the first level and then their immediate sub-categories at the next level,each sub-category under its parent category,and their sub-categories under them 1 level down. So its a tree like structure. there can be many levels in the tree
I dont need the exact code but need an idea. one such idea is get all the root categories with a select query and then fire a select query for each root query to get its subcategoies and so on recursively but that would be too much select queries.
Or if i know that my table is going to contain say 300 rows at the maximum, how can i do something like
$categories=GetResultAsArray(select * from categories);
and now manipulate the $categories array in memory to get the desired tree.
You're right, using the solution with a "parentid" column is simple but it makes you write recursive queries. There are several other designs for storing hierarchical data in a database that let you do queries more efficiently.
See:
What is the most efficient/elegant way to parse a flat table into a tree?
My other answers to SO questions on hierarchical-data
My presentation Models for Hierarchical Data with SQL and PHP
My book SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming
Sql Antipatterns Strike Back
It's possible duplicate (http://stackoverflow.com/questions/8431463/more-efficient-hierarchy-system/8431551) but here is a snippet to query an adjacent tree (parent_id,id,title):
$q = mysql_query("SELECT id, parent_id, name FROM categories");
while ($r = mysql_fetch_row($q)) {
$names[$r[0]] = $r[2];
$children[$r[0]][] = $r[1];
}
function render_select($root=0, $level=-1) {
global $names, $children;
if ($root != 0)
echo '<option>' . strrep(' ', $level) . $names[$root] . '</option>';
foreach ($children[$root] as $child)
render_select($child, $level+1);
}
echo '<select>';
render_select();
echo '</select>';
You can be inspired by this solution that retrieves the breadcrumb trail of a url and its parents (infinite depth of hierarchy levels).
$q = "SELECT T2.*
FROM (
SELECT
#r AS parent_id,
(SELECT #r := `parent`
FROM `route` WHERE id = parent_id)
AS `parent`, #l := #l + 1 AS `depth`
FROM
(SELECT #r := $route_id, #l := 0) vars, `route` T3
WHERE #r <> 0) T1
JOIN `route` T2
ON T1.parent_id = T2.id
ORDER BY T1.`depth` DESC";
if($res = $db->query($q)) {
if($res = $res->fetchAll(PDO::FETCH_ASSOC)) {
if($size = sizeof($res) > 0) {
// push results in breadcrumb items array
$breadcrumb['items'] = $res;
$breadcrumb['levels'] = $size;
// retrieve only titles
$titles = array_column($res, 'title');
// construct html result seperated by '>' or '/'
$breadcrumb['html']['gt'] = implode(' > ', $titles);
$breadcrumb['html']['sl'] = implode(' / ', $titles);
}
}
}
Entire get_breadcrumb() function available here : https://stackoverflow.com/a/63578607/2282880
I researched this a bit already and all my attempts have come up short so far. I am trying to perform a mysql query in my php script that deals with multiple tables.
Here is what the tables look like:
TABLE 1
name
TABLE 2
Product (name)
Inventory
CatID
ProductID
TABLE 3
product_url
"name" (Table 1) must be the sane as "Product" (Table 2). Next, "Inventory" (table 2) must be = to "Y". Lastly, "CatID" must be = "2".
My attempt looked somewhat like this:
SELECT 1.name, 2.Product, 2.Inventory, 2.CatID
FROM table1 1, table2 2
WHERE 2.Inventory = 'Y'
AND 1.name = 2.Product
AND 2.CatID = '2'
From the results, I would be looking to get more information from the table such as product description, etc which would be in table1 and table2... I have never joined or queried 2 (or more) tables before. Any help would be greatly appreciated.
Try this:
SELECT table1.Name, table2.Product, tabl2.Inventory, table2.CatID
FROM table1 INNER JOIN table2
ON table1.Name = table2.Product
WHERE table2.CatID = '2'
SELECT t1.name, t2.Product, t2.Inventory, t2.CatID, t2.ProductID
FROM table1 t1
INNER JOIN table2 t2 ON t2.Product = t1.name
WHERE t2.Inventory = 'Y' AND t2.CatID = 2
I'm sorry to say that the database you have to work with was very poorly designed. If the query I gave you doesn't work, then make sure you have data in the tables that actually meets the criteria you're looking for.
Also remember that when you're accessing these fields in PHP, capitalization matters. You need to do something like this:
<?php
$q = QUERY FROM ABOVE
$r = mysql_query($q);
while($row = mysql_fetch_assoc($r)) {
$name = $row["name"];
$product = $row["Product"];
$inventory = $row["Inventory"];
$catid = $row["CatID"];
$productid = $row["ProductID"];
}
?>