/    Sign up×
Community /Pin to ProfileBookmark

Categories & related categories

Hi all,
Cheers for reading my post. I’ve got a list of categories in my main database table, and any of these can be related categories of another (1 to many).

So, i’ve got 2 tables:

Categories
———-

categoryID
categoryName

subCategory
————

subCategoryID
categoryID
relatedCategoryID

and i would need to output as:

category 1
– category 2
– category 3
category 2
– category 6
– category 8

I’ve tried a few SQL queries, but i can’t seem to get the right output.

Can anyone please provide help or guidance?

Thanks. Martin.

to post a comment
PHP

14 Comments(s)

Copy linkTweet thisAlerts:
@NogDogJun 01.2010 — I might try something like:
[code=php]
$sql = "
SELECT
c1.categoryName,
IFNULL(c2.categoryName, '')
FROM
Categories AS c1,
LEFT JOIN subCategory AS sc ON sc.categoryID = c1.categoryID,
LEFT JOIN Categories AS c2 ON c2.categoryID = sc.subCategoryID
GROUP BY c1.categoryID
ORDER BY c1.categoryName, c2.categoryName
";
$result = mysql_query($sql) or die(mysql_error());
$category = "";
while($row = mysql_fetch_row($result))
{
if($row[0] != $category)
{
$category = $row[0];
echo $row[0] . "<br />n";
}
if(!empty($row[1]))
{
echo "- " . $row[1] . "<br />n";
}
}
[/code]

The above is untested and the HTML it outputs could be made much more semantically meaningful (perhaps nested UL lists?), but hopefully it gives you the idea.
Copy linkTweet thisAlerts:
@martinsmithauthorJun 01.2010 — Hi NogDog,

Cheers for the reply, really appreciated.

I tried the SQL query, and received the following error:

#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'LEFT JOIN subCategory AS sc ON sc.categoryID = c1.categoryID, LEFT JOIN Cate' at line 6

Just so i've given you the full picture...

I've got 2 tables:

Categories
----------


categoryID

categoryName

subCategory
------------


subCategoryID // Automatically generated (Not really part of the query)

categoryID <-- CategoryID from main table

relatedCategoryID <-- CategoryID from main table that is to be related to field above

and i would need to output as:

category 1 (categoryID from main table)

- category 2 (relatedCategoryID)

- category 3 (relatedCategoryID)

category 2 (categoryID from main table)

- category 6 (relatedCategoryID)

- category 8 (relatedCategoryID)

Thanks again for the help, it would be great to crack this ?

Martin.
Copy linkTweet thisAlerts:
@NogDogJun 01.2010 — Oops. Try removing the commas at the end of these lines (in the FROM clause):
<i>
</i> Categories AS c1,
LEFT JOIN subCategory AS sc ON sc.categoryID = c1.categoryID,
Copy linkTweet thisAlerts:
@martinsmithauthorJun 01.2010 — Thanks NogDog, it's just about there now.

I did a test on the table data, and it appears fine when there is one category linked to a related category. However, if a category has more than one related category assigned, it only shows the first 1:


subCategoryID categoryID relatedCategoryID


1 1 2

2 1 3

6 10 11

5 4 5

7 10 12

Is there an extra step in the loop?

Thank you so much for your help so far!

Martin.
Copy linkTweet thisAlerts:
@NogDogJun 01.2010 — I think you want to replace...
<i>
</i>LEFT JOIN Categories AS c2 ON c2.categoryID = sc.subCategoryID
...with...
<i>
</i>LEFT JOIN Categories AS c2 ON c2.categoryID = sc.relatedCategoryID

However, I've got a suspicion this won't be quite what you want, as I think it will list all sub-categories as categories without sub-categories (while also listing them correctly as sub-categories). I might have to think about that a bit....
Copy linkTweet thisAlerts:
@martinsmithauthorJun 02.2010 — Hi NogDog,

Apologies, i amended the query with your replace, and it gave me the output i pasted before:

subCategoryID categoryID relatedCategoryID

1 1 2

2 1 3

6 10 11

5 4 5

