Skip to content

8. 练习 IMPOTS 配合 MySQL

我们已经编写了该练习的三个版本。最新版本使用了一个税款计算类。该类从文本文件中读取用于计算的数据。现在,它将从数据库中读取数据。为此,我们编写了第一段代码,用于将文本文件中的数据导入数据库。

文本文件 impots.txt 内容如下:

12620:13190:15640:24740:31810:39970:48360:55790:92970:127860:151250:172040:195000:0
0:0.05:0.1:0.15:0.2:0.25:0.3:0.35:0.4:0.45:0.5:0.55:0.6:0.65
0:631:1290.5:272.5:3309.5:4900:6898.5:9316.5:12106:16754.5:23147.5:30710:39312:49062

需要构建的数据库如下:

该数据库名为 [dbimpots],包含一个名为 [impots] 的表。该数据库由用户 [root] 操作,无需密码。

8.1. 将文本文件导入表 MySQL(txt2mysql)


<?php

// 传输用于计算税款所需的文本数据文件
// 导入到 MySQL 表中
// 数据
$IMPOTS = "impots.txt";
$BASE = "dbimpots";
$TABLE = "impots";
$USER = "root";
$PWD = "";
$HOTE = "localhost";

// 用于计算税款所需的数据已放入文件 $IMPOTS
// 每张表格占一行,格式为
// val1:val2:val3,...
list($erreur, $limites, $coeffR, $coeffN) = getTables($IMPOTS);
// 是否出现错误?
if ($erreur) {
  print "$erreur\n";
  exit;
}//if
// 将这些数组导入到 MySQL 表中
$erreur = copyToMysql($limites, $coeffR, $coeffN, $HOTE, $USER, $PWD, $BASE, $TABLE);
if ($erreur)
  print "$erreur\n";
else
  print "Transfert opéré\n";
// 结束
exit;

// --------------------------------------------------------------------------
function copyToMysql($limites, $coeffR, $coeffN, $HOTE, $USER, $PWD, $BASE, $TABLE) {
  // 将 3 个数字表 $limites、$coeffR、$coeffN
  // 到 mysql 数据库 $BASE 中的表 $TABLE
  // 该 MySQL 数据库位于 $HOTE 机器上
  // 用户由 $USER 和 $PWD 标识
  // 连接
  list($erreur, $connexion) = connecte("mysql:host=$HOTE;dbname=$BASE", $USER, $PWD);
  if ($erreur)
    return "Erreur lors de la connexion à MySql sous l'identité ($HOTE,$USER,$PWD) : $erreur\n";
  // 删除表
  $requête = "drop table $TABLE";
  exécuteRequête($connexion, $requête);
  // 创建表
  $requête = "create table $TABLE (limites decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2))";
  list($erreur, $res) = exécuteRequête($connexion, $requête);
  if ($erreur)
    return "$requête : erreur ($erreur)";
  // 填充
  for ($i = 0; $i < count($limites); $i++) {
    // 插入查询
    $requête = "insert into $TABLE (limites,coeffR,coeffN) values ($limites[$i],$coeffR[$i],$coeffN[$i])";
    // 执行查询
    list($erreur, $res) = exécuteRequête($connexion, $requête);
    // 若出错则返回
    if ($erreur)
      return "$requête : erreur ($erreur)";
  }//for
  // 完成 - 断开连接
  déconnecte($connexion);
  // 无错误返回
  return "";
}
// --------------------------------------------------------------------------
function getTables($IMPOTS) {
  // $IMPOTS: 包含表 $limites、$coeffR、$coeffN 数据的文件名
  ...
  // 结束
  return array("", $limites, $coeffR, $coeffN);
}
// --------------------------------------------------------------------------
function cutNewLinechar($ligne) {
  // 如果存在,则删除 $ligne 的行尾标记
  ...
  // 结束
  return($ligne);
}
// ---------------------------------------------------------------------------------
function connecte($dsn, $login, $pwd) {
  // 将 ($login,$pwd) 连接到数据库 $dsn
  // 返回连接ID及错误信息
  ...
}

//建立连接
// ---------------------------------------------------------------------------------
function déconnecte($connexion) {
  // 关闭由 $connexion 标识的连接
  ...
}

// ---------------------------------------------------------------------------------
function exécuteRequête($connexion, $sql) {
  // 在连接 $connexion 上执行查询 $sql
  // 返回一个包含 2 个元素的数组($erreur,$résultat)
 ...
}

屏幕结果

Transfert opéré

8.2. 税款计算程序( impots_04)

此版本使用类 Impôts,该类会从数据库中检索计算税款所需的值。在此我们引入一个新概念,即预编译查询。 由 SGBD 执行 SQL 命令分为两个阶段:

  1. 准备查询:SGBD 将为该查询准备一个优化的执行计划。目的是以最高效的方式执行该查询。
  1. 执行查询。

如果同一条查询被执行 N 次,则上述两个步骤将重复 N 次。不过,也可以只预备查询一次,然后执行 N 次。 为此需要使用预编译查询。若 $requête 是待执行的 SQL 命令,$connexion 是代表连接的 PDO 对象:

  • $statement=$connexion->prepare($requête) 准备一个查询并返回“已准备”的查询
  • $statement->execute() 执行该预备好的查询。

如果预编译的查询是一个 select 命令,那么

  • $statement->fetchAll() select 表中的所有行以数组 T 的形式返回,其中 T[i,j] 是该表中第 ij 列的值
  • $statement->fetch() 返回表中当前行,并将其转换为一个数组 T,其中 T[j] 是第 i 行中 j 列的值

