Diziye veritabanı sonucu çevirin

5 Cevap php

http://www.slideshare.net/billkarwin/sql-antipatterns-strike-back: Ben sadece güncelleme / Bu Slideshare sayfa 70 gösterilir sorgu hiyerarşik veri düzenleme "Kapatma masa" şekilde kısmını eklemek / silmek yaptık

Benim veritabanı bu gibi görünüyor:

Table Categories:

ID         Name
1          Top value
2          Sub value1

Table CategoryTree:

child     parent     level
1          1         0
2          2         0  
2          1         1  

Ancak, geri tek bir sorguda bir çok boyutlu dizi olarak tam ağaç almanın bir sorun biraz var.

İşte geri almak istiyorum ne:

 array (

 'topvalue' = array (
                     'Subvalue',
                     'Subvalue2',
                     'Subvalue3)
                     );

 );

Update: Found this link, but I still have a hard time to convert it into an array: http://karwin.blogspot.com/2010/03/rendering-trees-with-closure-tables.html

Update2 : I was able to add depths to each of the categories now, if that can be of any help.

5 Cevap

Tamam, ben Zend Framework DB tablo, satır ve satır sınıfları uzatmak PHP sınıfları yazdık. Ben hiyerarşik veri modelleri hakkında bir kaç hafta içinde PHP Tek-X konuşan çünkü ben zaten bu gelişmekte oldum.

Ben bunu eğer örtülü Creative Commons lisansı altında olsun çünkü taşması Stack tüm kodumu göndermek istemiyorum. update: I Zend Framework extras incubator için kodumu taahhüt ve benim sunum Models for Hierarchical Data with SQL and PHP Slideshare yer almaktadır.

Ben pseudocode çözüm anlatacağım. I ITIS.gov indirilen test verileri gibi zoolojik taksonomi kullanıyorum. Tablo longnames,

CREATE TABLE `longnames` (
  `tsn` int(11) NOT NULL,
  `completename` varchar(164) NOT NULL,
  PRIMARY KEY (`tsn`),
  KEY `tsn` (`tsn`,`completename`)
)

Ben taksonomi hiyerarşisinde yolları için bir closure table oluşturduk:

CREATE TABLE `closure` (
  `a` int(11) NOT NULL DEFAULT '0',  -- ancestor
  `d` int(11) NOT NULL DEFAULT '0',  -- descendant
  `l` tinyint(3) unsigned NOT NULL,  -- levels between a and d
  PRIMARY KEY (`a`,`d`),
  CONSTRAINT `closure_ibfk_1` FOREIGN KEY (`a`) REFERENCES `longnames` (`tsn`),
  CONSTRAINT `closure_ibfk_2` FOREIGN KEY (`d`) REFERENCES `longnames` (`tsn`)
)

Tek bir düğüm birincil anahtar göz önüne alındığında, tüm soyuna bu şekilde alabilirsiniz:

SELECT d.*, p.a AS `_parent`
FROM longnames AS a
JOIN closure AS c ON (c.a = a.tsn)
JOIN longnames AS d ON (c.d = d.tsn)
LEFT OUTER JOIN closure AS p ON (p.d = d.tsn AND p.l = 1)
WHERE a.tsn = ? AND c.l <= ?
ORDER BY c.l;

closure AS p, her düğümün üst kimliği eklemektir katılmak.

Sorgu indeksler oldukça iyi kullanan:

+----+-------------+-------+--------+---------------+---------+---------+----------+------+-----------------------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref      | rows | Extra                       |
+----+-------------+-------+--------+---------------+---------+---------+----------+------+-----------------------------+
|  1 | SIMPLE      | a     | const  | PRIMARY,tsn   | PRIMARY | 4       | const    |    1 | Using index; Using filesort |
|  1 | SIMPLE      | c     | ref    | PRIMARY,d     | PRIMARY | 4       | const    | 5346 | Using where                 |
|  1 | SIMPLE      | d     | eq_ref | PRIMARY,tsn   | PRIMARY | 4       | itis.c.d |    1 |                             |
|  1 | SIMPLE      | p     | ref    | d             | d       | 4       | itis.c.d |    3 |                             |
+----+-------------+-------+--------+---------------+---------+---------+----------+------+-----------------------------+

