你如何在 php 脚本中嵌入你的 sql 查询(编码风格)?

Posted

技术标签:

【中文标题】你如何在 php 脚本中嵌入你的 sql 查询(编码风格)?【英文标题】:How do you embedded your sql queries in php scripts (coding-style)? 【发布时间】:2009-01-14 12:41:51 【问题描述】:

你如何在 php 中嵌入你的 sql 脚本?你只是把它们写在一个字符串或一个heredoc中,还是你把它们外包给一个sql文件?外包时是否有最佳实践?有没有一种优雅的方式来组织这个?

【问题讨论】:

我要问完全相同的问题。似乎在这个线程中没有人回答甚至提到问题的这个特定部分:是否有任何最佳实践何时外包? - 所以,我在这里添加我的评论,并希望听到有关该特定方面的 cmets / 建议 / 意见。特别是,如果有人知道一些用于拆分 SQL 查询和 PHP 代码的优秀开源工具/脚本,我将不胜感激。 【参考方案1】:

使用带有 ORM(对象关系映射)层的框架。这样您就不必将直接的 SQL 放在任何地方。嵌入式 SQL 在可读性、维护和所有方面都很糟糕。

【讨论】:

"这样您就不必在任何地方直接放置 SQL。"很好的建议,但理论上很好,在实践中很难。 ORM 永远不会拥有 SQL 所具备的原始能力,有时运行复杂的报告需要这种原始能力。 一个框架通常有助于拥有一个有助于避免复杂查询的数据库模式。这意味着数据库没有被完全使用(主要作为对象的持久存储),但它有助于维护。 @Alan:ORM 层确实允许您在需要时直接编写 SQL。选择权留给程序员 @Swanand True,但我们又回到了 OP 的问题。即使使用 ORM,它仍然必须回答。【参考方案2】:

始终记得转义输入。不要手动执行,使用准备好的语句。这是我的报告类中的一个示例方法。

public function getTasksReport($rmId, $stage, $mmcName) 
    $rmCondition = $rmId ? 'mud.manager_id = :rmId' : 'TRUE';
    $stageCondition = $stage ? 't.stage_id = :stageId' : 'TRUE';
    $mmcCondition = $mmcName ? 'mmcs.username = :mmcName' : 'TRUE';
    $sql = "
            SELECT
                    mmcs.id AS mmc_id,
                    mmcs.username AS mmcname,
                    mud.band_name AS mmc_name,
                    t.id AS task_id,
                    t.name AS task, 
                    t.stage_id AS stage,
                    t.role_id,
                    tl.id AS task_log_id,
                    mr.role,
                    u.id AS user_id,
                    u.username AS username,
                    COALESCE(cud.full_name, bud.band_name) AS user_name,
                    DATE_FORMAT(tl.completed_on, '%d-%m-%Y %T') AS completed_on,
                    tl.url AS url,
                    mud.manager_id AS rm_id
            FROM users AS mmcs
            INNER JOIN banduserdetails AS mud ON mud.user_id = mmcs.id
            LEFT JOIN tasks AS t ON 1
            LEFT JOIN task_log AS tl ON tl.task_id = t.id AND tl.mmc_id = mmcs.id
            LEFT JOIN mmc_roles AS mr ON mr.id = t.role_id
            LEFT JOIN users AS u ON u.id = tl.user_id
            LEFT JOIN communityuserdetails AS cud ON cud.user_id = u.id
            LEFT JOIN banduserdetails AS bud ON bud.user_id = u.id
            WHERE mmcs.user_type = 'mmc'
                    AND $rmCondition
                    AND $stageCondition
                    AND $mmcCondition
            ORDER BY mmcs.id, t.stage_id, t.role_id, t.task_order
    ";
    $pdo = new PDO(.....);
    $stmt = $pdo->prepare($sql);
    $rmId and $stmt->bindValue('rmId', $rmId); // (1)
    $stage and $stmt->bindValue('stageId', $stage); // (2)
    $mmcName and $stmt->bindValue('mmcName', $mmcName); // (3)
    $stmt->execute();
    return $stmt->fetchAll();

在标记为 (1)、(2) 和 (3) 的行中,您将看到条件绑定的一种方式。

对于简单的查询,我使用 ORM 框架来减少手动构建 SQL 的需要。

【讨论】:

呵呵,我以为我是唯一一个在代码中真正格式化 SQL 的人,好看!【参考方案3】:

这取决于查询的大小和难度。

我个人喜欢 heredocs。但我不将它用于简单的查询。 那不重要。最主要的是“永远不要忘记转义值”

【讨论】:

【参考方案4】:

你应该总是真的总是使用带有占位符的prepare语句来代表你的变量。

它的代码稍微多一些,但它在大多数数据库上运行效率更高,并且可以保护您免受 SQL 注入攻击。

【讨论】:

【参考方案5】:

我更喜欢这样:

$sql = "SELECT tbl1.col1, tbl1.col2, tbl2.col1, tbl2.col2"
        . " FROM Table1 tbl1"
        . " INNER JOIN Table2 tbl2 ON tbl1.id = tbl2.other_id"
        . " WHERE tbl2.id = ?"
        . " ORDER BY tbl2.col1, tbl2.col2"
        . " LIMIT 10, 0";

