doupang9080 2015-05-21 13:43
浏览 64

PHP usort()不一致的结果

I have created a PHP procedure which extract some table names from the database, and then sorts them based on custom algorithm.

Here is the gist of it:

function getTables($like) {
  // executes query "SHOW TABLES LIKE '$like%'" and return array
}

function tableSort(array $tables) {
  usort($tables, "compareTables");
  return $tables;
}

function compareTables($ta, $tb) {
    global $db;

    // foreign key test
    foreach(array(
        // if table A references table B, A is "greater than" B
        array(
            'TABLE' => $ta,
            'REFERENCED' => $tb,
            'VALUE' => 100
        ),
        // if table B references table A, A is "smaller than" B
        array(
            'TABLE' => $tb,
            'REFERENCED' => $ta,
            'VALUE' => -100
        ),
    ) as $test) {
        $res = $db->query("SELECT COUNT(*)
                           FROM information_schema.REFERENTIAL_CONSTRAINTS fk
                           WHERE fk.CONSTRAINT_SCHEMA = 'my_db'
                             AND fk.TABLE_NAME = '{$test['TABLE']}'
                             AND fk.REFERENCED_TABLE_NAME = '{$test['REFERENCED']}'")->field();

        if($res > 0) {
            return $test['VALUE'];
        }
    }

    // se non vi sono chiavi esterne, applico conteggio campi ID
    return countIds($ta) - countIds($tb);
}

function countIds($table) {
    global $db;

    // extract fields
    $fields = $db->query("DESCRIBE $table");

    // count ID fields
    $count = 0;

    // Listo per il numero di tutti i campi presenti nella tabella
    while($field = $fields->fetch()) {
        if (
           $field['Field'] == 'revisione_documento'
           || substr($field['Field'], 0, 3) == "id_"
        ) {
            ++$count;

            if($field['Field'] == 'revisione_documento') {
                // stop counting now
                break;
            }
        }
    }

    return $count;
}

however, I have found a couple of cases where the final sorted array has table_x before table_y even if compareTables('table_x', 'table_y') > 0, so that I would expect those to be sorted differently.

Is it an issue with my sorting algorithm, or rather with usort() itself?

Here is an actual, albeit quite long, example:

// UNSORTED array
array (
  0 => 'lin_98_3',
  58 => 'lin_98_3_anamnesi_abitudini',
  59 => 'lin_98_3_anamnesi_allergologica',
  60 => 'lin_98_3_anamnesi_familiare',
  61 => 'lin_98_3_anamnesi_fisiologica',
  62 => 'lin_98_3_anamnesi_generale',
  63 => 'lin_98_3_anamnesi_ginecologica',
  64 => 'lin_98_3_anamnesi_handicap',
  65 => 'lin_98_3_anamnesi_lavorativa',
  66 => 'lin_98_3_anamnesi_lavorativa_lavori',
  67 => 'lin_98_3_anamnesi_lavorativa_rischi',
  68 => 'lin_98_3_anamnesi_patologica_familiare',
  69 => 'lin_98_3_anamnesi_patologica_remota',
  70 => 'lin_98_3_anamnesi_sportiva',
  71 => 'lin_98_3_anamnesi_stupefacenti',
  72 => 'lin_98_3_cartelle_cliniche',
  73 => 'lin_98_3_dipendenti',
  74 => 'lin_98_3_dipendenti_prescrizioni',
  75 => 'lin_98_3_dosp',
  76 => 'lin_98_3_esame_clinico',
  77 => 'lin_98_3_esame_clinico_capo_collo',
  78 => 'lin_98_3_esame_clinico_cardiovascolare',
  79 => 'lin_98_3_esame_clinico_cute_mucose',
  80 => 'lin_98_3_esame_clinico_digerente',
  81 => 'lin_98_3_esame_clinico_endocrino',
  82 => 'lin_98_3_esame_clinico_generale',
  83 => 'lin_98_3_esame_clinico_muscolo_scheletrico',
  84 => 'lin_98_3_esame_clinico_nervoso',
  85 => 'lin_98_3_esame_clinico_osteoarticolare',
  86 => 'lin_98_3_esame_clinico_respiratorio',
  87 => 'lin_98_3_esame_clinico_tegumentario',
  88 => 'lin_98_3_esame_clinico_uditivo',
  89 => 'lin_98_3_esame_clinico_urogenitale',
  90 => 'lin_98_3_liste',
  91 => 'lin_98_3_liste_dipendenti',
  92 => 'lin_98_3_malattie',
  93 => 'lin_98_3_malattie_assenze',
  94 => 'lin_98_3_malattie_visite',
  95 => 'lin_98_3_medici_curanti',
  96 => 'lin_98_3_protocolli',
  97 => 'lin_98_3_protocolli_agenti',
  98 => 'lin_98_3_protocolli_attrezzi',
  99 => 'lin_98_3_protocolli_dipendenti',
  100 => 'lin_98_3_protocolli_haccp_alimenti',
  101 => 'lin_98_3_protocolli_impianti',
  102 => 'lin_98_3_protocolli_macchine',
  103 => 'lin_98_3_protocolli_mansioni',
  104 => 'lin_98_3_protocolli_operazioni',
  105 => 'lin_98_3_protocolli_opere_provvisionali',
  106 => 'lin_98_3_protocolli_processi',
  107 => 'lin_98_3_protocolli_rischi',
  108 => 'lin_98_3_protocolli_vani',
  109 => 'lin_98_3_protocolli_visite',
  110 => 'lin_98_3_relazioni',
  111 => 'lin_98_3_sgs_visite',
  112 => 'lin_98_3_sgs_visite_parametri',
  113 => 'lin_98_3_sgs_visite_prescrizioni',
  114 => 'lin_98_3_studi_medici',
  115 => 'lin_98_3_vaccinazioni',
  116 => 'lin_98_3_vaccinazioni_somministrazioni',
  117 => 'lin_98_3_visite',
  118 => 'lin_98_3_visite_dipendenti',
)

// SORTED array
array (
  0 => 'lin_98_3',
  1 => 'lin_98_3_esame_clinico',
  2 => 'lin_98_3_esame_clinico_generale',
  3 => 'lin_98_3_esame_clinico_muscolo_scheletrico',
  4 => 'lin_98_3_esame_clinico_nervoso',
  5 => 'lin_98_3_esame_clinico_endocrino',
  6 => 'lin_98_3_esame_clinico_digerente',
  7 => 'lin_98_3_esame_clinico_cardiovascolare',
  8 => 'lin_98_3_esame_clinico_cute_mucose',
  9 => 'lin_98_3_esame_clinico_osteoarticolare',
  10 => 'lin_98_3_esame_clinico_respiratorio',
  11 => 'lin_98_3_protocolli',
  12 => 'lin_98_3_relazioni',
  13 => 'lin_98_3_studi_medici',
  14 => 'lin_98_3_medici_curanti',
  15 => 'lin_98_3_liste',
  16 => 'lin_98_3_esame_clinico_capo_collo',
  17 => 'lin_98_3_esame_clinico_uditivo',
  18 => 'lin_98_3_esame_clinico_urogenitale',
  19 => 'lin_98_3_visite',
  20 => 'lin_98_3_esame_clinico_tegumentario',
  21 => 'lin_98_3_anamnesi_ginecologica',
  22 => 'lin_98_3_anamnesi_generale',
  23 => 'lin_98_3_anamnesi_fisiologica',
  24 => 'lin_98_3_anamnesi_familiare',
  25 => 'lin_98_3_anamnesi_lavorativa',
  26 => 'lin_98_3_anamnesi_abitudini',
  27 => 'lin_98_3_dipendenti',
  28 => 'lin_98_3_protocolli_opere_provvisionali',
  29 => 'lin_98_3_protocolli_processi',
  30 => 'lin_98_3_visite_dipendenti',
  31 => 'lin_98_3_protocolli_macchine',
  32 => 'lin_98_3_protocolli_mansioni',
  33 => 'lin_98_3_protocolli_operazioni',
  34 => 'lin_98_3_protocolli_rischi',
  35 => 'lin_98_3_protocolli_vani',
  36 => 'lin_98_3_sgs_visite',
  37 => 'lin_98_3_sgs_visite_parametri',
  38 => 'lin_98_3_sgs_visite_prescrizioni',
  39 => 'lin_98_3_vaccinazioni',
  40 => 'lin_98_3_protocolli_visite',
  41 => 'lin_98_3_protocolli_impianti',
  42 => 'lin_98_3_anamnesi_allergologica',
  43 => 'lin_98_3_protocolli_attrezzi',
  44 => 'lin_98_3_cartelle_cliniche',
  45 => 'lin_98_3_anamnesi_patologica_familiare',
  46 => 'lin_98_3_anamnesi_lavorativa_lavori',
  47 => 'lin_98_3_anamnesi_patologica_remota',
  48 => 'lin_98_3_anamnesi_stupefacenti',
  49 => 'lin_98_3_dipendenti_prescrizioni',
  50 => 'lin_98_3_protocolli_haccp_alimenti',
  51 => 'lin_98_3_anamnesi_sportiva',
  52 => 'lin_98_3_anamnesi_handicap',
  53 => 'lin_98_3_dosp',
  54 => 'lin_98_3_malattie',
  55 => 'lin_98_3_protocolli_dipendenti',
  56 => 'lin_98_3_protocolli_agenti',
  57 => 'lin_98_3_malattie_visite',
  58 => 'lin_98_3_liste_dipendenti',
  59 => 'lin_98_3_malattie_assenze',
  60 => 'lin_98_3_vaccinazioni_somministrazioni',
  61 => 'lin_98_3_anamnesi_lavorativa_rischi',
)

however, the table lin_98_3_esame_clinico (index 1 in sorted array) should come after lin_98_3_dipendenti (index 27), since compareTables('lin_98_3_esame_clinico', 'lin_98_3_dipendenti') == 1

Finally, here is the structure for lin_98_3_esame_clinico and lin_98_3_dipendenti:

CREATE TABLE IF NOT EXISTS `lin_98_3_dipendenti` (
  `id_dipendente` int(10) unsigned NOT NULL DEFAULT '0',
  `id_azienda` int(10) unsigned NOT NULL DEFAULT '1',
  `id_sede` int(10) unsigned NOT NULL DEFAULT '1',
  `revisione_documento` int(10) unsigned NOT NULL DEFAULT '0',
  -- countIds() stops here!
  `id_sgs_medico` int(10) unsigned DEFAULT NULL,
  `sgs_medico` varchar(255) DEFAULT NULL,
  `esito` enum('Idoneo','Idoneo con prescrizioni','Idoneità parziale temporanea','Idoneità parziale permanente','Inidoneità temporanea','Inidoneità permanente','Sorveglianza sanitaria dopo cessazione esposizione') DEFAULT NULL,
  `note` text,
  `dosp_medico` varchar(255) DEFAULT NULL,
  `dosp_esito` enum('Idoneo','Idoneo con prescrizioni','Inidoneità permanente','Sorveglianza sanitaria dopo cessazione esposizione') DEFAULT NULL,
  `dosp_note` text,
  `dosp_categoria` enum('Non esposto','Categoria A','Categoria B') DEFAULT NULL,
  `flag_servizio_militare` tinyint(1) DEFAULT '0',
  `flag_lavoro_notturno` tinyint(1) DEFAULT '0',
  `attivita_extralavorative` set('Volontariato','Primo soccorso','Attività venatoria','Agricoltura/allevamento','Altro') DEFAULT NULL,
  `grado_studio` enum('Nessuno','Licenza elementare','Licenza media','Diploma superiore','Laurea') DEFAULT NULL,
  `medico_curante` varchar(255) DEFAULT NULL,
  `indirizzo_medico` varchar(255) DEFAULT NULL,
  `telefono_medico` varchar(255) DEFAULT NULL,
  `email_medico` varchar(255) DEFAULT NULL,
  `id_ultima_visita` int(10) unsigned DEFAULT NULL,
  `dosp_id_ultima_visita` int(10) unsigned DEFAULT NULL,
  `id_cartella_clinica` int(10) unsigned DEFAULT NULL,
  `flag_modifiche` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id_dipendente`,`id_azienda`,`id_sede`,`revisione_documento`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `lin_98_3_esame_clinico` (
  `id_esame_clinico` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `id_cartella_clinica` int(10) unsigned DEFAULT '0',
  `id_azienda` int(10) unsigned NOT NULL DEFAULT '1',
  `id_sede` int(10) unsigned NOT NULL DEFAULT '1',
  `revisione_documento` int(10) unsigned NOT NULL DEFAULT '0',
  -- countIds() stops here!
  PRIMARY KEY (`id_esame_clinico`,`id_azienda`,`id_sede`,`revisione_documento`),
  UNIQUE KEY `unique_id_cartella_clinica_lin_98_3_esame_clinico` (`id_cartella_clinica`,`id_azienda`,`id_sede`,`revisione_documento`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

for these two tables, countIds('lin_98_3_dipendenti') == 4 and countIds('lin_98_3_esame_clinico') == 5, and there is no foreign key relation between these two.

Also, lin_98_3_cartelle_cliniche (index 44) should be before lin_98_3_anamnesi_generale (index 22), in fact there is a foreign key in lin_98_3_anamnesi_generale that references lin_98_3_anamnesi_generale and compareTables('lin_98_3_anamnesi_generale', 'lin_98_3_cartelle_cliniche') == 100

  • 写回答

1条回答 默认 最新

  • dtewnsdf47253 2015-05-21 15:33
    关注

    Your sort function is essentially wrong at least in the aspect it does not form a proper order. There might be cycles, i.e., two tables referring to each other, in which case your function returns 100 both for (A,B) and for (B,A).

    评论

报告相同问题?

悬赏问题

  • ¥15 基于卷积神经网络的声纹识别
  • ¥15 Python中的request,如何使用ssr节点,通过代理requests网页。本人在泰国,需要用大陆ip才能玩网页游戏,合法合规。
  • ¥100 为什么这个恒流源电路不能恒流?
  • ¥15 有偿求跨组件数据流路径图
  • ¥15 写一个方法checkPerson,入参实体类Person,出参布尔值
  • ¥15 我想咨询一下路面纹理三维点云数据处理的一些问题,上传的坐标文件里是怎么对无序点进行编号的,以及xy坐标在处理的时候是进行整体模型分片处理的吗
  • ¥15 CSAPPattacklab
  • ¥15 一直显示正在等待HID—ISP
  • ¥15 Python turtle 画图
  • ¥15 stm32开发clion时遇到的编译问题