检索结果时如何解析歧义的列名?


75

我的数据库中有两个表:

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?

Answers:


119

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

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


3
这种方法的主要缺点是,与相比SELECT *,查询与表结构紧密耦合。也就是说,无论何时在架构中添加/删除/重命名列,都必须相应地更改查询。
罗恩·英巴尔

4
@RonInbar。SELECT *在大型系统中是维护的噩梦。您建议添加列很简单,如果使用SELECT *,则不需要对SQL进行任何更改,但是情况并非如此处标识的别名问题那样。如果新领域很大,它也会给性能带来不必要的影响。它也可能破坏期望某些数量或字段的客户端控件。我可以继续,但是SELECT *当您了解它对可维护性和性能的影响时,消失的好处很快就会消失。
simo.3792 2014年

1
@ simo.379209我想说选择*可能是一场噩梦。那是我们的最后一次。在我们当前的系统中,一切都是从头开始构建的,以使用可扩展的数据结构。检查的形式为“是否存在此数据”而不是“是否存在此数据且不再存在”。我们将放入一只混乱的猴子,该猴子随机化列顺序并添加新列。通过更快地开发功能并减少破损,我们获得了很多好处。select *是否不好取决于整个上下文,即使在非常大的系统中也是如此。
Max Murphy

似乎应该有一个选择,如果您select table.id应该返回列table.id。似乎是一个简单的功能,可以防止混淆。在php中,必须为select语句加上别名会有些麻烦。
Kolob

42

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

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


13
还要注意,如果您使用wildwarcd *,它必须是select子句中的第一个。不:SELECT user_id,* ...
Anders

2
没错,但是SELECT user.id AS user_id, user.* FROM ...如果顺序很重要,您可以随时进行。
Cyrille

23

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

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

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

当您重复了列名时,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;

在上面的示例中,dupI get的值将来自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在第一个示例中将为空白,那简直是无赖。


3
依靠目前适用于您的无证订购,如果数据库实现发生更改,这不会引起任何错误,这是一个AWFUL的主意。
simo.3792 2014年

2
订购技巧很吓人,但可以选择+1,table.*以便我可以做SELECT table1.*, table2.this_one_column_need FROM table1 INNER JOIN table2 ON...
tobek's

10

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

在php中,如果您使用MYSQL_NUMin,mysql_fetch_array()那么您将获得所有列。问题是当您使用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中的关联数组中时,某些信息会静默丢失...


9

你可以做类似的事情

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

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


9

另一个提示:如果您想要更简洁的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";

3

我在动态表中也遇到了同样的问题。(假定表具有可连接的ID,但其余字段没有任何假设。)在这种情况下,您事先不知道别名。

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

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

其中$ zendDbInstance是Zend_Db的实例,或者您可以在此处使用其中一个函数以不依赖Zend php pdo:获取表的列名

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

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

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


0

如果您不希望使用别名,也可以在表名前面加上前缀。

这样,您可以更好地自动生成查询。此外,这是一个最好的做法是不使用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']..任何解决方案?
CoffeDeveloper 2015年

0

这是上面的答案,这很简单,并且可以与返回的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语句中始终将*放在第一位,但是在它之后,您可以根据需要包含任意数量的命名和别名列,因为重复选择列不会造成任何问题。


0

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

$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
}

-1

有两种方法:

  1. 使用别名;在这种方法中,您为各个列提供了新的唯一名称(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'];
    }
    
  2. 使用场所位置或结果集列索引;在此,数组位置用于引用重复的列名。由于它们出现在不同的位置,因此将使用的索引号始终是唯一的。但是,索引定位编号从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];
    }
    
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.