Exibição de uma tabela em PHP com colunas repetidas

Tenho os seguintes dados em um database MySQL:

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 

Estou trabalhando no PHP agora, mas eu encontrei esse problema várias vezes usando C #, Java e outros idiomas.

Agora, meu objective no passado e no presente foi exibir os dados no seguinte formato:

 
Name MetaValue1
Rose
  • Drinker
  • Nice Person
  • Runner
Gary
  • Player
  • Funny

Eu abordei esse problema antes criando uma class que representa minha tabela SQL. Então eu criei um Dictionary para segurar EmployeeId e a class.

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

Estou puxando passos, mas espero que você tenha o meu ponto. Eu provavelmente só preciso de palavras-chave sobre o que chamar esse tipo de problema. Qual é a maneira preferida de conseguir isso?

Não gosto da solução SQL por uma razão. Você solicita ao mecanismo de database que se junte à string e, em seguida, você use o PHP para dividi-lo. É como um trabalho repetido. Outro lado negativo é que se o metavalue contenha personagem seperator.

Meu código apenas demonstra como você pode facilmente agrupar dados com matriz bidimensional em PHP e cabe ao codificador decidir qual método recuperar dados do database ou mysql_ * legado ou 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); 

Não é necessário armazenar temporariamente todo o conjunto de resultados em uma matriz. Você pode iterar em frente pelo primeiro tipo de dados pelo campo que deseja agrupar seus dados e acompanhar o estado.

No entanto, existem duas razões pelas quais eu prefiro esse estilo e é uma preferência pessoal (não é a solução obrigatória para fazer):

  1. Normalmente, todos querem separar a lógica e a apresentação separadas. O armazenamento de dados na matriz pode ser facilmente passado para o seu mecanismo de modelo. Eu prefiro usar o mecanismo de modelo do PHP, pois é fácil de implementar e ele funciona em velocidade de luz.

  2. Preferiria trocar ligeira velocidade de desempenho e uso de memory por legibilidade e capacidade de manutenção. O algoritmo é curto e simples de entender. Se o desempenho for sua prioridade máxima, você sempre pode usar o cache, como armazenar no arquivo, armazenar no memcached ou armazenar o resultado calculado no database.

É verdade que estamos agrupando dados de um lado diferente, mas você assume que o metavalue não contém separador (o que em seu código é |). Pode ser facilmente corrigido escolhendo o separador que quase impossível ser usado pelo metavalue . De qualquer forma, sua solução funcionará perfeitamente em quase todos os casos porque metavalue que @jvelez mostrou nunca contêm caráter |.

O problema não está no código php / java / c #, mas no SQL final das coisas.

 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 

Desta forma, você obtém uma lista de valores de name , e você todos os MetaValues ​​associados para esse item, basta dividir a list em '|' .

Em PHP você faz isso com explode() , em C # com String.Split() e, eu suponho, que você poderá encontrar algo semelhante para qualquer idioma.

A geração de HTML torna-se bastante trivial.