PHP 可能需要稍长一点的时间来连接所有字符串,但我认为它看起来更好并且更易于编辑。

当然,对于极长且专门的查询,读取 .sql 文件或使用存储过程是有意义的。根据您的框架,这可能很简单:

$sql = (string) View::factory('sql/myfile');

(如有必要,您可以选择在视图/模板中分配变量)。如果没有模板引擎或框架的帮助,您将使用:

$sql = file_get_contents("myfile.sql");

希望这会有所帮助。

【讨论】:

【参考方案6】:

我通常把它们写成函数参数:

db_exec ("SELECT ...");

除了 sql 会很大的情况,我将它作为变量传递:

$SQL = "SELECT ...";
$result = db_exec ($SQL);

(我使用包装函数或对象进行数据库操作)

【讨论】:

【参考方案7】:
$sql = sprintf("SELECT * FROM users WHERE id = %d", mysql_real_escape_string($_GET["id"]));

MySQL 注入安全

【讨论】:

【参考方案8】:

您可以使用 ORM 或 sql 字符串构建器,但一些复杂的查询需要编写 sql。在编写 sql 时,如 Michał Słaby 所示,使用查询绑定。查询绑定可防止 sql 注入并保持可读性。至于将查询放在哪里:使用模型类。

【讨论】:

【参考方案9】:

一旦达到一定水平,您就会意识到您编写的 99% 的 SQL 都可以自动化。如果你写了这么多查询,以至于你想到了一个属性文件,那么你可能正在做一些更简单的事情:

我们程序员做的大部分事情都是 CRUD:创建读取更新删除

作为我自己的工具,我构建了 Pork.dbObject。 2个简单类中的对象关系映射器+活动记录(数据库抽象+dbObject类)

我网站上的几个例子:

创建一个博客:

    $weblog = new Weblog(); // create an empty object to work with. 
    $weblog->Author = 'SchizoDuckie'; // mapped internally to strAuthor. 
    $weblog->Title = 'A test weblog';  
    $weblog->Story = 'This is a test weblog!'; 
    $weblog->Posted = date("Y-m-d H:i:s"); 
    $weblog->Save(); // Checks for any changed values and inserts or updates into DB. 
    echo ($weblog->ID) // outputs: 1 

还有一个回复:

    $reply = new Reply(); 
    $reply->Author = 'Some random guy'; 
    $reply->Reply = 'w000t'; 
    $reply->Posted = date("Y-m-d H:i:s"); 
    $reply->IP = '127.0.0.1'; 
    $reply->Connect($weblog); // auto-saves $reply and connects it to $weblog->ID 

然后,获取并显示博客 + 所有回复:

    $weblog = new Weblog(1); //Fetches the row with primary key 1 from table weblogs and hooks it's values into $weblog;

    echo("<h1>$weblog->Title</h1> 
    <h3>Posted by $weblog->Author @ $weblog->Posted</h3> 
    <div class='weblogpost'>$weblog->Story</div>"); 

    // now fetch the connected posts. this is the real magic: 
    $replies = $weblog->Find("Reply"); // fetches a pre-filled array of Reply objects. 
    if ($replies != false) 
     
        foreach($replies as $reply) 
         
            echo("<div class='weblogreply'><h4>By $reply->Author @ $reply->Posted</h4> $reply->Reply</div>"); 
         
     

weblog 对象如下所示:

class Weblog extends dbObject 
 
    function __construct($ID=false) 
     
        $this->__setupDatabase('blogs', // database table 
        array('ID_Blog' => 'ID',    // database field => mapped object property 
            'strPost' => 'Story',    // as you can see, database field strPost is mapped to $this->Story 
            'datPosted' => 'Posted', 
            'strPoster' => 'Author', 
            'strTitle'  => 'Title',
            'ipAddress'  => 'IpAddress', 
            'ID_Blog',    // primary table key  
            $ID);    // value of primary key to init with (can be false for new empty object / row) 
        $this->addRelation('Reaction'); // define a 1:many relation to Reaction 

    

看,无需手动编写 SQL :) 链接+更多示例:Pork.dbObject

哦,是的,我还为我的脚手架工具创建了一个基本的 GUI:Pork.Generator

【讨论】:

【参考方案10】:

我喜欢这种格式。之前的评论中提到过,但我觉得对齐方式不对。

$query = "SELECT "
       . " foo, "
       . " bar "
       . "FROM "
       . " mytable "
       . "WHERE "
       . " id = $userid";

足够容易阅读和理解。点与等号对齐,使所有内容保持清晰。

我也喜欢将您的 SQL 保存在一个单独的文件中的想法,尽管我不确定在上面的示例中这将如何处理像 $userid 这样的变量。

【讨论】:

复制之前的评论有什么用?改为投票。此外,使用参数化查询并避免所有这些双引号。

以上是关于你如何在 php 脚本中嵌入你的 sql 查询(编码风格)?的主要内容,如果未能解决你的问题,请参考以下文章

如何在执行php脚本后将mysql查询结果返回到html页面?

sql 10多万行的数据 求助

如何检测SQL注入技术以及跨站脚本攻击

如何使用sql语句使你的数据库减肥,下面以网狐6603数据库减肥脚本举例!

请教如何在没有安装python的环境中执行py脚本

SQL脚本如何还原成SQL数据库呢?