Ve ben longnames ve closure yılında 4.299.883 satırlara 490.032 satırlar var ki verilen, oldukça iyi zamanlı olarak çalışır:

+--------------------+----------+
| Status             | Duration |
+--------------------+----------+
| starting           | 0.000257 |
| Opening tables     | 0.000028 |
| System lock        | 0.000009 |
| Table lock         | 0.000013 |
| init               | 0.000048 |
| optimizing         | 0.000032 |
| statistics         | 0.000142 |
| preparing          | 0.000048 |
| executing          | 0.000008 |
| Sorting result     | 0.034102 |
| Sending data       | 0.001300 |
| end                | 0.000018 |
| query end          | 0.000005 |
| freeing items      | 0.012191 |
| logging slow query | 0.000008 |
| cleaning up        | 0.000007 |
+--------------------+----------+

Şimdi işlem sonrası yukarıda SQL sorgusu sonucu, hiyerarşi (yalancı) göre alt kümelere satırları sıralama:

while ($rowData = fetch()) {
  $row = new RowObject($rowData);
  $nodes[$row["tsn"]] = $row;
  if (array_key_exists($row["_parent"], $nodes)) {
    $nodes[$row["_parent"]]->addChildRow($row);
  } else {
    $top = $row;
  }
}
return $top;

Ben de Satırlar ve Satır kümeleri için sınıfları tanımlar. Bir satır kümesi temelde satır bir dizidir. Bir Satır satır veri bir ilişkisel dizi içerir ve aynı zamanda çocuklar için bir satır kümesi içerir. Bir yaprak düğüm için çocuk Rowset boştur.

Satırlar ve satır kümeleri aynı zamanda bir düz dizi olarak ardışık kendi veri içeriğini dökümü toArrayDeep() denilen yöntemleri tanımlar.

Sonra birlikte, bu gibi bütün sistemini kullanabilirsiniz:

// Get an instance of the taxonomy table data gateway 
$tax = new Taxonomy();

// query tree starting at Rodentia (id 180130), to a depth of 2
$tree = $tax->fetchTree(180130, 2);

// dump out the array
var_export($tree->toArrayDeep());

Aşağıdaki gibi çıktı:

array (
  'tsn' => '180130',
  'completename' => 'Rodentia',
  '_parent' => '179925',
  '_children' => 
  array (
    0 => 
    array (
      'tsn' => '584569',
      'completename' => 'Hystricognatha',
      '_parent' => '180130',
      '_children' => 
      array (
        0 => 
        array (
          'tsn' => '552299',
          'completename' => 'Hystricognathi',
          '_parent' => '584569',
        ),
      ),
    ),
    1 => 
    array (
      'tsn' => '180134',
      'completename' => 'Sciuromorpha',
      '_parent' => '180130',
      '_children' => 
      array (
        0 => 
        array (
          'tsn' => '180210',
          'completename' => 'Castoridae',
          '_parent' => '180134',
        ),
        1 => 
        array (
          'tsn' => '180135',
          'completename' => 'Sciuridae',
          '_parent' => '180134',
        ),
        2 => 
        array (
          'tsn' => '180131',
          'completename' => 'Aplodontiidae',
          '_parent' => '180134',
        ),
      ),
    ),
    2 => 
    array (
      'tsn' => '573166',
      'completename' => 'Anomaluromorpha',
      '_parent' => '180130',
      '_children' => 
      array (
        0 => 
        array (
          'tsn' => '573168',
          'completename' => 'Anomaluridae',
          '_parent' => '573166',
        ),
        1 => 
        array (
          'tsn' => '573169',
          'completename' => 'Pedetidae',
          '_parent' => '573166',
        ),
      ),
    ),
    3 => 
    array (
      'tsn' => '180273',
      'completename' => 'Myomorpha',
      '_parent' => '180130',
      '_children' => 
      array (
        0 => 
        array (
          'tsn' => '180399',
          'completename' => 'Dipodidae',
          '_parent' => '180273',
        ),
        1 => 
        array (
          'tsn' => '180360',
          'completename' => 'Muridae',
          '_parent' => '180273',
        ),
        2 => 
        array (
          'tsn' => '180231',
          'completename' => 'Heteromyidae',
          '_parent' => '180273',
        ),
        3 => 
        array (
          'tsn' => '180213',
          'completename' => 'Geomyidae',
          '_parent' => '180273',
        ),
        4 => 
        array (
          'tsn' => '584940',
          'completename' => 'Myoxidae',
          '_parent' => '180273',
        ),
      ),
    ),
    4 => 
    array (
      'tsn' => '573167',
      'completename' => 'Sciuravida',
      '_parent' => '180130',
      '_children' => 
      array (
        0 => 
        array (
          'tsn' => '573170',
          'completename' => 'Ctenodactylidae',
          '_parent' => '573167',
        ),
      ),
    ),
  ),
)