Aqui está um exemplo PHP (com API de conexão PDO usada):

 $statement = $pdo->query( $sql ); if ( $statement->execute() ) { $data = $statement->fetchAll(PDO::FETCH_ASSOC); foeach ( $data as $row ) { echo '' , $row['name'] , ''; echo '
  • ' , str_replace( '|', '
  • ' , $row['list']) ; echo '
'; } }

Isso geraria o conteúdo

do seu exemplo.

Veja seus dados, ele é ordenado e ordenado:

 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 

Então, o que você pode fazer é resolver isso pela saída:

  • Cada vez que o ID / Nome muda, você tem uma nova linha de tabela.
  • Cada vez que o MetaValue muda, você tem uma nova input na lista.

Tudo o que você precisa fazer é fazer uma olhada no próximo elemento para ver a mudança. Isso significa que você itera antecipadamente (iteração pré-calculada) ou faz uma iteração em cache (por exemplo, com CachingIterator em PHP).

Se você também acompanhar o estado, você pode dizer por cada input se:

  • Uma linha da tabela precisa ser aberta antes dela.
  • Uma linha de tabela precisa ser fechada após ele.

Na verdade, é ainda muito mais simples, aqui um exemplo de iteração de linhas baseadas em matriz ( 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 "\n \n \n \n \n \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 " \n
NameMetaValue1
";

Não há muito código para resolver seu problema porque o problema em si é apenas um pouco de estado dentro dos dados.

O que foi descrito como events aqui pode ser invertido criando seu próprio iterador que pode ser passado para um sistema de modelo que suporte iteradores, por exemplo, twig ou bigode. Um exemplo completo disso é mostrado em minhas respostas anteriores e dentro de uma postagem de blog:

  • Obtendo o modelo ajustado nested em uma subtreada “fechada”, mas escondida
  • Como posso converter uma série de relações pai-filho em uma tree hierárquica?
  • Algum PHP Iterator Fun

Comparado com o exemplo acima, pode não parecer tão simples, no entanto, espero que ele forneça código suficiente para fazer você decidir aonde você quer ir. É bastante extensível.

Aqui está uma solução, é semelhante ao @ invisal’s na forma como criamos uma matriz baseada em uma propriedade da tabela para agrupar os resultados juntos.

 < ?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; } ?>  < ?php foreach($results as $id => $values): ?>  < ?php endforeach; ?> 
Name MetaValue
< ?php echo $values[0]->Name ?>
    < ?php foreach($values as $value): ?>
  • < ?php echo $value->MetaValue ?>
  • < ?php endforeach; ?>

Isso parece produzir os resultados desejados. Espero que ajude.

Eu resolveria isso de uma maneira diferente. Agora, você está tendo problemas porque o esquema do database não está normalizado. Em vez disso, eu começaria alterando o esquema. Agora, você tem isso:

 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) ) 

Em vez disso, eu dividi-lo em duas tabelas:

 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 ) 

Ignorando o fato de que isso parece uma tabela EAV (o que não é, o meta é apenas um nome incorreto devido à nomeação do OP), deve ser claro como isso é mais fácil. Então vejamos seu registro e crie a tabela:

 #User id | name 1 | Rose 2 | Gary #User Meta user_id | value 1 | drinker 1 | Nice Person 1 | Runner 2 | Player 2 | Funny 

Agora que temos os dados, vejamos como o extrairemos. Vamos assumir que sempre queremos imprimir todos os usuários (limitando isso seria simples, mas por clareza, podemos fazer tudo por enquanto). Então, eu configuraria uma class simples em torno de cada tabela aqui, armazenando em cache os dados em uma única consulta.

 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]; } } 

Agora, a construção da saída torna-se trivial:

 $users = new User; $meta = new User_Meta; echo ""; foreach ($users as $id => $name) { echo ""; } echo "
Name MetaValue1
".htmlspecialchars($name)."
    "; foreach ($meta->getDataForUserId($id) as $metaValue) { echo "
  • " . htmlspecialchars($metaValue) . "
  • "; } echo "
";

Agora, com tudo isso dito, eu pessoalmente o implementaria um pouco diferente, usando objects de negócios de primeira class e mapeadores de dados para tudo.

E eu cometi alguns crimes aqui, mas eu fiz isso por simplicidade. Eis o que eu teria feito de maneira diferente:

  1. Não faz lógica no construtor de um object. Em vez disso, eu teria injetado ou usado uma fábrica para pré-preencher os dados.
  2. Destaque o conceito de relacionamentos em objects de negócios separados.
  3. Separar a lógica de tração da lógica de exibição.
  4. Nomeie as coisas melhor (metavalue não é um bom nome para um campo).

Mas isso realmente depende dos requisitos exatos da aplicação. Não há nenhuma (ou duas) maneiras diretas de fazer isso. Cada uma das formas possíveis tem prós e contras. Aprenda a pesar os compromissos para que você possa escolher o caminho certo para seus requisitos …

Você perguntou como chamar esse tipo de problema: o chamado agrupamento. Uma possibilidade é, de fato, resolvê-lo em SQL com a ajuda da cláusula GROUP BY (mas diferentes mecanismos SQL têm suporte variável para estes, especialmente quando se trata de agregações mais complexas dos dados a serem agrupados).

