【问题标题】:how to identify the source table of fields from a mysql query如何从 mysql 查询中识别字段的源表
【发布时间】:2009-05-07 12:44:47
【问题描述】:

我有两个动态表(tabx 和 taby),它们是通过 php 界面创建和维护的,可以在其中添加、删除、重命名等列。 我想像这样从两个表中同时读取所有列;-

select * from tabx,taby where ... ;

我希望能够从查询结果中判断每列是来自 tabx 还是 taby - 有没有办法强制 mysql 返回完全限定的列名,例如tabx.col1、tabx.col2、taby.coln 等?

【问题讨论】:

    标签: php mysql


    【解决方案1】:

    在PHP中,你可以从结果中获取字段信息,就像这样(从我很久以前写的一个项目中偷来的):

    /*
    Similar to mysql_fetch_assoc(), this function returns an associative array
    given a mysql resource, but prepends the table name (or table alias, if
    used in the query) to the column name, effectively namespacing the column
    names and allowing SELECTS for column names that would otherwise have collided
    when building a row's associative array.
    */
    function mysql_fetch_assoc_with_table_names($resource) {
        // get a numerically indexed row, which includes all fields, even if their names collide
        $row = mysql_fetch_row($resource);
        if( ! $row)
            return $row;
    
        $result = array();
    
        $size = count($row);    
        for($i = 0; $i < $size; $i++) {
            // now fetch the field information
            $info = mysql_fetch_field($resource, $i);
            $table = $info->table;
            $name = $info->name;
            // and make an associative array, where the key is $table.$name
            $result["$table.$name"] = $row[$i];  // e.g. $result["user.name"] = "Joe Schmoe";
        }
    
        return $result;
    }
    

    那么你可以这样使用它:

    $resource = mysql_query("SELECT * FROM user JOIN question USING (user_id)");
    while($row = mysql_fetch_assoc_with_table_names($resource)) {
        echo $row['question.title'] . ' Asked by ' . $row['user.name'] . "\n";
    }
    

    所以要直接回答你的问题,表名数据总是由 MySQL 发送的——由客户端来告诉你每列的来源。如果您真的希望 MySQL 明确地返回每个列名,则需要修改查询以显式执行别名,如 @Shabbyrobe 建议的那样。

    【讨论】:

    • 我不知道你能做到这一点。谢谢你的提示!与旧的 mysql_* 函数相比,我更喜欢 PDO。不过,我似乎无法在此处发布如何使用 PDO 执行此操作的代码 sn-p - 它很无聊。我会将其添加到我的答案中,但如果您想将其添加到您的答案中,我会将其删除。
    【解决方案2】:
    select * from tabx tx, taby ty where ... ;
    

    【讨论】:

    • 这不起作用 - 表名没有附加到返回的列名上。即你得到 col1,col2,col3 而不是 tx.col1,tx.col2 等。
    • 我在 mysql 上测试过它确实有效。我错过了什么吗?
    • 它输出带有表别名前缀的列名?哪个平台的哪个版本的mysql?
    • 是的,我在 CentOS 5 上使用 MySql 5.0.45
    【解决方案3】:

    做:

    SELECT tabx.*, taby.* FROM tabx, taby WHERE ...
    

    工作?

    【讨论】:

    • 呸——被它打败了 22 秒:p
    • 再一次,这不起作用 - 表名没有附加到返回的列名上。即你得到 col1,col2,col3 而不是 tabx.col1,tabx.col2 等。
    • 这将起作用,您只需将查询分成两个,每个表一个。这只需要执行一次,之后您应该知道哪些列属于哪个表。 SELECT * FROM tabx WHERE ... SELECT * FROM taby WHERE ... 现在从这些结果中,您可以使用正确的附件对查询进行自定义。
    【解决方案4】:

    我想知道您要完成什么。首先,在表中添加和删除列是一种奇怪的做法;这意味着您的数据架构在运行时正在发生变化。

    此外,要同时从两个表中查询,它们之间应该存在某种关系。一个表中的行应该以某种方式与另一个表中的行相关联。如果不是这种情况,最好执行两个单独的 SELECT 查询。

    您的问题的答案已经给出:SELECT tablename.* 从给定表中检索所有列。如果两个表中都有同名的列,这可能会也可能不会正常工作;您应该在文档中查找。

    您能否就您要解决的问题向我们提供更多信息?我认为你很有可能会走错路。

    【讨论】:

    • 数据的模式在运行时会发生变化,有问题的 2 个表用作帐户记录的扩展,其中用户定义要存储在帐户上的信息,例如可以为移动电话号码或传真号码添加一列。等等。这个代码已经到位并且正在工作。 2 个额外的表格通过帐户上的唯一 id 链接到帐户,这是两个额外表格的关键。我可以在 3 个单独的查询中读取所有必需的信息,但是我希望合并为 1 个查询,所以我需要知道每列来自哪个表,以防我需要更新它。
    【解决方案5】:

    撇开关于为什么要这样做以及为什么要在这里进行交叉连接的任何问题,这是我能想到的最好的方法。

    您可以尝试对每个表执行 EXPLAIN 并根据结果以编程方式构建 select 语句。这是一个糟糕的脚本示例,它将为您提供一个动态生成的带有别名的字段列表。这将增加您执行的查询数量,因为动态生成的查询中的每个表都会导致 EXPLAIN 查询被触发(尽管这可以通过缓存相当容易地缓解)。

    <?php
    $pdo = new PDO($dsn, $user, $pass, array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION));
    function aliasFields($pdo, $table, $delim='__') {
        $fields = array();
        // gotta sanitise the table name - can't do it with prepared statement
        $table = preg_replace('/[^A-z0-9_]/', "", $table);
        foreach ($pdo->query("EXPLAIN `".$table."`") as $row) {
            $fields[] = $table.'.'.$row['Field'].' as '.$table.$delim.$row['Field'];
        }
        return $fields;
    }
    $fieldAliases = array_merge(aliasFields($pdo, 'artist'), aliasFields($pdo, 'event'));
    $query = 'SELECT '.implode(', ', $fieldAliases).' FROM artist, event';
    echo $query;
    

    结果是一个如下所示的查询,表名和列名由两个下划线分隔(或任何您喜欢的分隔符,请参见 aliasFields() 的第三个参数):

    // ABOVE PROGRAM'S OUTPUT (assuming database exists)
    SELECT artist__artist_id, artist__event_id, artist__artist_name, event__event_id, event__event_name FROM artist, event
    

    从那里,当您迭代结果时,您可以使用相同的分隔符对每个字段名称进行分解,以获得表名称和字段名称。


    John Douthat 的回答比上面的要好得多。仅当数据库未返回字段元数据时它才有用,因为 PDO 威胁可能是某些驱动程序的情况。

    这是一个简单的 sn-p,说明如何使用 PDO 而不是 mysql_*() 来执行 John 建议的操作:

    <?php
    
    $pdo = new PDO($dsn, $user, $pass, array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION));
    
    $query = 'SELECT artist.*, eventartist.* FROM artist, eventartist LIMIT 1';
    $stmt = $pdo->prepare($query);
    $stmt->execute();
    
    while ($row = $stmt->fetch()) {
        foreach ($row as $key=>$value) {
            if (is_int($key)) {
                $meta = $stmt->getColumnMeta($key);
                echo $meta['table'].".".$meta['name']."<br />";
            }
        }
    }
    

    【讨论】:

    • 不幸的是,由于我无法控制何时将列添加到表中或从表中删除,因此每次我想读取它时都必须重新构建这个选择查询,这会导致过多的开销.
    • aliasFields() 方法是完全动态的——如果你在运行时传递一个表名,它会为你计算出列别名,以便在运行时包含表名。我在帖子底部包含的查询只是输出的一个示例。
    猜你喜欢
    • 1970-01-01
    • 2021-05-27
    • 2018-11-29
    • 1970-01-01
    • 2011-04-27
    • 2020-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多