从表创建嵌套数组的最佳方法:多个查询/循环VS单个查询/循环样式

时间:2015-05-14 23:28:26

标签: php mysql sql relational-database server-side

说我有2张桌子, 我可以“合并”并在一个嵌套数组中表示。

我正在徘徊这是最好的方法,考虑:

  • 效率
  • 最佳实践
  • 数据库/服务器端使用权折扣
  • 你应该在现实生活中做些什么
  • 同样的情况,可以“合并”那样的3,4或更多表

问题是关于任何服务器端/关系数据库。

我想到的两种简单方法 (如果你有其他人,请建议! 注意我要求一个简单的SERVER-SIDE和RELATIONAL-DB , 所以请不要浪费你的时间解释为什么我不应该 使用这种DB,使用MVC设计等等......):

  1. 2个循环,5个简单的“SELECT”查询
  2. 1次循环,1次“加入”查询
  3. 我试图给出一个简单而详细的例子, 为了解释自己和更好地理解你的答案 (虽然如何编写代码和/或 找到可能的错误不是问题, 所以尽量不要专注于......)

    用于创建和插入数据到表格的SQL脚本

    CREATE TABLE persons
    (
        id int NOT NULL AUTO_INCREMENT,
        fullName varchar(255),
        PRIMARY KEY (id)
    );
    
    INSERT INTO persons (fullName) VALUES ('Alice'), ('Bob'), ('Carl'), ('Dan');
    
    CREATE TABLE phoneNumbers
    (
        id int NOT NULL AUTO_INCREMENT,
        personId int,
        phoneNumber varchar(255),
        PRIMARY KEY (id)
    );
    
    INSERT INTO phoneNumbers (personId, phoneNumber) VALUES ( 1, '123-456'), ( 1, '234-567'), (1, '345-678'), (2, '456-789'), (2, '567-890'), (3, '678-901'), (4, '789-012');  
    

    我“合并”之后表格的JSON表示:

    [
      {
        "id": 1,
        "fullName": "Alice",
        "phoneNumbers": [
          "123-456",
          "234-567",
          "345-678"
        ]
      },
      {
        "id": 2,
        "fullName": "Bob",
        "phoneNumbers": [
          "456-789",
          "567-890"
        ]
      },
      {
        "id": 3,
        "fullName": "Carl",
        "phoneNumbers": [
          "678-901"
        ]
      },
      {
        "id": 4,
        "fullName": "Dan",
        "phoneNumbers": [
          "789-012"
        ]
      }
    ]
    
    <2> 2条PSEUDO代码:

    1

    query: "SELECT id, fullName FROM persons"
    personList = new List<Person>()
    foreach row x in query result:
        current = new Person(x.fullName)
        "SELECT phoneNumber FROM phoneNumbers WHERE personId = x.id"
        foreach row y in query result:
            current.phoneNumbers.Push(y.phoneNumber)
        personList.Push(current)        
    print personList         
    

    2

    query: "SELECT persons.id, fullName, phoneNumber FROM persons
                LEFT JOIN phoneNumbers ON persons.id = phoneNumbers.personId"
    personList = new List<Person>()
    current = null
    previouseId = null
    foreach row x in query result:
        if ( x.id !=  previouseId )
            if ( current != null )
                personList.Push(current)
                current = null
            current = new Person(x.fullName)
        current.phoneNumbers.Push(x.phoneNumber)
    print personList            
    

    PHP / MYSQL中的代码实现:

    1。     

    /* get all persons */
    $result = mysql_query("SELECT id, fullName FROM persons"); 
    $personsArray = array(); //Create an array
    //loop all persons
    while ($row = mysql_fetch_assoc($result))
    {
        //add new person
        $current = array();
        $current['id'] = $row['id'];
        $current['fullName'] = $row['fullName'];
    
        /* add all person phone-numbers */
        $id = $current['id'];
        $sub_result = mysql_query("SELECT phoneNumber FROM phoneNumbers WHERE personId = {$id}");
        $phoneNumbers = array();
        while ($sub_row = mysql_fetch_assoc($sub_result))
        {
            $phoneNumbers[] = $sub_row['phoneNumber']);
        }
        //add phoneNumbers array to person
        $current['phoneNumbers'] = $phoneNumbers;
    
        //add person to final result array
        $personsArray[] = $current;
    }
    
    echo json_encode($personsArray);
    

    2。     

    /* get all persons and their phone-numbers in a single query */
    $sql = "SELECT persons.id, fullName, phoneNumber FROM persons
                LEFT JOIN phoneNumbers ON persons.id = phoneNumbers.personId";
    $result = mysql_query($sql); 
    
    $personsArray = array();
    /* init temp vars to save current person's data */
    $current = null;
    $previouseId = null;
    $phoneNumbers = array();
    while ($row = mysql_fetch_assoc($result))
    {
        /*
           if the current id is different from the previous id:
           you've got to a new person.
           save the previous person (if such exists),
           and create a new one
        */
        if ($row['id'] != $previouseId )
        {
            // in the first iteration,
            // current (previous person) is null,
            // don't add it
            if ( !is_null($current) )
            {
                $current['phoneNumbers'] = $phoneNumbers;
                $personsArray[] = $current;
                $current = null;
                $previouseId = null;
                $phoneNumbers = array();
            }
    
            // create a new person
            $current = array();
            $current['id'] = $row['id'];
            $current['fullName'] = $row['fullName'];
            // set current as previous id
            $previouseId = $current['id'];
        }
    
        // you always add the phone-number 
        // to the current phone-number list
        $phoneNumbers[] = $row['phoneNumber'];
        }
    }
    
    // don't forget to add the last person (saved in "current")
    if (!is_null($current))
        $personsArray[] = $current);
    
    echo json_encode($personsArray);
    

    P.S。 此链接是此处不同问题的示例,我尝试建议第二种方式:tables to single json