Em C #, a solução básica é um one-liner graças ao LINQ, basta usar o método de extensão GroupBy :

 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("{0}\n", grp.Key); sb.AppendLine(" 
    "); foreach (var element in grp) { sb.AppendFormat("
  • {0}
  • \n", element.Value); } sb.AppendLine("
\n"); } Console.WriteLine(sb.ToString());

A idéia básica por trás do GroupBy é apenas um tipo de matriz ou dictionary associativo, com os atributos a serem agrupados como a chave e uma lista de todas as linhas associadas a essa chave. Por exemplo (usando os dados variables ​​com a definição acima):

 var map = new Dictionary, List>(); foreach (var row in data) { var key = Tuple.Create(row.EmpId, row.Name); if (!map.ContainsKey(key)) map.Add(key, new List()); map[key].Add(row); } 

O snippet acima é conceitualmente o mesmo que as respostas baseadas em PHP de drew010 e invisal (eles usam uma matriz de PHP bidimensional, que é conceitualmente o mesmo que o Dictionary of Lists). Eu tentei fazer as etapas o mais explícito possível e não usar muitas características especiais de C # (por exemplo, em um programa real, você deveria tender a usar uma class apropriada própria para chaves complexas em vez de uma simples Tuple), então o acima O snippet deve ser facilmente portátil para Java e outros idiomas.

Como você pode ver por todos os exemplos aqui, depois de agrupar os dados, a geração de HTML é sempre a mesma (dois laços nesteds).

Eu tinha uma tabela semelhante uma vez e acabei usando algum SQL para retornar os dados como linhas individuais. Modifiquei meus objects para usar seu sql e escrevemos uma function simples que exibirá seus dados na tabela seguindo o formato que você usou na sua pergunta.

A function agregada de consulta principal que usei é o MySQL GROUP_CONCAT. Isso é semelhante a uma function implode() . O separador padrão é uma vírgula, então, se você tem vírgulas em seus metadados, pode alterá-lo para um marcador diferente, adicionando SEPARATOR 'yourSeparator' dentro dos colchetes após o nome da coluna. Você também pode usar uma frente distinct do nome da coluna para selecionar somente linhas distintas no group_concat.

Coloquei a seção de header da tabela dentro da instrução try , de modo que, se você não tiver nenhum resultado, você não exibirá uma tabela meio gerada.

 < ?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 "  "; $dbh = new PDO($hostname, $username, $password); $stmt = $dbh->query($this->SQL); $obj = $stmt->setFetchMode(PDO::FETCH_INTO, new myUser); foreach($stmt as $myUser) { echo " "; } unset($obj); unset($stmt); unset($dbh); echo "   "; } catch(PDOException $e){ echo 'Error : '.$e->getMessage(); exit(); } } } $myPage= new myTables(); $myPage->outputTable(); ?>

Exemplo de saída:

 
Name MetaValue
".$myUser->name."
    "; $metas=explode(",", $myUser->metas); for($i=0;$i".$metas[$i].""; } echo "
Name MetaValue1
Rose
  • Drinker
  • Nice Person
  • Runner
Gary
  • Player
  • Funny

Autonum o Autonum da sua consulta, já que, de outra forma, esbarrou a function agregada. Se você faz, no entanto, precisa disso, você teria que agregá-lo de forma semelhante. A function group_concat ignorará os campos nulos, então você precisará trazê-los de forma astuta, caso contrário, suas IDs de autonum não coincidirão com seus resultados. Eu fiz isso aqui com um simples coalesce() dentro da function group_concat .

Para acomodar esses bits extras, reescrevi um pouco o object. Isso deve fazer tudo o que você precisa, deixei minhas notas de debugging em que são definidas pela variável privada isDebug que eu configurei como falso, mas configurá-la como verdadeira lhe dará informações extras à medida que o object percorrer as funções. Isso irá ajudá-lo com sua debugging, pois eu suponho que seus dados de origem são realmente muito mais complexos.

 < ?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.
