12

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

私は現在PHPで作業していますが、C#、Java、およびその他の言語を使用してこの問題に何度か遭遇しました。

現在、過去と現在の私の目標は、データを次の形式で表示することでした。

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

私は以前、SQLテーブルを表すクラスを作成することでこの問題に取り組んできました。次に、Dictionary保持EmployeeIdするクラスとクラスを作成しました。

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

手順をスキップしていますが、私の主張を理解していただければ幸いです。私はおそらく、このタイプの問題を何と呼ぶか​​というキーワードが必要です。これを達成するための好ましい方法は何ですか?

4

14 に答える 14

11

私は1つの理由でSQLソリューションが好きではありません。データベースエンジンに文字列を結合するように要求し、次にPHPを使用して文字列を分割します。それは繰り返しの仕事のようなものです。もう1つの欠点は、metavalueセパレータ文字が含まれている場合はどうなるかということです。

私のコードは、PHPで2次元配列を使用してデータを簡単にグループ化する方法を示しているだけであり、データベースからデータを取得する方法をレガシーmysql_*または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);

結果セット全体を一時的に配列に格納する必要はありません。データをグループ化し、状態を追跡するフィールドでデータを最初に並べ替えることで、簡単に繰り返すことができます。

しかし、私がこのスタイルを好む理由は2つあり、それは個人的な好みです(これは必須の解決策ではありません)。

  1. 通常、誰もがロジックとプレゼンテーションを分離したいと考えています。配列にデータを保存すると、テンプレートエンジンに簡単に渡すことができます。実装が簡単で、高速で実行されるため、PHPテンプレートエンジンを使用することをお勧めします。

  2. 私はむしろ、読みやすさと保守性のために、わずかなパフォーマンス速度とメモリ使用量を交換したいと思います。アルゴリズムは短く、理解しやすいです。パフォーマンスを最優先する場合は、ファイルへの保存、memcachedへの保存、計算結果のデータベースへの保存など、いつでもキャッシュを使用できます。

metavalue確かに、別の側からデータをグループ化していますが、にはセパレーター(コードでは|)が含まれていないと想定しています。で使用することはほとんど不可能なセパレータを選択することで簡単に修正できますmetavalue。とにかく、metavalue@ jvelezが示したように、文字|が含まれることはないため、ほとんどすべての場合にソリューションは完全に正常に機能します。

于 2012-07-17T20:44:24.680 に答える
9

問題はphp/java / c#コードではなく、SQLの終わりにあります。

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

このようにして、name値のリストを取得し、そのアイテムに関連付けられているすべてのMetaValueを取得し、を分割するだけlistです'|'

PHPexplode()では、C#では、でそれを行います。String.Split()そして、私は、どの言語でも似たようなものを見つけることができると思います。

HTMLの生成は非常に簡単になります。


PHPの例を次に示します(PDO接続APIを使用)。

$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>';
    }
}

<tbody>これにより、例からコンテンツが生成されます。

于 2012-07-17T18:36:03.643 に答える
3

あなたのデータを見てください、それはソートされ、順序付けられています:

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

したがって、実行できることは、出力によってそれを解決することです。

  • ID /名前が変更されるたびに、新しいテーブル行が作成されます。
  • MetaValueが変更されるたびに、新しいリストエントリがあります。

あなたがする必要があるのは、変化を見るために次の要素を先読みすることです。つまり、事前に反復するか(事前に計算された反復)、キャッシュされた反復を実行します(たとえばCachingIterator、PHPで)。

その後、状態も追跡する場合は、各エントリごとに次のことを言うことができます。

  • その前にテーブル行を開く必要があります。
  • その後、テーブル行を閉じる必要があります。

実際には、さらに単純です。ここでは、配列ベースの行の反復例(デモ)を示します。

$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>";

問題自体はデータ内のほんの少しの状態であるため、問題を解決するためのコードはそれほど多くありません。