2 个答案:

答案 0 :(得分:0)

通常,最佳做法是尽可能少地访问数据库,然后将数据映射到适当的对象中。 (选项2)

但是,为了回答你的问题,我会问自己,你的数据的用例是什么。如果你确定你需要你的人和你的电话号码数据,那么我会说第二种方法是你最好的选择。

但是,当联接数据是可选的时,选项一也可以具有其用例。例如,在UI上,您有一张包含所有人的表格以及用户是否想要查看电话号码特别的人,他们必须点击那个人。那么&#34; lazy-load&#34;是可以接受的。所有的电话号码。

答案 1 :(得分:0)

这是常见问题,尤其是在创建WebAPI时,将这些表集转换为嵌套数组是一件大事。

我总是为你找第二个选项(虽然方法略有不同),因为第一个选择是最糟糕的方式...我从经验中学到的一件事就是从不在循环中查询,这是浪费DB调用,你知道我想说什么。

虽然我不接受PerformanceDBA所说的所有事情,但我需要两个主要的地址, 1.没有重复的数据 2.仅获取您想要的数据

我在加入表格时遇到的唯一问题是,我们最终复制了大量的数据,以数据为例,加入人员和电话号码表我们最终复制每个人的每个电话号码,两个表几百行它的罚款,想象我们需要合并5个表格,数千行,其巨大... 所以这是我的解决方案:
查询:

SELECT id, fullName From Person;
SELECT personId, phoneNumber FROM phoneNumbers 
WHERE personId IN (SELECT id From Person);

所以我在结果集中找到表,现在我将Table [0]分配给我的Person列表, 并使用2个循环将右侧phoneNumbers放入合适的人...

代码:

personList = ConvertToEntity<List<Person>>(dataset.Table[0]);    
pnoList = ConvertToEntity<List<PhoneNumber>>(dataset.Table[1]);

    foreach (person in personList) {
        foreach (pno in pnoList) {
            if(pno.PersonId = person.Id)
                person.PhoneNumer.Add(pno)
        }
    }

我认为上面的方法减少了很多重复,只能得到我想要的东西,如果上述方法有任何不足之处请告诉我...并且感谢您提出这些问题......