";} unset($obj); unset($stmt); unset($dbh); } // Pulls the data from the database and populates the this->object. public function outputTable() { echo " "; 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
";} if($this->isDebug){echo "My first segment is ".($this->myUsers[$i]->metaDesc)."
";} echo ""; } echo "
Name MetaValue
".$this->myUsers[$i]->name."
    "; for($j=0;$jmyUsers[$i]->metaDescs);$j++) { echo "
  • ID: ".$this->myUsers[$i]->metaNums[$j]." - ".$this->myUsers[$i]->metaDescs[$j]."
  • "; } echo "
"; } // Outputs the data held in the object into the table as required. } $myPage= new myTables(); $myPage->getUserData(); $myPage->outputTable(); ?>

A saída agora se parece com isto:

 
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
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
 This would print exactly what you need within the  tags : < ?php $result = mysql_query("SELECT name, metavalue FROM table"); $previous_name = ""; //Variable to keep track of previous name value in array() print ''; //print the static table header while ($row = mysql_fetch_array($result)) { $name = $row['name']; $meta_value = $row['metavalue']; if (($previous_name != $name) && ($previous_name !="")) { //If $name has changed, close tags, reset $previous_name print ''; $previous_name = ""; } if ($previous_name == "") { //New value of $name, open tags, set $name to $previous_name print ''; //close the static table header ?>

Aqui está a minha segunda abordagem sobre o problema, como com o primeiro , ainda sou fã de resolver isso com o resultado, por exemplo:

 
NameMetavalue
'.$name.'
    '; $previous_name = $name; } if ($previous_name == $name) { //print contents of $meta_value if $name is the same print '
  • '.$meta_value.'
  • '; } } //Once there are no more values in the array, close the static table header print '
< ?php foreach($users as $name => $columns) : ?> < ?php endforeach ?>
< ?php echo htmlspecialchars($name); ?>
    < ?php foreach($columns as $column) : ?>
  • < ?php echo htmlspecialchars($column); ?>
  • < ?php endforeach ?>

Onde, como o conjunto de resultados do database é aqui simulado com uma estrutura de matriz, mas o iterador típico:

 $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'), ); $result = new ArrayIterator($rows); $users = new Users($result); 

O que demonstra que esta é uma única consulta, sem subselects, nenhum re-agrupamento necessário (conforme descrito na primeira pergunta) fazendo isso com base em seu pedido.

Somente o código da class Users está faltando:

 class Users extends IteratorIterator { private $last; public function __construct($it) { parent::__construct(new NoRewindIterator($it)); } public function current() { $current = $this->getInnerIterator()->current(); $this->last = $current[2]; return new Columns($this->getInnerIterator(), $this->last); } public function key() { return $this->last; } public function next() { $current = $this->getInnerIterator()->current(); if ($this->last === $current[2] || $current === NULL) { parent::next(); } } } class Columns extends IteratorIterator { private $name; public function __construct($it, $name) { $this->name = $name; parent::__construct($it); } public function valid() { $current = parent::current(); return parent::valid() && $current[2] === $this->name; } public function current() { $current = parent::current(); return $current[3]; } } 

Demonstração completa no teclado – devo admitir que isso contém um pouco de magia negra de iterador.

Eu recomendo que você crie uma function no database

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

e use uma declaração limpa no lado do PHP assim.

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

então você precisa fazer o

 echo $row["meta_output"]; 

como qualquer outra coluna. Isto é o que eu usaria.

Isso é bastante próximo de algumas das outras respostas, mas acho um pouco mais limpo e mais fácil de seguir.

Eu crio uma matriz tridimensional antes de qualquer saída para facilidade de uso. Você poderia fazer tudo de uma só vez, mas acho que o código de busca mais limpo tende a separá-lo (a leitura do database é diferente da saída, por exemplo, para mantê-los separados). Desta forma, você também tem uma matriz útil se você precisar referenciar o 15º metavalo da pessoa 33 da pessoa $resultArr[33]["meta"][15] .

