12

I have the following data in a MySQL database:

Autonum  ID  Name  MetaValue
1         1  Rose  Drinker
2         1  Rose  Nice Person
3         1  Rose  Runner
4         2  Gary  Player
5         2  Gary  Funny

I am working in PHP now but I have encountered this problem several times using C#, Java and other languages.

Now my goal in the past and present has been to display the data in the following format:

<table>
    <thead>
    <th>Name</th>
    <th>MetaValue1</th>
    </thead>
    <tbody>
           <tr>      
               <td>Rose</td>
                <td>
                    <ul>
                        <li>Drinker</li> 
                        <li>Nice Person</li>
                        <li>Runner</li>
                     </ul>
                </td>
            </tr>
            <tr>
                <td>Gary</td>
                <td>
                    <ul>
                         <li>Player</li>
                         <li>Funny</li>
                 </td>
                </tr>
        </tbody>

</table> 

I have tackled this problem before by creating a class that represents my SQL table. Then I created a Dictionary to hold EmployeeId and the class.

Dictionary<string,MyTable> MyData = new <string,MyTable>();
Table MyMetaData = new Table();
MyMetaData SomeMetaData=getMetaValueList();//imagine a web service that does that
MyData.add(EmployeeId,SomeMetaData);

I am skipping steps but I hope you get my point. I probably just need keywords of what to call this type of problem. What is the preferred way to accomplish this?

Kenny Cason
  • 12,109
  • 11
  • 47
  • 72