7 10 12

It's generating this in the SQL query:

category1 - category2 (missing out category 3)

category2

category3

category4 - category 5

category5

category6

category7

category8

category9

category10 - category11 (missing out category 12)

category11

category12

category13

Here's the updated code:

$sql = "

SELECT c1.categoryName, IFNULL( c2.categoryName, '' )

FROM category AS c1

LEFT JOIN subCategory AS sc ON sc.categoryID = c1.categoryID

LEFT JOIN category AS c2 ON c2.categoryID = sc.relatedCategoryID

GROUP BY c1.categoryID

ORDER BY c1.categoryName, c2.categoryName

";

$result = mysql_query($sql) or die(mysql_error());

$category = "";

while($row = mysql_fetch_row($result))

{

if($row[0] != $category)

{

$category = $row[0];

echo $row[0] . "<br />n";

}

if(!empty($row[1]))

{

echo "- " . $row[1] . "<br />n";

}

}

Many thanks for your help so far NogDog!
Copy linkTweet thisAlerts:
@NogDogJun 02.2010 — OK, I broke down and created the DB tables, did some tweaking and testing, and came up with...
[code=php]
$sql = "
SELECT
c1.categoryName,
IFNULL(c2.categoryName, '') AS subName
FROM
Categories AS c1
LEFT JOIN SubCategory AS sc ON sc.categoryID = c1.categoryID
LEFT JOIN Categories as c2 ON sc.relatedCategoryID = c2.categoryID
WHERE c1.categoryID NOT IN (SELECT relatedCategoryID FROM SubCategory)
ORDER BY c1.categoryName, c2.categoryName
";
$result = mysql_query($sql) or die(mysql_error());
$category = '';
while($row = mysql_fetch_row($result))
{
if($row[0] != $category)
{
$category = $row[0];
echo $row[0]."<br />n";
}
if($row[1] != '')
{
echo "- ".$row[1]."<br />n";
}
}
[/code]

It output the following:
<i>
</i>Top A
- Sub A 1
- Sub A 2
Top B
- Sub B 1
- Sub B 2
Top C

And here's the test data I used:
<i>
</i>-- phpMyAdmin SQL Dump
-- version 3.2.0.1
-- http://www.phpmyadmin.net
--
-- Host: localhost
-- Generation Time: Jun 01, 2010 at 08:20 PM
-- Server version: 5.1.36
-- PHP Version: 5.2.9-2

SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";

--
-- Database: <span><code>tester</code></span>
--

-- --------------------------------------------------------

--
-- Table structure for table <span><code>categories</code></span>
--

