检索结果时如何解决不明确的列名?

Posted

技术标签:

【中文标题】检索结果时如何解决不明确的列名?【英文标题】:How to resolve ambiguous column names when retrieving results? 【发布时间】:2010-09-30 16:35:02 【问题描述】:

我的数据库中有两个表:

带有列的NEWS表:

id - 新闻 ID user - 作者的用户ID)

带有列的USERS表:

id - 用户 ID

我要执行这条 SQL:

SELECT * FROM news JOIN users ON news.user = user.id 

当我在 php 中获得结果时,我想获得关联数组并通过$row['column-name'] 获得列名。如何获取具有相同列名的新闻 ID 和用户 ID?

【问题讨论】:

【参考方案1】:

您可以为您选择的列设置别名:

$query = 'SELECT news.id AS newsId, user.id AS userId, [OTHER FIELDS HERE] FROM news JOIN users ON news.user = user.id'

【讨论】:

这个答案使***.com/questions/9233387/sql-should-i-use-a-join 的答案对我有用,谢谢! 这种方法的主要缺点是与SELECT *不同,查询与表结构紧密耦合。也就是说,每当您在架构中添加/删除/重命名列时,您都必须相应地更改您的查询。 @RonInbar。 SELECT * 是大型系统中的维护噩梦。您建议添加一列很简单,如果使用 SELECT *,则不需要对 SQL 进行任何更改,但是在此处确定的别名问题中并非如此。如果新字段很大,它也会对性能产生不必要的影响。它还可以破坏需要特定数字或字段的客户端控件。我可以继续说下去,但是当您了解 SELECT * 对可维护性和性能造成的影响时,它的好处很快就会消失。 @simo.379209 我宁愿说 select * 可能是一场噩梦。这是我们的最后一次。在我们当前的系统中,一切都是从头开始构建的,以使用可扩展的数据结构。检查的形式是“此数据是否存在”而不是“此数据是否存在且不再存在”。我们即将放入一个混乱的猴子,它会随机化列顺序并添加新列。通过能够更快地开发功能并减少损坏,我们获得了很多好处。 select * 是否不好完全取决于上下文,即使在非常大的系统中也是如此。 似乎应该有一个选项,如果你 select table.id 它应该返回列 table.id。似乎是一个可以防止混叠的简单功能。在 php 中,必须为 select 语句加上别名会有点痛苦。【参考方案2】:

您可以使用数字索引 ($row[0]) 或更好,在 mysql 中使用 AS

SELECT *, user.id AS user_id FROM ...

【讨论】:

另外请注意,如果您使用的是 wildwarcd *,它必须是 select 子句中的第一个。不是:SELECT user_id,* ... 没错,但如果顺序很重要,您可以随时使用SELECT user.id AS user_id, user.* FROM ...【参考方案3】:

你可以这样做

SELECT news.id as news_id, user.id as user_id ....

然后$row['news_id'] 将是新闻ID,$row['user_id'] 将是用户ID

【讨论】:

【参考方案4】:

如果您不想使用别名,您也可以只为表名添加前缀。

通过这种方式,您可以更好地自动生成查询。此外,最好的做法是不使用 select *(它显然比只选择您需要的字段要慢 此外,只需明确命名您想要拥有的字段。

SELECT
    news.id, news.title, news.author, news.posted, 
    users.id, users.name, users.registered 
FROM 
    news 
LEFT JOIN 
    users 
ON 
    news.user = user.id

【讨论】:

这有问题。 PHP 会去掉前缀。因此,如果您获取结果,您将无法执行$row['new.id'].. 任何解决方案?【参考方案5】:

我刚刚想通了。这可能是一种不好的做法,但在这种情况下它对我有用。

我是一个懒惰的人,不想用表前缀来别名或写出每个列名。

您可以在 select 语句中使用 table_name.* 从特定表中选择所有列。

当你有重复的列名时,mysql会从头到尾覆盖。当再次遇到该列名时,将覆盖第一个重复列名中的数据。所以最后出现的重复列名获胜。

如果我要连接 3 个表,每个表都包含重复的列名,则 select 语句中表的顺序将决定我为重复列获取的数据。

例子:

SELECT table1.* , table2.* , table3.* FROM table1 LEFT JOIN table2 ON table1.dup = table2.dup LEFT JOIN table3 ON table2.dup = table3.dup;

在上面的例子中,我得到的dup的值将来自table3

如果我想让dup 成为table1 的值怎么办?

那么我需要这样做:

SELECT table3.* , table2.* , table1.* FROM table1 LEFT JOIN table2 ON table1.dup = table2.dup LEFT JOIN table3 ON table2.dup = table3.dup;

现在,table1 排在最后,所以 dup 的值将是 table1 中的值。

我得到了我想要的 dup 的价值,而不必写出每一个可怕的列,我仍然可以使用所有列。耶!

我知道dup 的值在所有 3 个表中应该相同,但是如果table3 没有与dup 匹配的值怎么办?那么dup 在第一个示例中将是空白的,这将是一个无赖。

【讨论】:

依靠无证排序,现在适合你,如果数据库实现发生变化也不会引发任何错误是一个糟糕的想法。 排序黑客很可怕,但是 +1 可以选择 table.* 以便我可以使用 SELECT table1.*, table2.this_one_column_need FROM table1 INNER JOIN table2 ON...【参考方案6】:

我对动态表也有同样的问题。 (假设有一个 id 可以加入但对其余字段没有任何假设的表。)在这种情况下,您事先不知道别名。

在这种情况下,您可以先获取所有动态表的表列名称:

$tblFields = array_keys($zendDbInstance->describeTable($tableName));

其中 $zendDbInstance 是 Zend_Db 的一个实例,或者您可以使用此处的某个函数来不依赖 Zend php pdo: get the columns name of a table

然后对于所有动态表,您可以获得别名并为不需要别名的表使用 $tableName.*:

$aliases = "";
foreach($tblKeys as $field)
    $aliases .= $tableName . '.' . $field . ' AS ' . $tableName . '_' . $field . ',' ;
$aliases = trim($aliases, ',');

您可以将整个过程封装到一个通用函数中,只需编写更简洁的代码,或者根据需要变得更懒惰:)

