如何管理SQL查询

时间:2008-09-01 11:38:37

标签: php sql mysql

目前我的代码(PHP)中包含太多SQL查询。例如...

// not a real example, but you get the idea...
$results = $db->GetResults("SELECT * FROM sometable WHERE iUser=$userid");
if ($results) {
    // Do something
}

我正在研究使用存储过程来减少这种情况并使事情变得更加健壮,但我有一些担忧..

我在网站上使用了数百个不同的查询,其中很多都非常相似。当从上下文中删除所有这些查询(使用结果的代码)并将其放在数据库的存储过程中时,我应该如何管理这些查询?

10 个答案:

答案 0 :(得分:29)

答案 1 :(得分:4)

首先,您应该在查询中使用占位符,而不是直接插入变量。 PDO / MySQLi允许您编写如下查询:

SELECT * FROM sometable WHERE iUser = ?

API会安全地将值替换为查询。

我也更喜欢在代码而不是数据库中查询。当查询与您的代码一起使用时,使用RCS要容易得多。

使用ORM时我有一条经验法则:如果我一次只使用一个实体,我将使用该界面。如果我正在汇总报告/处理记录,我通常会编写SQL查询来执行此操作。这意味着我的代码中的查询非常少。

答案 2 :(得分:3)

我不得不清理一个项目,其中有许多(重复/类似)查询充斥着注入漏洞。 我采取的第一步是使用占位符,并使用创建查询的对象/方法和源代码行标记每个查询。 (将PHP常量 METHOD LINE 插入SQL注释行)

它看起来像这样:

  

- @Line:151 UserClass :: getuser():

SELECT * FROM USERS;

短时间记录所有查询为我提供了一些要合并查询的起点。 (还有哪里!)

答案 3 :(得分:3)

我将所有SQL移动到一个单独的Perl模块(.pm)许多查询可以重用相同的函数,但参数略有不同。

开发人员的一个常见错误是深入了解ORM库,参数化查询和存储过程。然后我们连续几个月工作以使代码“更好”,但它在开发方式中只是“更好”。你没有做任何新功能!

仅在代码中使用复杂性来满足客户需求。

答案 4 :(得分:2)

使用ORM包,任何一半体面的包都允许你

  1. 获取简单的结果集
  2. 使复杂的SQL保持接近数据模型
  3. 如果你有非常复杂的SQL,那么视图也很适合让它更适合应用程序的不同层。

答案 5 :(得分:2)

我们曾经处于类似的困境中。我们以超过50多种方式查询了一个特定的表格。

我们最终做的是创建一个包含WhereClause参数值的Fetch存储过程。 WhereClause是在Provider对象中构建的,我们采用了Facade设计模式,我们可以擦除它用于任何SQL注入攻击。

因此,就维护而言,它很容易修改。 SQL Server也是 chum ,并且缓存了动态查询的执行计划,因此整体性能非常好。

您必须根据自己的系统和需求确定性能缺点,但总而言之,这对我们来说非常好。

答案 6 :(得分:1)

有一些库,例如PEAR中的MDB2,使查询更容易和更安全。

不幸的是,设置它们可能有点罗嗦,有时您必须将相同的信息传递两次。我在几个项目中使用过MDB2,我倾向于在它周围写一个薄的贴面,特别是用于指定字段的类型。我通常会创建一个知道特定表及其列的对象,然后在调用MDB2查询函数时为我填充字段类型的辅助函数。

例如:

function MakeTableTypes($TableName, $FieldNames)
{
    $Types = array();

    foreach ($FieldNames as $FieldName => $FieldValue)
    {
        $Types[] = $this->Tables[$TableName]['schema'][$FieldName]['type'];
    }

    return $Types;
}

显然,这个对象有一个表名的映射 - >它知道的模式,只提取您指定的字段的类型,并返回适合与MDB2查询一起使用的匹配类型数组。

然后,MDB2(和类似的库)为你处理参数替换,所以对于更新/插入查询,你只需要从列名到值构建一个哈希/映射,并使用'autoExecute'函数来构建和执行相关的查询。

例如:

function UpdateArticle($Article)
{
    $Types = $this->MakeTableTypes($table_name, $Article);

    $res = $this->MDB2->extended->autoExecute($table_name,
        $Article,
        MDB2_AUTOQUERY_UPDATE,
        'id = '.$this->MDB2->quote($Article['id'], 'integer'),
        $Types);
}

并且MDB2将构建查询,正确地转义所有内容等。

我建议使用MDB2测量性能,因为如果您没有运行PHP加速器,它会引入一些可能导致问题的代码。

正如我所说,设置开销一开始似乎令人生畏,但一旦完成,查询可以更简单/更符号来编写和(尤其)修改。我认为MDB2应该更多地了解你的模式,这会简化一些常用的API调用,但你可以通过自己封装模式来减少这种烦恼,如上所述,并提供简单的访问器函数来生成数组MDB2需要执行这些查询。

当然,如果你愿意的话,你可以使用query()函数将平面SQL查询作为字符串进行,所以你不必被迫切换到完整的'MDB2方式' - 你可以逐个尝试它,并且看你是否讨厌它。

答案 7 :(得分:0)

This other question也有一些有用的链接...

答案 8 :(得分:0)

我尝试使用相当通用的函数,只是传递它们之间的差异。这样,您只有一个函数来处理大多数数据库SELECT。显然你可以创建另一个函数来处理所有的INSERTS。

例如

function getFromDB($table, $wherefield=null, $whereval=null, $orderby=null) {
    if($wherefield != null) { 
        $q = "SELECT * FROM $table WHERE $wherefield = '$whereval'"; 
    } else { 
        $q = "SELECT * FROM $table";
    }
    if($orderby != null) { 
        $q .= " ORDER BY ".$orderby; 
    }

    $result = mysql_query($q)) or die("ERROR: ".mysql_error());
    while($row = mysql_fetch_assoc($result)) {
        $records[] = $row;
    }
    return $records;
}

这只是我的头脑,但你明白了。要使用它,只需将函数传递给必要的参数:

例如

$blogposts = getFromDB('myblog', 'author', 'Lewis', 'date DESC');

在这种情况下, $ blogposts 将是一个数组数组,代表表的每一行。然后你可以直接使用foreach或直接引用数组:

echo $blogposts[0]['title'];

答案 9 :(得分:0)

使用像QCodo这样的ORM框架 - 您可以轻松映射现有数据库