hidden
  • 3,216
  • 8
  • 47
  • 69
  • 3
    Is there a reason you need to only have one table? My first instinct would be to split off the metavalue (or name depending on how you're looking at it) so that each table only contains the data once. – nwalke Jul 17 '12 at 18:19
  • I am dealing with wordpress wp_postmeta table which stores metavalue in a new row each time. – hidden Jul 17 '12 at 18:22
  • What is your major point? What are you trying to achieve? Where do you hit the road block? PHP has not dictionaries, but `Array`s and `ArrayAccess`. – hakre Jul 23 '12 at 16:33
  • I'm confused on the bounty - what is meant by "Alternatives to html synatx"? It's a web page, are there any alternatives? – DACrosby Jul 25 '12 at 06:03
  • alternative would mean instead of listing it on as ul maybe someone uses columns. – hidden Jul 27 '12 at 04:24

14 Answers14

10

I do not like SQL solution for one reason. You ask database engine to join string and then you use PHP to split it. It is like repeated work. Another down side is that what if metavalue contains seperator character.

My code just demonstrates how you can easily group data with 2 dimensional array in PHP and it is up to coder to decide which method to retrieve data from database either legacy mysql_* or PDO.

// get result
$result = mysql_query("SELECT autonum,id,name,metavalue FROM table");

// loop through each row
while ($row = mysql_fetch_assoc($result)) {
    $table[$row['name']][] = $row['metavalue'];
}

// print it out
print_r($table);

There is no need to temporarily store the whole resultset into an array. You can iterate straight ahead by first sort data by what field you want to group your data and keep track of state.

However, there are two reasons why I prefer this style and it is personal preference (it is not the must-must do solution):

  1. Usually, everyone want to separate logic and presentation apart. Storing data in array can be easily passed to your template engine. I prefer using PHP template engine for it is easy to implement and it runs at light speed.

  2. I would rather trade slight performance speed and memory usage for readability and maintainability. The algorithm is short and simple to understand. If performance is your top priority, you can always use cache such as storing in file, storing in memcached, or storing calculated result in database.

True, we are grouping data from different side, but you assume that the metavalue does not contain separator (which in your code is |). It can be easily fixed by choosing separator that almost impossible to be used by metavalue. Anyway, your solution will works perfectly fine in almost every case because metavalue that @jvelez has shown will never contain character |.

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
invisal
  • 11,075
  • 4
  • 33
  • 54
  • I will check this out. His solution only works in certain scenarios. – hidden Jul 17 '12 at 21:14
  • @jvelez , which scenarios? That PHP code is doing exactly the same as my SQL code. The distinction is only on which side you perform the grouping. – tereško Jul 23 '12 at 12:48
  • You should also mention attacking the problem of not using PDO. – Bailey Parker Jul 24 '12 at 00:45
  • There is no need to temporarily store the whole resultset into an array of it's own, you can just iterate straight ahead with very little logic: http://stackoverflow.com/a/11619281/367456 - works with PDO/mysqli, too. – hakre Jul 24 '12 at 01:23
  • @invisal: The logic is encapsulated with the iterators (as opposed to opened in the array), the usage is equally straight forward and I'll add an example later on that offers this configurable. – hakre Jul 24 '12 at 09:12
9

The problem is not in the php/java/c# code, but in the SQL end of things.

SELECT ID      AS id,
       Autonum AS num,
       Name    AS name,
       GROUP_CONCAT
       (
           ORDER BY MetaValue DESC SEPARATOR '|'
       )       AS list
FROM Things
GROUP BY Things.ID

This way you get a list of name values, and you all the associated MetaValues for that item, you just split the list on '|'.

In PHP you do it with explode(), in C# with String.Split() and, I assume, that you will be able to find something similar for any language.

Generation of HTML becomes quite trivial.


Here is a PHP example ( with PDO connection API used ):

$statement = $pdo->query( $sql );

if ( $statement->execute() )
{
    $data = $statement->fetchAll(PDO::FETCH_ASSOC);

    foeach ( $data as $row )
    {
         echo '<tr><td>' , $row['name'] , '</td>';
         echo '<td><ul><li>' , str_replace( '|', '</li><li>' , $row['list']) ;
         echo '</li></ul></td></tr>';
    }
}

This would generate the <tbody> content from your example.

tereško
  • 58,060
  • 25
  • 98
  • 150
  • I got hit with 2 downvotes within a couple minutes of eachother, probably around the same time you did. Dunno what the deal is either. – drew010 Jul 23 '12 at 15:47
  • There's a 500 points bounty, and some people really want to be banned from so. – oxygen Jul 26 '12 at 15:29
  • @Tiberiu-IonuțStan , this is why , IMHO, the 500 bounties on simple issues are counter-productive. Author would have had much better responses with 100 point bounty. Just sort by age and count how many **unique** solutions here are: 4.5 (hakre's solution is the 1/2 because it uses alternative method to iterate) by my count. – tereško Jul 26 '12 at 15:52
3

Please see your data, it is sorted and ordered:

Autonum  ID  Name  MetaValue
1         1  Rose  Drinker
2         1  Rose  Nice Person
3         1  Rose  Runner
4         2  Gary  Player
5         2  Gary  Funny

So what you can do is to solve that by the output:

  • Each time the ID/Name changes, you have a new table row.
  • Each time the MetaValue changes you have a new list entry.

All you need to do is to do a look-ahead onto the next element to see the change. That means you iterate in advance (pre-calculated iteration) or you do a cached iteration (e.g. with CachingIterator in PHP).

If you then keep track of the state as well, you can say per each entry whether:

  • A table row needs to be opened before it.
  • A table row needs to be closed after it.

Actually it's even much more simple, here an array based rows iteration example (Demo):

$rows = array(
    array(1, 1, 'Rose', 'Drinker'),
    array(2, 1, 'Rose', 'Nice Person'),
    array(3, 1, 'Rose', 'Runner'),
    array(4, 2, 'Gary', 'Player'),
    array(5, 2, 'Gary', 'Funny'),
);

echo "<table>\n    <thead>\n    <th>Name</th>\n    <th>MetaValue1</th>\n    </thead>\n    <tbody>\n";

$events = new Events();
$last = array(2 => null);
foreach($rows as $row)
{
    if ($last[2] !== $row[2]) {
        $events->newRow($row[2]);
    }
    $events->newItem($row[3]);
    $last = $row;
}
$events->closeRow();

echo "    </tbody>\n</table>";

Not much code to solve your problem because the problem itself is just a little bit of state inside the data.

What has been described as events here can be inverted by creating your own iterator which can be passed to a template system that supports iterators, e.g. twig or mustache. A full blown example of that is shown in previous answers of mine and inside a blog post:

Compared with the example above it might not look that simple, however it hopefully gives enough code to make you make up your mind where you want to go. It's fairly extensible.
Community
  • 1
  • 1
hakre
  • 193,403
  • 52
  • 435
  • 836
  • I added the example with iterators as another answer: http://stackoverflow.com/a/11619281/367456 – hakre Jul 23 '12 at 19:37
2

Here is a solution, it is similar to @invisal's in the way we create an array based on a property of the table to group the results together.

<?php

$mysqli = new mysqli("localhost", "uname", "pword", "wordpress");
if ($mysqli->connect_errno) {
    die("Failed to connect to MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error);
}

$res = $mysqli->query("SELECT * FROM wp_postmeta");

// array that will hold the final results
$results = array();

// while we have more rows
while ($row = $res->fetch_assoc()) {
    // check to see if we already have this ID
    if (!array_key_exists($row['ID'], $results)) {
            // if not create a new index for it in $results, which holds an array
        $results[$row['ID']] = array();
    }

    // create new stdClass object that holds the row data
    $obj = new stdClass;
    $obj->Name = $row['Name'];
    $obj->MetaValue = $row['MetaValue'];

    // append this data to our results array based on the ID
    $results[$row['ID']][] = $obj;
}
?>

<table>
    <thead>
    <th>Name</th>
    <th>MetaValue</th>
    </thead>
    <tbody>

<?php foreach($results as $id => $values): ?>
    <tr>
        <td style="vertical-align: top"><?php echo $values[0]->Name ?></td>
        <td>
            <ul>
            <?php foreach($values as $value): ?>
                <li><?php echo $value->MetaValue ?></li>
            <?php endforeach; ?>
            </ul>
        </td>
    </tr>
<?php endforeach; ?>
    </tbody>
</table>

This seems to produce your desired results. Hope that helps.

drew010
  • 68,777
  • 11
  • 134
  • 162
  • so you made it two dimentsional array to store the meta values. I like it. So did invisal... – hidden Jul 21 '12 at 19:50
  • Yes, the main reason for the 2-D array is so the first dimension holds the primary identifier so it is easy to group the results for each individual user together. Then when you iterate over that array, you are basically looping over one user at a time. It also means that your query results don't have to be in any particular order, it will group the results together as needed regardless of whether or not the IDs come out in sequence, or randomly. – drew010 Jul 21 '12 at 20:02
2

You asked how to call this type of problem: Its called grouping. One possibility is indeed to solve it in SQL with the help of the GROUP BY clause (but different SQL engines have varying support for these, especially when it comes to more complex aggregations of the data to be grouped).

In C# the basic solution is a one-liner thanks to LINQ, just use the GroupBy extension method:

var data = new [] {
    new { RowId = 1, EmpId = 1, Name = "Rose", Value = "Drinker" },
    new { RowId = 2, EmpId = 1, Name = "Rose", Value = "Runner" },
    new { RowId = 3, EmpId = 2, Name = "Gary", Value = "Player" },
};

var grouping = data.GroupBy(row => row.Name);
var sb = new StringBuilder();
foreach (var grp in grouping) {
    sb.AppendFormat("<tr><td>{0}</td>\n", grp.Key);
    sb.AppendLine("  <td><ul>");
    foreach (var element in grp) {
        sb.AppendFormat("    <li>{0}</li>\n", element.Value);
    }
    sb.AppendLine("  </ul></td>\n</tr>");
}

Console.WriteLine(sb.ToString());

The basic idea behind GroupBy is just some kind of an associative array or dictionary, with the attributes to group by as the key and a list of all rows associated with that key. For example (using the variable data with the above definition):

var map = new Dictionary<Tuple<int, string>, List<object>>();
foreach (var row in data) {
    var key = Tuple.Create(row.EmpId, row.Name);
    if (!map.ContainsKey(key))
        map.Add(key, new List<object>());

    map[key].Add(row);
}

The above snippet is conceptually the same as the PHP based answers from drew010 and invisal (they use a two-dimensional PHP array, which is conceptually the same as above Dictionary of Lists). I tried to make the steps as explicit as possible and not use too much special features of C# (for example in a real program you should tend to use a proper class of its own for complex keys instead of an simple Tuple), so the above snippet should be easily portable to Java and other languages.

As you can see by all the examples here, after grouping the data, the HTML generation is always the same (two nested loops).

Stefan Nobis
  • 957
  • 4
  • 12
  • This grouping iterator is interesting. Never done that in PHP, I came up with this http://stackoverflow.com/a/11616884/367456 which does the "grouping" on the fly but with iterator nesting it is more compatible with outputting these structures. I might add that. – hakre Jul 23 '12 at 17:36
2

I would solve this in a different way. Right now, you're having issues because you're database schema is not normalized. Instead, I would start off by altering the schema. Right now, you have this:

CREATE TABLE foo {
    `autonum` INT(12) NOT NULL AUTO_INCREMENT,
    `id` INT(12) NOT NULL,
    `name` VARCHAR(255) NOT NULL,
    `metaValue` VARCHAR(255) NOT NULL,
    PRIMARY KEY(autonum)
)

Instead, I would split it out into two tables:

CREATE TABLE user {
    `id` INT(12) NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(255) NOT NULL,
    PRIMARY KEY(id)
)
CREATE TABLE user_meta {
    `user_id` INT(12) NOT NULL,
    `value` VARCHAR(255) NOT NULL
)

Ignoring the fact that this looks like an EAV table (which it isn't, meta is just mis-named due to the OP's naming), it should be clear how this is easier. So let's look at your record, and build up the table:

#User
id   | name
1    | Rose
2    | Gary

#User Meta
user_id | value
1       | drinker
1       | Nice Person
1       | Runner
2       | Player
2       | Funny

Now that we have the data, let's look at how we'd extract it. Let's assume that we always want to print out all users (limiting it would be simple, but for clarity, we can just do all for now). So, I would setup a simple class around each table here, caching the data into a single query.

class User implements IteratorAggregate {
    protected $data = array();
    public function __construct() {
        $query = mysql_query("SELECT id, name FROM user");
        while ($row = mysql_fetch_assoc($query)) {
            $this->data[$row['id']] => $row['name'];
        }
    }
    public function getIterator() {
        return new ArrayObject($this->data);
    }
}

class User_Meta {
    protected $data = array();
    public function __construct() {
        $query = mysql_query("SELECT user_id, value FROM user_meta");
        while ($row = mysql_fetch_assoc($query)) {
            if (empty($this->data[$row['user_id']])) {
                $this->data[$row['user_id']] = array();
            }
            $this->data[$row['user_id']][] = $row['value'];
        }
    }
    public function getDataForUserId($id) {
        return empty($this->data[$id]) ? array() : $this->data[$id];
    }
}

Now, constructing the output becomes trivial:

$users = new User;
$meta = new User_Meta;

echo "<table>
<thead>
<th>Name</th>
<th>MetaValue1</th>
</thead>
<tbody>";

foreach ($users as $id => $name) {

    echo "<tr>      
           <td>".htmlspecialchars($name)."</td>
            <td>
                <ul>";
    foreach ($meta->getDataForUserId($id) as $metaValue) {
         echo "<li>" . htmlspecialchars($metaValue) . "</li>";
    }

    echo "      </ul>
            </td>
           </tr>";
}

echo "</tbody></table>";

Now, with all of that said, I would personally implement it a bit different, using first-class business objects and data mappers for everything.

And I did commit a few felonies here, but I did it for simplicity. Here's what I would have done differently:

  1. Not do logic in the constructor of an object. Instead, I would have injected it or used a factory to pre-populate the data.
  2. Abstract out the concept of the relationships into separate business objects.
  3. Separate out the pull logic from the display logic.
  4. Name things better (metavalue is not a good name for a field).

But it really depends on the exact requirements of the application. There is no one (or two) right ways to do this. Each of the possible ways has pros and cons. Learn to weigh the tradeoffs so that you can pick the right one for your requirements...

ircmaxell
  • 163,128
  • 34
  • 264
  • 314
  • Restructuring DB of course would be the best option, but I'm not sure it it is even possible in OP's case. He mentioned table `wp_postmeta`. – tereško Jul 23 '12 at 18:24
  • 1
    @tereško: he mentioned it in the comment. The original post says nothing about it. So I answered the general question, rather than solving his specific problem. And in the general case, restructuring the database schema is the better alternative... – ircmaxell Jul 23 '12 at 19:58
  • I am using wordpress! I am not restructing their tables but good answer if I had flexibility. – hidden Jul 27 '12 at 04:21
1

I had a similar table once and I ended up using some SQL to return the data as individual rows. I have modified my objects to use your sql and written a simple function that will output your data in the table following the format that you used in your question.

The main query aggregate function that I used is the MySQL GROUP_CONCAT. This is similar to an implode() function. The default separator is a comma, so if you have commas in your metadata you can change it to a different marker by adding SEPARATOR 'yourSeparator' inside the brackets after the column name. You can also use a distinct infront of the column name to only select distinct rows in the group_concat.

I have put the table header section inside the try statement, so that if you don't have any results, you won't be displaying a half-generated table.

<?php


class myUser
{
    public $ID;
    public $name;
    public $metas;
}

class myTables
{
    public $SQL="
select 
    ID,
    name,
    GROUP_CONCAT(metavalue) as metas
FROM 
    table1
GROUP BY 
    ID,
    name;";

    public function outputTable()
    {
        $hostname="mysql:host=localhost;dbname=test";
        $username="testdb";
        $password="testdbpassword";

        try{
            echo "
        <table>
            <thead>
            <th>Name</th>
            <th>MetaValue</th>
            </thead>
            <tbody>
            ";
            $dbh = new PDO($hostname, $username, $password);
            $stmt = $dbh->query($this->SQL);
            $obj = $stmt->setFetchMode(PDO::FETCH_INTO, new myUser);
            foreach($stmt as $myUser)
            {
                echo "
                        <tr><td>".$myUser->name."</td><td><ul>";
                $metas=explode(",", $myUser->metas);
                for($i=0;$i<count($metas);$i++)
                {
                    echo "<li>".$metas[$i]."</li>";
                }
                echo "</ul></td></tr>";
            }
            unset($obj);
            unset($stmt);
            unset($dbh);
            echo "
            </tbody>
        </table>
            ";
        }
        catch(PDOException $e){
            echo 'Error : '.$e->getMessage();
            exit();
        }
    }

}

$myPage= new myTables();
$myPage->outputTable();
?>

Example Output:

<table>
    <thead>
    <th>Name</th>
    <th>MetaValue1</th>
    </thead>
    <tbody>
    <tr><td>Rose</td><td><ul><li>Drinker</li><li>Nice Person</li><li>Runner</li></ul></td></tr>
    <tr><td>Gary</td><td><ul><li>Player</li><li>Funny</li></ul></td></tr>
   </tbody>
</table>

I cut out the Autonum from your query, as it was otherwise buggering up the aggregate function. If you do however need it, you would have to aggregate it in a similar fashion. The group_concat function will skip over null fields, so you will need to bring them in cunningly otherwise your autonum IDs won't match your results. I did this here with a simple coalesce() inside the group_concat function.

To accomodate these extra bits, I rewrote the object up somewhat. This should do everything you need, I have left my debugging notes in which are set by the private variable isDebug which I have set to false, but setting it to true will give you extra information as the object runs through the functions. This will help you with your debugging as I assume your source data is actually much more complex.

<?php


class myUser
{
    public $ID;
    public $name;
    public $metaDesc;
    public $metaNum;
    public $metaDescs;
    public $metaNums;
}
// Basic User stored here. One Object per Row is created.

class myTables
{
    private $isDebug=false; // Change this to true to get all validation messages;
    private $hostname="mysql:host=localhost;dbname=test";
    private $username="testdb";
    private $password="testdbpassword";
    private $myUsers=array();
    private $curUser = myUser;
    private $userCount=0;
    private $SQL="
select 
    ID,
    name,
    GROUP_CONCAT(coalesce(metavalue,'null')) as metaDesc,
    group_concat(autonum) as metaNum
FROM 
    table1
GROUP BY 
    ID,
    name;";

    public function getuserData()
    {
        $dbh = new PDO($this->hostname, $this->username, $this->password);
        $stmt = $dbh->query($this->SQL);
        $obj = $stmt->setFetchMode(PDO::FETCH_INTO, new myUser);
        $userCount=0;
        foreach($stmt as $myUser)
        {
            $this->myUsers[$userCount]=new myUser;
            $this->myUsers[$userCount]->ID=$myUser->ID;
            $this->myUsers[$userCount]->name=$myUser->name;
            $this->myUsers[$userCount]->metaDesc=$myUser->metaDesc;
            $this->myUsers[$userCount]->metaNum=$myUser->metaNum;
            $userCount++;
        }
        $this->userCount=$userCount;
        if($this->isDebug){echo "There are ".$this->userCount." users found.<br>";}
        unset($obj);
        unset($stmt);
        unset($dbh);
    }
    // Pulls the data from the database and populates the this->object.

    public function outputTable()
    {
        echo "
    <table>
        <thead>
        <th>Name</th>
        <th>MetaValue</th>
        </thead>
        <tbody>
        ";
        for($i=0; $i<$this->userCount; $i++)
        {
            if($this->isDebug){echo "Running main cycle. There are ".$this->userCount." elements.";}
            $this->myUsers[$i]->metaDescs=explode(',', $this->myUsers[$i]->metaDesc);
            $this->myUsers[$i]->metaNums=explode(',', $this->myUsers[$i]->metaNum);
            if($this->isDebug){echo "This user has ".(count($this->myUsers[$i]->metaDescs))." segments<br>";}
            if($this->isDebug){echo "My first segment is ".($this->myUsers[$i]->metaDesc)."<br>";}
            echo "<tr><td>".$this->myUsers[$i]->name."</td><td><ul>";
            for($j=0;$j<count($this->myUsers[$i]->metaDescs);$j++)
            {
                echo "<li>ID: ".$this->myUsers[$i]->metaNums[$j]." - ".$this->myUsers[$i]->metaDescs[$j]."</li>";
            }
            echo "</ul></td></tr>";
        }
            echo "
            </tbody>
        </table>
            ";
    }
    // Outputs the data held in the object into the table as required.

}

$myPage= new myTables();
$myPage->getUserData();
$myPage->outputTable();
?>

The output now looks like this:

<table>
    <thead>
    <th>Name</th>
    <th>MetaValue</th>
    </thead>
    <tbody>
    <tr><td>Rose</td><td><ul><li>ID: 1 - Drinker</li><li>ID: 2 - Nice Person</li><li>ID: 3 - Runner</li></ul></td></tr>
    <tr><td>Gary</td><td><ul><li>ID: 4 - Player</li><li>ID: 5 - Funny</li><li>ID: 6 - null</li><li>ID: 7 - Smelly</li></ul></td></tr>
</tbody>
</table>

Name    MetaValue
Rose    

ID: 1 - Drinker
ID: 2 - Nice Person
ID: 3 - Runner

Gary    

ID: 4 - Player
ID: 5 - Funny
ID: 6 - null
ID: 7 - Smelly
Fluffeh
  • 33,228
  • 16
  • 67
  • 80
1

I recommend you to create a function in database

create function get_meta(@autonum int)
 returns varchar(500)
as
 declare @meta varchar(500)='<ul>';
 select @meta=@meta+'<li>'+MetaValue+'</li>' from mytable where Autonum=@autonum
 return @meta+'</ul>';

and use a clean statement in the PHP side like this.

select id,autonum,name, get_meta(autonum) as meta_output from mytable

then you need to do the

echo $row["meta_output"]; 

like any other column. This is what I would use.

Uğur Gümüşhan
  • 2,455
  • 4
  • 34
  • 62
  • 1
    Generating HTML in the SQL functions is a really REALLY bad idea. Also this would perform additional `SELECT` or each table entry. – tereško Jul 23 '12 at 16:08
  • 1
    @tereško you being against it doesn't make it a bad idea. it works and it works just fine. selecting repeated actor names when selecting the items from the MetaValue column also causes additional IO traffic and this code doesn't do that. – Uğur Gümüşhan Jul 23 '12 at 16:26
  • 1
    Nice deflection: *"you being against it doesn't make it a bad idea"*. You managed dismiss the question with little bit of `ad hominem`. The problem with your code is that you put presentation logic on the SQL server. What if I need to add a CSS class to the `
  • `? What if there are two different templates where the data is used?
  • – tereško Jul 23 '12 at 16:38
  • No need to do it in the database actually, and only one select (no sub-selects) is necessary: http://stackoverflow.com/a/11616884/367456 – hakre Jul 23 '12 at 17:34
  • @tereško there are many options like adding a li{} to the style sheet, wrapping around the container or adding class to the relevant td. keeping php clean has many benefits. – Uğur Gümüşhan Jul 24 '12 at 00:44
  • @hakre other methods aren't any better, they multiply the amount of fetch operations. – Uğur Gümüşhan Jul 24 '12 at 00:45
  • The amount of fetch operations? Not at all: http://stackoverflow.com/a/11619281/367456 - that is a straight forward iteration over the resultset of simple select query with no subselects (your mysql function contains a subselect for example). – hakre Jul 24 '12 at 01:18
  • @hakre php solution fetches rows which are repeated, like rose rose gary gary. my solution decreases the unnecessary traffic caused by the redundant data. – Uğur Gümüşhan Jul 24 '12 at 01:23
  • So you are suggesting to replace the repetition of rose and gary with
    • . Hmm. That has a negative difference of 5 bytes per metavalue and additionally 8 bytes per name. If the other fields are 16bit long, that will mean your solution transports one byte more per metavalue and 8 bytes more per name. Which renders the prevention of redundancy useless. - And all that while @tereško already pointed with the finger to the issue you have. CSS does not save you here. The real solution: 1.) normalize the data in the database, 2.) use a RDBMS that supports lists in a resultset row. – hakre Jul 24 '12 at 01:45
    • And just for the understanding why I do not suggest that in my answer: OP wrote that he is using wordpress. So I assume that touching the database is not an option here, so I didn't even bother with it. – hakre Jul 24 '12 at 01:54
    • @hakre I also recommend normalization in this case if possible. – Uğur Gümüşhan Jul 24 '12 at 02:24
    • @hakre fetching each row is way more than
        and
      • row fetches more than just redundancy in one column.. several columns are repeated here.
      – Uğur Gümüşhan Jul 24 '12 at 02:26
    • @hakre I expect that fetch+fetch+fetch+fetch takes more time than fetch – Uğur Gümüşhan Jul 24 '12 at 02:29
    • If you are that concerned, I suggest to actually request the full dataset from OP and run some metrics on your own first. But be aware that you might do premature optimization here. – hakre Jul 24 '12 at 02:51
    • @hakre OP should actually write his web page in machine language using a casio calculator for best optimization. - My answer has no major faults and it's relatively compact and cleaner than most answers here. – Uğur Gümüşhan Jul 24 '12 at 03:00
    • Your answer has the major falult of mixing output code into the database I'd say, but naturally that is subjective. You prefer stretching spaghetti code even into the database, so I won't stop you but don't expect me to applaud and shout bravo either. – hakre Jul 24 '12 at 03:04