ここでイベントとして説明されているものは、イテレータをサポートするテンプレートシステム(小枝や口ひげなど)に渡すことができる独自のイテレータを作成することで反転できます。その本格的な例は、私の以前の回答とブログ投稿の中に示されています。

上記の例と比較すると、それほど単純に見えないかもしれませんが、行きたい場所を決めるのに十分なコードが得られることを願っています。それはかなり拡張可能です。

于 2012-07-23T16:48:55.400 に答える
2

これが解決策です。結果をグループ化するためにテーブルのプロパティに基づいて配列を作成する方法は、@invisalと似ています。

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

これにより、希望する結果が得られるようです。お役に立てば幸いです。

于 2012-07-21T19:31:30.637 に答える
2

あなたはこのタイプの問題をどのように呼ぶかを尋ねました:それはグループ化と呼ばれます。1つの可能性は、実際にGROUP BY句を使用してSQLで解決することです(ただし、グループ化するデータのより複雑な集約に関しては、SQLエンジンごとにサポートが異なります)。

C#では、基本的なソリューションはLINQのおかげでワンライナーです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("<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());

背後にGroupByある基本的な考え方は、キーとしてグループ化する属性と、そのキーに関連付けられているすべての行のリストを備えた、ある種の連想配列または辞書です。例(上記の定義で変数データを使用):

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

上記のスニペットは、drew010およびinvisalからのPHPベースの回答と概念的に同じです(2次元のPHP配列を使用します。これは、上記のリストの辞書と概念的に同じです)。手順をできるだけ明確にし、C#の特別な機能をあまり使用しないようにしました(たとえば、実際のプログラムでは、単純なタプルではなく、複雑なキーに対して独自の適切なクラスを使用する傾向があります)。スニペットは、Javaや他の言語に簡単に移植できる必要があります。

ここにあるすべての例からわかるように、データをグループ化した後、HTMLの生成は常に同じです(2つのネストされたループ)。

于 2012-07-22T16:35:02.377 に答える
2

私はこれを別の方法で解決します。現在、データベーススキーマが正規化されていないため、問題が発生しています。代わりに、スキーマを変更することから始めます。今、あなたはこれを持っています:

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

代わりに、2つのテーブルに分割します。

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
)

これがEAVテーブルのように見えるという事実を無視すると(そうではありませんが、メタはOPの名前付けのために誤った名前が付けられています)、これがいかに簡単であるかは明らかです。それでは、あなたの記録を見て、テーブルを作りましょう:

#User
id   | name
1    | Rose
2    | Gary

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

データができたので、それをどのように抽出するかを見てみましょう。常にすべてのユーザーを印刷したいとします(制限するのは簡単ですが、わかりやすくするために、今のところすべてを実行できます)。したがって、ここで各テーブルの周りに単純なクラスを設定し、データを単一のクエリにキャッシュします。

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

これで、出力の作成は簡単になります。

$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>";

さて、そうは言っても、私は個人的に少し異なる方法で実装し、すべてにファーストクラスのビジネスオブジェクトとデータマッパーを使用します。

そして、私はここでいくつかの重罪を犯しましたが、簡単にするためにそれをしました。これが私が違ったやり方でしたであろうことです:

  1. オブジェクトのコンストラクターでロジックを実行しないでください。代わりに、それを注入するか、ファクトリを使用してデータを事前入力します。
  2. 関係の概念を個別のビジネスオブジェクトに抽象化します。
  3. プルロジックをディスプレイロジックから分離します。
  4. 物事に適切な名前を付けます(メタ値はフィールドの適切な名前ではありません)。

しかし、それは実際にはアプリケーションの正確な要件に依存します。これを行う正しい方法は1つ(または2つ)ありません。考えられる方法にはそれぞれ長所と短所があります。要件に合ったものを選択できるように、トレードオフを比較検討する方法を学びましょう...

于 2012-07-23T18:14:44.733 に答える
1

これがこの問題に対する私の2番目のアプローチです。最初のアプローチと同様に、私はまだ出力でこれを解決するのが好きです。例:

<table>
    <?php foreach($users as $name => $columns) : ?>
        <tr>
            <td>
                <?php echo htmlspecialchars($name); ?>
            </td>
            <td>
                <ul>
                    <?php foreach($columns as $column) : ?>
                        <li><?php echo htmlspecialchars($column); ?></li>
                    <?php endforeach ?>
                </ul>
            </td>
        </tr>
    <?php endforeach ?>
</table>

ここでは、データベースの結果セットが配列構造でシミュレートされていますが、一般的なイテレーターは次のとおりです。

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

これは、これが単一のクエリであり、副選択がなく、順序に基づいてこれを行うために再グループ化する必要がないことを示しています(最初の質問で概説)。

クラスのコードのみUsersが欠落しています:

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

コードパッドでの完全なデモ-これにはイテレータの黒魔術が少し含まれていることを認めなければなりません。

于 2012-07-23T19:36:08.890 に答える
1

ここでは、MySql側でもPHP側でもグループ化されていません。つまり、グループ化やmysql関数の使用にphpのメモリを使用していません。印刷を制御するだけです。

以下のコードの実際の例をここで見ることができます

<?php
$result = mysql_query("SELECT name,metavalue FROM table");

$count = mysql_num_rows($result);
?>
     <table>
        <thead>
        <th>Name</th>
        <th>MetaValue1</th>
        </thead>
        <tbody>
    <?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 "<tr>";
            echo "<td>" . $row['name'] . "</td>
                 <td><ul><li>" . $row['metadata'] . "</li>";
        } else {
            $start = 1;
            echo "</ul></td>";
            echo "</tr><tr>";
            echo "<td>" . $row['name'] . "</td>
                 <td><ul><li>" . $row['metadata'] . "</li>";
        }
    } else {//row with the same name
        echo "<li>" . $row['metadata'] . "</li>";
        if ($i == $count - 1) {
            echo "</ul></td></tr>";
        }
    }
?>


<?php
    $name = $row['name'];
    $i++;
}
?>
    </tbody>
    </table> 
于 2012-07-26T10:44:51.263 に答える
1

かつて同様のテーブルがあり、SQLを使用してデータを個々の行として返すことになりました。SQLを使用するようにオブジェクトを変更し、質問で使用した形式に従ってテーブルにデータを出力する単純な関数を作成しました。

私が使用した主なクエリ集計関数は、MySQLGROUP_CONCATです。これはimplode()関数に似ています。デフォルトの区切り文字はコンマであるため、メタデータにコンマがある場合はSEPARATOR 'yourSeparator'、列名の後に角かっこ内を追加することで、別のマーカーに変更できます。distinct列名の前を使用して、group_concat内の個別の行のみを選択することもできます。

ステートメント内にテーブルヘッダーセクションを配置したtryので、結果がない場合は、半分生成されたテーブルが表示されません。

<?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();
?>

出力例:

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

Autonum集計関数をバグしているので、クエリからを切り取りました。ただし、それが必要な場合は、同様の方法で集約する必要があります。このgroup_concat関数はnullフィールドをスキップするため、巧妙にそれらを取り込む必要があります。そうしないと、autonumIDが結果と一致しません。ここでは、関数coalesce()内の単純なものを使用してこれを行いました。group_concat

これらの余分なビットに対応するために、オブジェクトを少し書き直しました。これで必要なすべてが実行されます。falseに設定したプライベート変数によって設定されるデバッグノートを残しましたが、isDebugtrueに設定すると、オブジェクトが関数を実行するときに追加情報が得られます。ソースデータは実際にははるかに複雑であると私は思うので、これはデバッグに役立ちます。

<?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();
?>

出力は次のようになります。

<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
于 2012-07-22T05:50:30.473 に答える
1

データベースに関数を作成することをお勧めします

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>';

このようにPHP側でcleanステートメントを使用します。

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

その後、あなたはする必要があります

