1

This is essentially very simple I've just been rather verbose about it. My data is structured as shown at the bottom of my post. My goal is to organise the data in a simple spreadsheet format, 'flattening' the relational database. Something like:

Product1 URL
Product2 URL URL URL
Product3 URL

My initial approach is as below. The reason I'm looking for an alternative is I have > 10,000 rows in the products table and each product has an average of 5 URLs. This generates an enourmous amount of MySQL queries (in fact exceeding my shared hosting resources), there must be a better way, right? My code has been simplified to distill the essence of my question.

My Approach

//Query to get all products
$products = mysql_query("SELECT * FROM products");

$i = 0;
//Iterate through all products
while($product  = mysql_fetch_array($products)){

    $prods[$i]['name'] = $product['name'];
    $prods[$i]['id'] = $product['id'];

    //Query to get all URLs associated with this particular product
    $getUrls = mysql_query("SELECT * FROM urls WHERE product_id =".$product['id']);

    $n = 0;

    while($url = mysql_fetch_array($getUrls)){

        $prods[$i]['urls'][$n] = $url['url'];  
    }
}

The Result

To be clear, this is the intended result, the output was expected. I just need a more efficient way of getting from the raw database tables to this multidimensional array. I suspect there's more efficient ways of querying the database. Also, I understand that this array uses a lot of memory when scaled up to the required size. As my aim is to use this array to export the data to .csv I would consider using XML (or something) as an intermediary if it is more efficient. So feel free to suggest a different approach entirely.

//The result from   print_r($prods);

Array
(
    [0] => Array
        (
            [name] => Swarovski Bracelet
            [id] => 1
            [urls] => Array
                (
                    [0] => http://foo-example-url.com/index.php?id=7327

                )
        )
    [1] => Array
        (
           [name] => The Stranger - Albert Camus
           [id] => 2
           [urls] => Array
               (
                   [0] => http://bar-example-url.com/index.php?id=9827
                   [1] => http://foo-foo-example-url.com/index.php?id=2317
                   [2] => http://baz-example-url.com/index.php?id=5644
                )
        )
)

My Database Schema

Products

id  product_name

1   Swarovski Bracelet
2   The Stranger - Albert Camus
3   Finnegans Wake - James Joyce

URLs

id  product_id  url                                                price

1   1           http://foo-example-url.com/index.php?id=7327       £16.95
2   2           http://bar-example-url.com/index.php?id=9827       £7.95
3   2           http://foo-foo-example-url.com/index.php?id=2317   £7.99
4   2           http://baz-example-url.com/index.php?id=5644       £6.00
5   3           http://bar-baz-example-url.com/index.php?id=9534   £11.50

URLs.product_id links to products.id

If you've read this, thank you for your incredible patience! I look forward to reading your response. So how can I do this properly?

IsisCode
  • 2,490
  • 18
  • 20
  • How do you plan to represent a two-level hierarchy in CSV? – Marcelo Cantos Sep 06 '11 at 10:32
  • @Marcelo Cantos: OP told us that every product could have up to five URLS; anyway, he could save into csv `product;url 1;...;url n` and when retrieves this file reads product and then, one at a time, urls until the end of the row – Marco Sep 06 '11 at 10:37
  • Products in the left-most column, URLs in columns to the right as many as are needed. There will be blank spaces in the spreadsheet. – IsisCode Sep 06 '11 at 10:37
  • Does your code actually work? You don't appear to be incrementing the $i and $n variables..? Also improve code your readability by specifying the fieldnames in the SQL query rather than using SELECT * FROM.... – Bruce Sep 06 '11 at 10:37
  • what is your aim? save query?how about join? – ajreal Sep 06 '11 at 10:38
  • @Marco: There are several ways to structure the output. I wanted to know what the OP actually intended, because it will have a big influence on how it should be answered. – Marcelo Cantos Sep 06 '11 at 10:39
  • The actual code works, here in my haste I forgot to include the increments when simplifying my code. Sorry for any confusion. – IsisCode Sep 06 '11 at 10:39
  • @Isiscode - no worries also $prods[$i]['name'] = $product['name']; should that not read $prods[$i]['name'] = $product['product_name']; – Bruce Sep 06 '11 at 10:44
  • bruce182, yes, another error in my explanation but it's unrelated to my actual problem. Well spotted anway. – IsisCode Sep 06 '11 at 10:46

3 Answers3

3

The SQL

 SELECT p.id, p.product_name, u.url 
 FROM products p, urls u 
 WHERE p.id = u.product_id ORDER BY p.id

PHP code to "flatten" the urls, I have just seperated the urls with a space.

$i = 0;
$curr_id;
while($product  = mysql_fetch_array($products))
{
    $prods[$i]['name'] = $product['product_name'];
    $prods[$i]['id'] = $product['id'];

    if($product['id'] == $curr_id)
    {
        $prods[$i]['urls'] .= " ".$url['url'];  
    }
    else
    {
        $prods[$i]['urls'] = $url['url']; 
    }
    $i++;
    $curr_id = $product['id'];
}

The Result

[0] => Array
        (
            [name] => Swarovski Bracelet
            [id] => 1
            [urls] => http://foo-example-url.com/index.php?id=7327
        )
  [1] => Array
        (
           [name] => The Stranger - Albert Camus
           [id] => 2
           [urls] => http://bar-example-url.com/index.php?id=9827 http://foo-foo-example-url.com/index.php?id=2317 http://baz-example-url.com/index.php?id=5644

        )
Bruce
  • 1,542
  • 13
  • 17
2

You are able to pull back that exact information with one query even if you want to pull back the csv of urls.

SELECT 
    p.id, p.product_name, 
    GROUP_CONCAT(u.url) AS URLS 
    FROM products p LEFT JOIN urls u 
    ON p.id = u.product_id 
    GROUP BY p.id, p.product_name

And then you have 1 query to your database that brings back what you need.

Or the cleaner approach is how bruce suggested and then let PHP merge all of the results together.

Marco
  • 56,740
  • 14
  • 129
  • 152
judda
  • 3,977
  • 1
  • 25
  • 27
  • That's the perfect solution: +1 for you! – Marco Sep 06 '11 at 10:56
  • 1
    Take note on the default max length for group_concat is 1024, and in hosting company normally they won't allow you to change this server variable dynamically or simply don't give you privileges to do so – ajreal Sep 06 '11 at 11:21
1

You can accomplish this in one query:

select * from products join urls on products.id = urls.product_id order by products.id

pseudocode:

while(query_result) {
     if (oldpid == pid) { print ", \"url\"" }
     else { print "pid, \"url\"" }
     oldpid = pid;
}
Jeff Ferland
  • 17,832
  • 7
  • 46
  • 76