Eu também adicionei nomes de class e uma identificação para cada linha de resultados para um estilo CSS fácil.

 < ?php // DB connect // // Get the DB Data $result = mysql_query("SELECT Autonum, ID, Name, MetaValue FROM table GROUP BY ID"); // Set Start Values $resultArr = array(); $curRow = -1; $curMeta = -1; // Loop Through the Rows while ($row = mysql_fetch_assoc($result)) { // Start of a new ID, Set the name and Create the MetaValue Array if ($curRow != $row["ID"]){ $curRow = $row["ID"]; $resultArr[$curRow]["name"] = $row["Name"]; $resultArr[$curRow]["meta"] = array(); $curMeta = 0; } // Add the MetaValue $resultArr[$curRow]["meta"][$curMeta] = $row["MetaValue"}; $curMeta++; } /* Array looks like: $resultArr[1]["name"] = "Rose"; $resultArr[1]["meta"][0] = "Drinker"; $resultArr[1]["meta"][1] = "Nice Person"; $resultArr[1]["meta"][2] = "Runner"; $resultArr[2]["name"] = "Gary"; $resultArr[2]["meta"][0] = "Player"; $resultArr[2]["meta"][1] = "Funny"; */ // Start the Table $out = ""; $out .= "\n"; $out .= "\n"; $out .= "\n"; $out .= "\n"; $out .= "\n"; // Add the Rows foreach($resultArr as $id => $col){ $out .= "\n\n"; $out .= "\n"; $out .= "\n"; $out .= "\n"; } // Close the Table $out .= "\n"; $out .= "\n"; // Print It Out Where You Need It echo $out; ?>

Aqui não estou agrupando no lado MySql ou PHP, ou seja, não usando a memory do php para agrupar ou usar a function mysql. Apenas tendo controle sobre a impressão.

Você pode ver o exemplo de trabalho do código abaixo aqui

 < ?php $result = mysql_query("SELECT name,metavalue FROM table"); $count = mysql_num_rows($result); ?> 
NameMetaValue1
" . $col["name"] . ""; $out .= "\n
    "; foreach($col["meta"] as $meta){ $out .= "\n
  • ".$meta."
  • "; } $out .= "\n
"; $out .= "\n
< ?php $i = 0; // loop through each row $start = 1; while ($row = mysql_fetch_assoc($result) { if ($name != $row['name']) {// row with new name if ($start) { $start = 0; echo ""; echo ""; echo ""; echo ""; } } ?> < ?php $name = $row['name']; $i++; } ?>
Name MetaValue1
" . $row['name'] . "
  • " . $row['metadata'] . "
  • "; } else { $start = 1; echo "
" . $row['name'] . "
  • " . $row['metadata'] . "
  • "; } } else {//row with the same name echo "
  • " . $row['metadata'] . "
  • "; if ($i == $count - 1) { echo "

Aqui está o código para você quer. Para evitar qualquer conflito com o seu código, não estou usando qualquer conceito de object de class, portanto, o código é escrito de maneira simples. Parece de forma simples, mas está funcionando sem dúvida. Observe que o código é colocado por um programador sênior trabalhando em php no último ano e meio. então não se importe com a moda simples do código


   < ?php mysql_select_db("dbName", $con); $result = mysql_query("SELECT DISTINCT Name,ID FROM tableName"); while($row = mysql_fetch_array($result)) { $userName = $row['Name']; echo $userId = $row['ID']; ?>  < ?php } ?> 
Name MetaValue1
< ?=$userName?> < ?=$userId?>
    < ?php $resultNew = mysql_query("SELECT * FROM tableName WHERE ID =$userId"); while($row = mysql_fetch_array($resultNew)) { $mValue = $row['MetaValue']; ?>
  • < ?=$mValue?>
  • < ?php } ?>
 < ?php /** * Displaying a table in PHP with repeated columns * @link http://stackoverflow.com/a/11616884/367456 */ class Events { private $row = 0; public function newRow($name) { $this->closeRow(); echo " $name
    "; $this->row++; } public function closeRow() { if (!$this->row) return; echo "
\n"; $this->row = 0; } public function newItem($name) { echo "
  • $name
  • "; } } $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 "\n \n \n \n \n \n"; $events = new Events(); $last = null; foreach ($rows as $row) { if (@$last[2] !== $row[2]) { $events->newRow($row[2]); } $events->newItem($row[3]); $last = $row; } $events->closeRow(); echo " \n
    NameMetaValue1
    ";