echo $row["meta_output"]; 

他の列と同じように。これは私が使用するものです。

于 2012-07-23T12:13:46.943 に答える
1
This would print exactly what you need within the <tbody> tags :

<?php

$result = mysql_query("SELECT name, metavalue FROM table");
$previous_name = ""; //Variable to keep track of previous name value in array()

print '<table><thead><th>Name</th><th>Metavalue</th></thead><tbody>'; //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 '</ul></td></tr>';
        $previous_name = "";
    }

    if ($previous_name == "") {
        //New value of $name, open tags, set $name to $previous_name
        print '<tr><td>'.$name.'</td><td><ul>';
        $previous_name = $name;
    } 

    if ($previous_name == $name) {
        //print contents of $meta_value if $name is the same
        print '<li>'.$meta_value.'</li>';
    }   
}
//Once there are no more values in the array, close the static table header
print '</tbody></table>'; //close the static table header

?>
于 2012-07-23T13:35:49.450 に答える
1

これは他のいくつかの応答にかなり近いですが、私は少しきれいで、従うのが簡単だと思います。

使いやすさのために、出力の前に3次元配列を作成します。すべてを一度に行うこともできますが、見た目がすっきりしたコードではコードが分離される傾向があります(たとえば、データベースからの読み取りは出力とは異なるため、分離してください)。このようにして、人物33の15番目のメタ値を参照する必要がある場合にも便利な配列があります$resultArr[33]["meta"][15]

また、CSSのスタイルを簡単にするために、各結果行にクラス名とIDを追加しました。

<?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 = "<table>";
$out .= "\n<thead>";
$out .= "\n<th>Name</th>";
$out .= "\n<th>MetaValue1</th>";
$out .= "\n</thead>";
$out .= "\n<tbody>";

// Add the Rows
foreach($resultArr as $id => $col){
    $out .= "\n\n<tr id='result_" . $id . "'>";
    $out .= "\n<td class='rowName'>" . $col["name"] . "</td>";
    $out .= "\n<td class='rowMeta'>";
    $out .= "\n<ul>";
    foreach($col["meta"] as $meta){
        $out .= "\n<li>".$meta."</li>";
    }
    $out .= "\n</ul>";
    $out .= "\n</td>";
    $out .= "\n</tr>";
}

// Close the Table
$out .= "\n</tbody>";
$out .= "\n</table>";

// Print It Out Where You Need It
echo $out;
?>
于 2012-07-24T19:30:19.233 に答える
0

これがあなたが望むコードです。コードとの競合を避けるために、私はクラスオブジェクトの概念を使用していないため、コードは単純な方法で記述されています。見た目はシンプルですが、間違いなく動作しています。コードは、過去1年半の間phpで作業しているシニアプログラマーによって配置されていることに注意してください。だから、コードの単純な方法を気にしないでください


    <table>
        <thead>
        <th>Name</th>
        <th>MetaValue1</th>
        </thead>
        <tbody>
        <?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'];

        ?>     
             <tr>      
                   <td><?=$userName?> <?=$userId?></td>
                    <td>
                       <ul>
                        <?php
                        $resultNew = mysql_query("SELECT * FROM tableName WHERE ID =$userId");
                        while($row = mysql_fetch_array($resultNew))
                        {
                            $mValue = $row['MetaValue'];
                        ?>
                        <li><?=$mValue?></li> 
                        <?php   
                        }
                        ?>
                        </ul>
                    </td>
                </tr>
          <?php } ?>      
            </tbody>

    </table>
于 2012-07-23T11:32:26.790 に答える
0
<?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 "        <tr><td>$name</td><ul>";
        $this->row++;
    }

    public function closeRow()
    {
        if (!$this->row) return;
        echo "</ul></tr>\n";
        $this->row = 0;
    }

    public function newItem($name)
    {
        echo "<li>$name</li>";
    }
}

$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 = 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>";
于 2012-07-26T03:55:10.057 に答える