预编译查询除了提高效率外,还具有其他优势。特别是它能带来更高的安全性。因此,应系统地使用它。


<?php

// 测试 -----------------------------------------------------
// 常量定义
$DATA = "data.txt";
$RESULTATS = "resultats.txt";
$TABLE = "impots";
$BASE = "dbimpots";
$USER = "root";
$PWD = "";
$HOTE="localhost";

// 计算税款所需的数据已放入表 mysqL $TABLE
// 该表属于数据库 $BASE。该表的结构如下
// 范围 decimal(10,2),coeffR decimal(6,2),coeffN decimal(10,2)
// 纳税人参数(婚姻状况、子女数量、年薪)
// 已按每位纳税人一行放入文本文件 $DATA
// 结果(婚姻状况、子女数量、年薪、应缴税额)被放入
// 结果(婚姻状况、子女数量、年薪、应缴税款)被放入文本文件 $RESULTATS,每行一个结果

// 创建一个“税款”对象
$I = new Impôts($HOTE, $USER, $PWD, $BASE, $TABLE);
// 是否发生错误?
$erreur=$I->getErreur();
if ($erreur) {
  print "$I->erreur\n";
  exit;
}//if
// 创建一个实用程序对象
$u = new Utilitaires();

// 打开纳税人数据文件
$data = fopen($DATA, "r");
if (!$data) {
  print "Impossible d'ouvrir en lecture le fichier des données [$DATA]\n";
  exit;
}

// 打开结果文件
$résultats = fopen($RESULTATS, "w");
if (!$résultats) {
  print "Impossible de créer le fichier des résultats [$RESULTATS]\n";
  exit;
}

// 处理纳税人数据文件的当前行
while ($ligne = fgets($data, 100)) {
  // 移除可能存在的行尾标记
  $ligne = $u->cutNewLineChar($ligne);
  // 提取构成$ligne的3个字段:已婚、子女、工资
  list($marié, $enfants, $salaire) = explode(",", $ligne);
  // 计算税额
  $impôt = $I->calculer($marié, $enfants, $salaire);
  // 写入计算结果
  fputs($résultats, "$marié:$enfants:$salaire:$impôt\n");
  // 下一条数据
}
// 关闭文件
fclose($data);
fclose($résultats);

// 结束
exit;

// ---------------------------------------------------------------------------------
// 定义一个“税款”类
class Impôts {

  // 属性:3个数据表
  private $limites;
  private $coeffR;
  private $coeffN;
  private $erreur;
  private $nbLimites;

  // 获取器
  public function getErreur(){
    return $this->erreur;
  }
  // 构造函数

  function __construct($HOTE, $USER, $PWD, $BASE, $TABLE) {
    // 初始化属性 $limites、$coeffR、$coeffN
    // 计算税款所需的数据已放入 MySQL 表 $TABLE
    // 该表属于数据库 $BASE。该表的结构如下
    // 范围 decimal(10,2),coeffR decimal(6,2),coeffN decimal(10,2)
    // 通过 ($USER,$PWD) 身份连接到 $HOTE 机器上的 MySQL 数据库
    // 初始化字段 $erreur,并记录可能出现的错误
    // 若无错误则清空
    // 
    // 连接到 mysql 数据库
    $DSN = "mysql:host=$HOTE;dbname=$BASE";
    list($erreur, $connexion) = connecte($DSN, $USER, $PWD);
    if ($erreur) {
      $this->erreur = "Erreur lors de la connexion à MySql sous l'identité ($HOTE,$USER,$PWD) : $erreur\n";
      return;
    }
    // 读取表 $TABLE
    $requête = "select limites,coeffR,coeffN from $TABLE";
    // 在连接 $connexion 上执行查询 $requête
    try {
      $statement = $connexion->prepare($requête);
      $statement->execute();
      // 处理查询结果
      while ($colonnes = $statement->fetch()) {
        $this->limites[] = $colonnes[0];
        $this->coeffR[] = $colonnes[1];
        $this->coeffN[] = $colonnes[2];
      }
      // 无错误
      $this->erreur = "";
      // 数组元素数量超出限制
      $this->nbLimites = count($this->limites);
    } catch (PDOException $e) {
      $this->erreur = $e->getMessage();
    }
     // 断开连接
    déconnecte($connexion);
  }
  // --------------------------------------------------------------------------
  function calculer($marié, $enfants, $salaire) {
    // $marié:是,否
    // $enfants:子女数量
    // $salaire:年薪
    
  // 该对象状态是否正常?
    if ($this->erreur)
      return -1;

    // 份额数
    ...
  }

}

// ---------------------------------------------------------------------------------
// 一个实用函数类
class Utilitaires {

  function cutNewLinechar($ligne) {
    // 如果存在,则删除 $ligne 的行尾标记
    ...
  }

}
// ---------------------------------------------------------------------------------
function connecte($dsn, $login, $pwd) {
  // 将 ($login,$pwd) 连接到数据库 $dsn
  // 返回连接ID及错误信息
  ...
}

//建立连接
// ---------------------------------------------------------------------------------
function déconnecte($connexion) {
  // 关闭由 $connexion 标识的连接
  ...
}

结果:与前几个版本相同。

注释

新增内容见第 98-109 行:

  • 第99行:命令SQL select,用于提取计算税款所需的数据。
  • 第102行:准备命令 SQL select
  • 第103行:执行已准备好的命令
  • 第 105-109 行:逐行处理 select 的结果表