PHP / MySQL - анализ общих наборов для нескольких наборов - PullRequest
0 голосов
/ 23 декабря 2011

Допустим, у меня есть две таблицы, people и families.

families имеет два поля - id и name.Поле name содержит фамилию семьи.

people имеет три поля - id, family_id и name - family_id - это идентификатор семьи, к которой принадлежит этот человек,Поле name - это имя этого человека.

В основном это отношения один ко многим, когда в одной семье много людей.

Я хочу получить списки наборов имен, упорядоченные понаибольшая встречаемость наибольшего набора имен в семьях.

Это, вероятно, не имеет особого смысла ...

Чтобы объяснить, чего я хочу дальше, мы можем оценить каждый набор имен.«Score» - это размер массива * количество вхождений в семействах.

Например, скажем, два имени, «John» и «Jane», существовали в трех семействах. «Score» этого набора будет 2* 3 = 6.

Как я могу получить массив наборов имен и «счет» набора, упорядоченный по счету каждого набора?

Образец набора результатов (я положил егов макете таблицы, но это может быть многомерный массив в PHP) - Обратите внимание, что это просто случайно придумано и не отражает статистические данные имен.

names              | occurrences | score
Ben, Lucy          | 4           | 8
Jane, John         | 3           | 6
James, Rosie, Jack | 2           | 6
Charlie, Jane      | 2           | 4

Просто чтобы уточнить, я 'Меня не интересуют наборы, где:

  • Количество вхождений равно 1 (очевидно, только одно семейство).
  • Размер набора равен 1 (просто общее имя).

Я надеюсь, что объяснил свою довольно сложную проблему - если кому-то понадобится разъяснение, скажите.

Ответы [ 2 ]

1 голос
/ 23 декабря 2011

ОК, понял:

<?php
require_once('query.lib.php');

$db=new database(DB_TYPE,DB_HOST,DB_USER,DB_PASS,DB_MISC);
$qry=new query('set names utf8',$db);

//Base query, this filters out names that are in just one family
$sql='select name, cast(group_concat(family order by family) as char) as famlist, count(*) as num from people group by name having num>0 order by num desc';
$qry=new query($sql,$db);

//$qry->result is something like 
/*
Array
(
    [name] => Array
        (
            [0] => cathy
            [1] => george
            [2] => jack
            [3] => john
            [4] => jane
            [5] => winston
            [6] => peter
        )

    [famlist] => Array
        (
            [0] => 2,4,5,6,8
            [1] => 2,3,4,5,8
            [2] => 1,3,5,7,8
            [3] => 1,2,3,6,7
            [4] => 2,4,7,8
            [5] => 1,2,6,8
            [6] => 1,3,6
        )

    [num] => Array
        (
            [0] => 5
            [1] => 5
            [2] => 5
            [3] => 5
            [4] => 4
            [5] => 4
            [6] => 3
        )

)

$qry->rows=7
*/

//Initialize
$names=$qry->result['name'];
$rows=$qry->rows;
$lists=array();
for ($i=0;$i<$rows;$i++) $lists[$i]=explode(',',$qry->result['famlist'][$i]);

//Walk the list and populate pairs - this filters out pairs, that are specific to only one family
$tuples=array();
for ($i=0;$i<$rows;$i++) {
  for ($j=$i+1;$j<$rows;$j++) {
    $isec=array_intersect($lists[$i],$lists[$j]);
    if (sizeof($isec)>1) {
      //Every tuple consists of the name-list, the family list, the length and the latest used name 
      $tuples[]=array($names[$i].'/'.$names[$j],$isec,2,$j);
    }
  }
}

//Now walk the tuples again rolling forward, until there is nothing left to do
//We do not use a for loop just for style
$i=0;
while ($i<sizeof($tuples)) {
  $tuple=$tuples[$i];
  //Try to combine this tuple with all later names
  for ($j=$tuple[3]+1;$j<$rows;$j++) {
    $isec=array_intersect($tuple[1],$lists[$j]);
    if (sizeof($isec)>0) $tuples[]=array($tuple[0].'/'.$names[$j],$isec,$tuple[2]+1,$j);
  }
  $i++;
}

//We have all the tuples, now we just need to extract the info and prepare to sort - some dirty trick here!
$final=array();
while (sizeof($tuples)>0) {
  $tuple=array_pop($tuples);
  //name list is in $tuple[0]
  $list=$tuple[0];
  //count is sizeof($tuple[1])
  $count=sizeof($tuple[1]);
  //length is in $tuple[2]
  $final[]=$tuple[2]*$count."\t$count\t$list";
}

//Sorting and output is all that is left
rsort($final);
print_r($final);
?>

Извините, я только что понял, что использую библиотеку запросов, которую я не могу найти здесь, но из комментария вы легко сможете создать массивыкак в разделе «Инициализация».

По сути, я начинаю с пар. Я сохраняю массив семейств, к которым принадлежат все имена в текущем списке имен, а затем пересекаю его со всеми еще не опробованнымиимена.

0 голосов
/ 23 декабря 2011

Будет ли это работать?

SELECT
    f.name AS 'surname',
    GROUP_CONCAT(DISTINCT p.name ORDER BY p.name) AS 'names',
    COUNT(DISTINCT p.name) AS 'distinct_names',
    COUNT(p.id) AS 'occurrences',
    COUNT(DISTINCT p.name) * COUNT(p.id) AS 'score'
FROM
    families f
    LEFT JOIN people p ON ( f.id = p.family_id )
GROUP BY
    f.id
ORDER BY
    f.name
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...