CREATE TABLE IF NOT EXISTS <span><code>categories</code></span> (
<span><code>categoryID</code></span> int(11) NOT NULL AUTO_INCREMENT,
<span><code>categoryName</code></span> varchar(255) NOT NULL,
PRIMARY KEY (<span><code>categoryID</code></span>)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ;

--
-- Dumping data for table <span><code>categories</code></span>
--

INSERT INTO <span><code>categories</code></span> (<span><code>categoryID</code></span>, <span><code>categoryName</code></span>) VALUES
(1, 'Top A'),
(2, 'Top B'),
(3, 'Sub A 1'),
(4, 'Sub A 2'),
(5, 'Sub B 1'),
(6, 'Sub B 2'),
(7, 'Top C');

-- --------------------------------------------------------

--
-- Table structure for table <span><code>subcategory</code></span>
--

CREATE TABLE IF NOT EXISTS <span><code>subcategory</code></span> (
<span><code>subCategoryID</code></span> int(11) NOT NULL AUTO_INCREMENT,
<span><code>categoryID</code></span> int(11) NOT NULL,
<span><code>relatedCategoryID</code></span> int(11) NOT NULL,
PRIMARY KEY (<span><code>subCategoryID</code></span>),
KEY <span><code>categoryID</code></span> (<span><code>categoryID</code></span>,<span><code>relatedCategoryID</code></span>)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;

--
-- Dumping data for table <span><code>subcategory</code></span>
--

INSERT INTO <span><code>subcategory</code></span> (<span><code>subCategoryID</code></span>, <span><code>categoryID</code></span>, <span><code>relatedCategoryID</code></span>) VALUES
(1, 1, 3),
(2, 1, 4),
(3, 2, 5),
(4, 2, 6);
Copy linkTweet thisAlerts:
@martinsmithauthorJun 02.2010 — EDIT: still not quite right... (sorry!)

The subcategories are being displayed, but then those that are subcategories aren't being shown in the main category list...
Copy linkTweet thisAlerts:
@martinsmithauthorJun 02.2010 — Hi NogDog,

I've tested with the tables etc that you created yourself, and on first glance it's fine. However, when you assign a category that has subcategories to be a subcategory itself, it causes those underneath it to disappear. Also, when an category is assigned to be a subcategory of another, it disappears from the list of main categories...

Any help would be greatly appreciated on this. I'm going through it myself to see if i can spot what may be wrong too ?

Thanks for your efforts so far.

Martin.
Copy linkTweet thisAlerts:
@NogDogJun 02.2010 — I was working under the impression that it was only a two-level structure. If it is going to have [i]n[/i] levels, then I'd probably scrap this data design and go for a "nested set" design. (Scroll down to the "Nested Set Model" heading on [url=http://dev.mysql.com/tech-resources/articles/hierarchical-data.html]this page[/url].)
Copy linkTweet thisAlerts:
@martinsmithauthorJun 02.2010 — That's absolutely fine, you've most certainly put me in the right direction with this. Thanks for all your help, it's nice to see people who go out of their way to help others. I'll pay it forward when i can on these forums.

Cheers. Martin.
Copy linkTweet thisAlerts:
@NogDogJun 02.2010 — That's absolutely fine, you've most certainly put me in the right direction with this. Thanks for all your help, it's nice to see people who go out of their way to help others. I'll pay it forward when i can on these forums.

Cheers. Martin.[/QUOTE]


For me, part of the "paying forward" is teaching myself stuff while trying to answer other people's questions. ?
Copy linkTweet thisAlerts:
@martinsmithauthorJun 13.2010 — Hi all,

I've worked out the query i need with a bit of help. Now, i need to output an XML file, based on the list of categories and their subcategories.

I have 2 tables:

category
--------


categoryID

categoryName

subcategory
------------


categoryID (PK)

parentID (PK)

The query i have is:

SELECT cat.categoryName AS categoryName, subcat.categoryName AS subcategoryName

FROM category AS cat

LEFT OUTER

JOIN subCategory ON subCategory.parentID = cat.categoryID

LEFT OUTER

JOIN category AS subcat ON subcat.categoryID = subCategory.categoryID

ORDER BY cat.categoryName, subcat.categoryName

This outputs:

Category 1
----------


  • - Category 2


  • Category 2
    ----------


  • - Category 1

  • - Category 3


  • etc.

    I'm trying to output the XML for this, but need to loop through the subcategories.

    My XML output script is as follows:

    <?php

    header("Content-type: text/xml");

    $xml_output = "<?xml version="1.0"?>n";

    $xml_output .= "<categories>n";

    dbConnect();

    $result = mysql_query("SELECT cat.categoryName AS categoryName, subcat.categoryName AS subcategoryName

    FROM category AS cat

    LEFT OUTER

    JOIN subCategory ON subCategory.parentID = cat.categoryID

    LEFT OUTER

    JOIN category AS subcat ON subcat.categoryID = subCategory.categoryID

    ORDER BY cat.categoryName, subcat.categoryName");

    while($row = mysql_fetch_assoc($result))

    {

    $xml_output .= "<category>";

    $xml_output .= "<title>" . $row['categoryName'] . "</title>";

    $xml_output .= "<related>";

    $xml_output .= "<opt>" . $row['subcategoryName'] . "</opt>";

    $xml_output .= "</related>";

    $xml_output .= "</category>n";

    }

    dbClose();

    $xml_output .= "</categories>";

    echo $xml_output;

    ?>

    I need to loop through the subcategories from the query. Do i need to do this in 2 queries, or can i use PHP arrays to do this?

    $xml_output .= "<opt>" . $row['subcategoryName'] . "</opt>";

    Any help would be appreciated ?

    Thanks.

    Martin.
    Copy linkTweet thisAlerts:
    @martinsmithauthorJun 17.2010 — Hi all,

    I'm so close now, it's unbelieveable. I've added in a break to check for subcategory, so it doesn't show the related xml items if there's no subcategory.

    The part i'm stuck with now is that when i have more than one related category, it doesn't add the second <opt> straight after, it's adds in the end part, which isn't quite right. For example:

    [CODE]<categories>

    - <category>
    <title>Work</title>
    <related>
    - <opt>
    <id>Physical Health</id>
    <title>Physical Health</title>
    <image>resources/images/saheph.jpg</image>
    </opt>
    [B] // Should be here[/B]
    </related>
    </category>
    - [B]<opt>
    <id>Relationships</id>
    <title>Relationships</title>
    <image />
    </opt>[/B]
    </categories>[/CODE]


    My up-to-date code:

    [code=php]<?php
    header("Content-type: text/xml");

    $xml_output = "<?xml version="1.0"?>n";

    $xml_output .= "<categories>n";

    dbConnect();
    $result = mysql_query("SELECT cat.categoryName AS categoryName, subcat.categoryName AS subcategoryName

    FROM category AS cat

    LEFT OUTER

    JOIN subCategory ON subCategory.parentID = cat.categoryID

    LEFT OUTER

    JOIN category AS subcat ON subcat.categoryID = subCategory.categoryID

    ORDER BY cat.categoryName, subcat.categoryName");
    $previousCategory = "";
    $previousSubCategory = ""; [B]// Added this variable to check[/B]
    while($row = mysql_fetch_assoc($result))

    {

    if ($row['categoryName'] != $previousCategory) {

    $xml_output .= "<category>";
    $xml_output .= "<title>" . $row['categoryName'] . "</title>";
    $xml_output .= "<description><![CDATA[" . $row['categoryDescription'] . "]]></description>";
    $xml_output .= "<image>" . $row['categoryImage'] . "</image>";
    $xml_output .= "<ymal>";
    }
    if ($row['subcategoryName'] != $previousSubCategory) {
    $xml_output .= "<opt>";
    $xml_output .= "<id>" . $row['subcategoryName'] . "</id>";
    $xml_output .= "<title>" . $row['subcategoryName'] . "</title>";
    $xml_output .= "<image>" . $row['subcategoryImage'] . "</image>";
    $xml_output .= "</opt>";
    }
    if ($row['categoryName'] != $previousCategory)
    {
    $xml_output .= "</ymal>";
    $xml_output .= "</category>";
    }
    $previousCategory = $row['categoryName'];

    $previousSubCategory = $row['subcategoryName'];

    }

    dbClose();
    $xml_output .= "</categories>";

    echo $xml_output;
    ?> [/code]



    If anyone can spot anything i might have missed, please let me know so i can crack this once and for all ?

    Thanks.

    Martin.
    ×

    Success!

    Help @martinsmith spread the word by sharing this article on Twitter...

    Tweet This
    Sign in
    Forgot password?
    Sign in with TwitchSign in with GithubCreate Account
    about: ({
    version: 0.1.9 BETA 5.17,
    whats_new: community page,
    up_next: more Davinci•003 tasks,
    coming_soon: events calendar,
    social: @webDeveloperHQ
    });

    legal: ({
    terms: of use,
    privacy: policy
    });
    changelog: (
    version: 0.1.9,
    notes: added community page

    version: 0.1.8,
    notes: added Davinci•003

    version: 0.1.7,
    notes: upvote answers to bounties

    version: 0.1.6,
    notes: article editor refresh
    )...
    recent_tips: (
    tipper: @AriseFacilitySolutions09,
    tipped: article
    amount: 1000 SATS,

    tipper: @Yussuf4331,
    tipped: article
    amount: 1000 SATS,

    tipper: @darkwebsites540,
    tipped: article
    amount: 10 SATS,
    )...