Ya da gerçekten her yolun uzunluğu - derinliği hesaplanması hakkında yorum yeniden.

Sadece gerçek düğümleri (longnames Yukarıdaki örnekte) tutan tablonuza yeni bir düğüm ekledikten varsayarsak, yeni düğümün kimliği LAST_INSERT_ID() MySQL veya tarafından döndürülen Başka bir şekilde alabilirsiniz.

INSERT INTO Closure (a, d, l)
  SELECT a, LAST_INSERT_ID(), l+1 FROM Closure
  WHERE d = 5 -- the intended parent of your new node 
  UNION ALL SELECT LAST_INSERT_ID(), LAST_INSERT_ID(), 0;

Proposed Solution

Bu aşağıdaki örnek için sormak biraz daha verir, ama bunu yaparken gerçekten güzel bir yoldur ve yine bilgiler her aşamada nereden geldiğini gösterir.

Aşağıdaki tablo yapısını kullanır:

+--------+------------------+------+-----+---------+----------------+
| Field  | Type             | Null | Key | Default | Extra          |
+--------+------------------+------+-----+---------+----------------+
| id     | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| parent | int(10) unsigned | NO   |     | NULL    |                |
| name   | varchar(45)      | NO   |     | NULL    |                |
+--------+------------------+------+-----+---------+----------------+

İşte:

<?php

    // Connect to the database
    mysql_connect('localhost', 'root', '');
    mysql_select_db('test');

    echo '<pre>';

    $categories = Category::getTopCategories();
    print_r($categories);

    echo '</pre>';

class Category
{
    /**
     * The information stored in the database for each category
     */
    public $id;
    public $parent;
    public $name;

    // The child categories
    public $children;

    public function __construct()
    {
        // Get the child categories when we get this category
        $this->getChildCategories();
    }

    /**
     * Get the child categories
     * @return array
     */
    public function getChildCategories()
    {
        if ($this->children) {
            return $this->children;
        }
        return $this->children = self::getCategories("parent = {$this->id}");
    }

    ////////////////////////////////////////////////////////////////////////////

    /**
     * The top-level categories (i.e. no parent)
     * @return array
     */
    public static function getTopCategories()
    {
        return self::getCategories('parent = 0');
    }

    /**
     * Get categories from the database.
     * @param string $where Conditions for the returned rows to meet
     * @return array
     */
    public static function getCategories($where = '')
    {
        if ($where) $where = " WHERE $where";
        $result = mysql_query("SELECT * FROM categories$where");

        $categories = array();
        while ($category = mysql_fetch_object($result, 'Category'))
            $categories[] = $category;

        mysql_free_result($result);
        return $categories;
    }
}

Test Case

Benim veritabanında ben şu satırlar var:

+----+--------+-----------------+
| id | parent | name            |
+----+--------+-----------------+
|  1 |      0 | First Top       |
|  2 |      0 | Second Top      |
|  3 |      0 | Third Top       |
|  4 |      1 | First Child     |
|  5 |      1 | Second Child    |
|  6 |      2 | Third Child     |
|  7 |      2 | Fourth Child    |
|  8 |      4 | First Subchild  |
|  9 |      4 | Second Subchild |
+----+--------+-----------------+

Ve böylece komut aşağıdaki (uzun) bilgi verir:

Array
(
    [0] => Category Object
        (
            [id] => 1
            [parent] => 0
            [name] => First Top
            [children] => Array
                (
                    [0] => Category Object
                        (
                            [id] => 4
                            [parent] => 1
                            [name] => First Child
                            [children] => Array
                                (
                                    [0] => Category Object
                                        (
                                            [id] => 8
                                            [parent] => 4
                                            [name] => First Subchild
                                            [children] => Array
                                                (
                                                )

                                        )

                                    [1] => Category Object
                                        (
                                            [id] => 9
                                            [parent] => 4
                                            [name] => Second Subchild
                                            [children] => Array
                                                (
                                                )

                                        )

                                )

                        )

                    [1] => Category Object
                        (
                            [id] => 5
                            [parent] => 1
                            [name] => Second Child
                            [children] => Array
                                (
                                )

                        )

                )

        )

    [1] => Category Object
        (
            [id] => 2
            [parent] => 0
            [name] => Second Top
            [children] => Array
                (
                    [0] => Category Object
                        (
                            [id] => 6
                            [parent] => 2
                            [name] => Third Child
                            [children] => Array
                                (
                                )

                        )

                    [1] => Category Object
                        (
                            [id] => 7
                            [parent] => 2
                            [name] => Fourth Child
                            [children] => Array
                                (
                                )

                        )

                )

        )

    [2] => Category Object
        (
            [id] => 3
            [parent] => 0
            [name] => Third Top
            [children] => Array
                (
                )

        )

)

Example Usage

Ben veri menüleri oluşturmak için gidiyoruz eğer özyinelemeli fonksiyonun bir tür oluşturma öneririm:

function outputCategories($categories, $startingLevel = 0)
{
    $indent = str_repeat("    ", $startingLevel);

    foreach ($categories as $category)
    {
        echo "$indent{$category->name}\n";
        if (count($category->children) > 0)
            outputCategories($category->children, $startingLevel+1);
    }
}

$categories = Category::getTopCategories();
outputCategories($categories);

hangi çıkış şu olacaktır:

First Top
    First Child
        First Subchild
        Second Subchild
    Second Child
Second Top
    Third Child
    Fourth Child
Third Top

Enjoy

Ben Icio gelen cevabı sevdi, ama ben daha ziyade nesnelerin diziler daha diziler diziler olmasını tercih. İşte nesneleri yapmadan çalışmak için değiştirilmiş onun script:

<?php

require_once('mysql.php');

echo '<pre>';

$categories = Taxonomy::getTopCategories();
print_r($categories);

echo '</pre>';

class Taxonomy
{ 


public static function getTopCategories()
{
    return self::getCategories('parent_taxonomycode_id = 0');
}

public static function getCategories($where = '')
{
    if ($where) $where = " WHERE $where";
    $result = mysql_query("SELECT * FROM taxonomycode $where");

    $categories = array();
   // while ($category = mysql_fetch_object($result, 'Category'))
    while ($category = mysql_fetch_array($result)){
    $my_id = $category['id'];
    $category['children'] = Taxonomy::getCategories("parent_taxonomycode_id = $my_id");
            $categories[] = $category;
        }

    mysql_free_result($result);
    return $categories;
  }
 }

Ben adil benim cevap ve icios hem doğrudan sorunuzu unutmayınız düşünüyorum. Her ikisi de ana tabloda bir üst kimliği bağlantı sahip güveniyor, ve kapatma tablonun hiçbir faydalanmak. Ancak, ardışık veritabanı sorgulama kesinlikle yapmak için bir yoldur, ancak bunun yerine ardışık ebeveyn kimliği geçme, üst kimliği VE böylece derinlik seviyesine (her hangi özyinelemenin birer artmalıdır) geçmek zorunda sorgular Her düzeyde yerine ana tabloda o sahip daha kapatma tablodan doğrudan üst bilgi almak için ana + derinlik kullanabilirsiniz.

HTH, -FT

Üzgünüm ama ben size (ya da herhangi) bir veritabanı sorgu dışında bir çok-boyutlu bir dizi alınamıyor sanmıyorum.

Eğer bir Sırasız liste olarak çıktı istediğinizde aşağıdaki gibi (dizide ftrotters diziler dayalı) outputCategories yöntemini değiştirebilirsiniz:

public function outputCategories($categories, $startingLevel = 0)
{
    echo "<ul>\n";
    foreach ($categories as $key => $category)
    {
        if (count($category['children']) > 0)
        {
            echo "<li>{$category['name']}\n";
            $this->outputCategories($category['children'], $startingLevel+1);
            echo "</li>\n";
        }
        else
        {
            echo "<li>{$category['name']}</li>\n";
        }
    }
    echo "</ul>\n";
}