【讨论】:

【参考方案7】:

@杰森。你是对的,除了 php 是罪魁祸首而不是 mysql。如果您将JOIN 放在 Mysql Workbench 中,您将获得三个名称完全相同的列(每个表一个)但数据不同(如果该表与 JOIN 不匹配,则某些列将是 null )。

在 php 中,如果您在 mysql_fetch_array() 中使用 MYSQL_NUM,那么您将获得所有列。问题是当您将mysql_fetch_array()MYSQL_ASSOC 一起使用时。然后,在该函数内部,php 正在构建返回值,如下所示:

$row['dup'] = [value from table1]

以后……

$row['dup'] = [value from table2]

...

$row['dup'] = [value from table3]

所以你只能从 table3 中得到值。问题是来自 mysql 的结果集可以包含具有相同名称的列,但 php 中的关联数组不允许数组中的重复键。当数据保存在关联数组中时,在php中,一些信息会默默地丢失...

【讨论】:

【参考方案8】:

另一个提示:如果你想拥有更简洁的 PHP 代码,你可以在数据库中创建一个 VIEW,例如

例如:

CREATE VIEW view_news AS
SELECT
  news.id news_id,
  user.id user_id,
  user.name user_name,
  [ OTHER FIELDS ]
FROM news, users
WHERE news.user_id = user.id;

在 PHP 中:

$sql = "SELECT * FROM view_news";

【讨论】:

【参考方案9】:

有两种方法:

    使用别名;在此方法中,您为各个列赋予新的唯一名称 (ALIAS),然后在 PHP 检索中使用它们。 例如

    SELECT student_id AS FEES_LINK, student_class AS CLASS_LINK 
    FROM students_fee_tbl 
    LEFT JOIN student_class_tbl ON students_fee_tbl.student_id = student_class_tbl.student_id
    

    然后在 PHP 中获取结果:

    $query = $PDO_stmt->fetchAll();
    
    foreach($query as $q) 
        echo $q['FEES_LINK'];
    
    

    使用位置位置或结果集列索引;在此,数组位置用于引用重复的列名。由于它们出现在不同的位置,因此将使用的索引号始终是唯一的。 但是,索引定位数字从 0 开始。例如

    SELECT student_id, student_class 
    FROM students_fee_tbl 
    LEFT JOIN student_class_tbl ON students_fee_tbl.student_id = student_class_tbl.student_id
    

    然后在 PHP 中获取结果:

    $query = $PDO_stmt->fetchAll();
    
    foreach($query as $q) 
        echo $q[0];
    
    

【讨论】:

【参考方案10】:

这是上述问题的答案,它既简单又适用于返回的 JSON 结果。虽然当您使用 SELECT * 时,SQL 查询会自动为具有相同字段名称的每个实例添加表名前缀,但要发送回网页的结果的 JSON 编码会忽略具有重复名称的那些字段的值,而是返回 NULL 值.

确切地说,它包含重复字段名称的第一个实例,但使其值为 NULL。并且字段名称的第二个实例(在另一个表中)被完全省略,包括字段名称和值。但是,当您直接在数据库上测试查询时(例如使用 Navicat),结果集中会返回所有字段。仅当您接下来对该结果进行 JSON 编码时,它们是否具有 NULL 值并且后续的重复名称将被完全省略。

因此,解决该问题的一种简单方法是首先执行 SELECT *,然后为重复项添加别名字段。这是一个示例,其中两个表都具有相同名称的 site_name 字段。

SELECT *, w.site_name AS wo_site_name FROM ws_work_orders w JOIN ws_inspections i WHERE w.hma_num NOT IN(SELECT hma_number FROM ws_inspections) ORDER BY CAST(w.hma_num AS UNSIGNED);

现在在解码的 JSON 中,您可以使用字段 wo_site_name,它有一个值。在这种情况下,站点名称具有撇号和单引号等特殊字符,因此在最初保存时进行编码,而在使用数据库结果时进行解码。

...dechtmlifEnc(decodeURIComponent( jsArrInspections[x]["wo_site_name"]))

您必须始终将 * 放在 SELECT 语句的前面,但在它之后您可以包含任意数量的命名和别名列,因为重复选择一个列不会产生任何问题。

【讨论】:

【参考方案11】:

当使用 PDO 进行数据库交互时,可以使用PDO::FETCH_NAMED fetch 模式来帮助解决问题:

$sql = "SELECT * FROM news JOIN users ON news.user = user.id";
$data = $pdo->query($sql)->fetchAll(PDO::FETCH_NAMED);
foreach ($data as $row) 
    echo $row['column-name'][0]; // from the news table
    echo $row['column-name'][1]; // from the users table
    echo $row['other-column']; // any unique column name

【讨论】:

以上是关于检索结果时如何解决不明确的列名?的主要内容,如果未能解决你的问题,请参考以下文章

在 C# 中检索列名

7表的列名与实体类列名不对应,如何将查询结果封装到实体类对象中

java.sql.SQLException:尝试检索视图条目时列名无效

使用“wininet”和“windows.h”时如何解决“IServiceProvider”不明确?

如何解决sqlserver2005中用多表连接的结果建一张新表时,提示各表中的列名必须唯一的问题?

PHP MongoDB 驱动程序的游标如何缓冲结果集?