SQL 从多个表中选择 *

Posted

技术标签:

【中文标题】SQL 从多个表中选择 *【英文标题】:SQL Select * from multiple tables 【发布时间】:2011-02-01 04:35:26 【问题描述】:

使用 php/PDO/mysql 是否可以在对多个表进行选择并且返回的数组键是完全限定的以避免列名冲突时对列使用通配符?

示例:

SELECT * from table1, table2;

给予:

数组键是'table1.id'、'table2.id'、'table1.name'等

我尝试了“SELECT table1.*,table2.* ...”,但返回的数组键不是完全限定的,因此具有相同名称的列发生冲突并被覆盖。

【问题讨论】:

注意不同表中的同名列 - 你只会得到一个实例。 是的。我确实注意到,如果我知道每个表中的列数,我可以通过数字索引检索值,但事实并非如此。 您是否有特定的原因需要这样做?无论如何,从多个表中使用 SELECT * 通常是个坏主意,所以在我给你答案之前,我只是想弄清楚你这样做背后的思考过程。 同意。除非您不知道列名(我敢打赌您知道),否则 SELECT * 绝不是一个好主意:您正在运行两个查询而不是一个(一个用于列名,然后是您的查询)并且您最可能会带回比您需要的更多的数据。现在可能不会注意到问题,但如果其中一些字段是 BLOB 或有很多文本,您会注意到的。 @Stephen 我有几个包含不相关列的“内容”表和一个包含其他表的类似数据的“元数据”表。我使用元数据表(可以说是一对一关系)查询所有内容表列的内容表。我希望可以更好地解释,但我的数据库术语是有限的。 @Tom我当然知道列名。只是不想把它们打出来! 【参考方案1】:

你可以这样做:

SELECT Table1.*,Table2.xyz, Table2.abc,... From...

您使用“*”从一个表中获取所有列,然后只从您需要的另一个表中获取列,因此没有冲突。

您还可以使用列别名,在其中“重命名”一列:

SELECT Table1.A AS T1_A,Table2.A AS T2_A,... From...

您的结果集将包含 T1_A 和 T2_A 列

【讨论】:

首先您要指定列名 - 我们不知道列以及如果 xyz 或 abc 也在 Table1 中怎么办?其次,正如我所说,我已经尝试过通配符。 如果我们不知道表中的列,那么重命名它们会有点困难。 你怎么会不知道列呢?这是用动态 SQL 生成的吗?如果是,则动态构建选择列表。 所涉及的表是动态的。我试图避免编写处理每种“类型”表的代码。 @zaf 如果你不知道列名,你怎么知道你需要哪些/哪些被覆盖了?【参考方案2】:

不幸的是,PHP(尤其是 MySQL、PgSQL、MSSQL 扩展)总是会在重叠的情况下覆盖您的列。

我建议在您的数据库中创建一个View,并为您的列添加别名,以便它们“完全合格”。

例如:(MySQL)

CREATE VIEW viewTable1Table2 AS
    SELECT
        t1.field1 AS Table1Field1
        t2.field1 AS Table2Field1
    FROM Table1 t1
        INNER JOIN Table2 t2
            ON t1.id = t2.id;

语法可能并不完美,但您可以大致了解我在说什么。

【讨论】:

不知道但还是同样的问题。【参考方案3】:

很遗憾,没有;没有用于确保列名唯一的 SQL 语法。

如果您真的不知道列的名称并且必须使用SELECT *,那么您唯一真正的选择是恢复到一些看起来非常难看的动态 SQL,它可以检查表的结构并生成一个查询将使用表名前缀显式选择它们。

我不知道您使用的是哪个 RDBMS,但这样的东西应该可以在 SQL Server 上运行:

declare @columns table (idx int identity(1,1), tablename varchar(100), columnname varchar(100))

insert into @columns (tablename, columnname) 
select tablename, columnname

from INFORMATION_SCHEMA.COLUMNS

where tablename in ('table_1', 'table_2')

declare @sql nvarchar(4000)

declare @i int
declare @cnt in

declare @col varchar(100)
declare @table varchar(100)

select @i = 0, @cnt = max(idx), @sql = '' from @columns

while @i < @cnt
begin
    select @i = @i + 1

    select @col = columnname, @table = tablename from @columns where idx = @i

    if len(@sql) > 0
        select @sql = @sql + ', '

    select @sql = @sql + '[' + @table + '].[' + @col + '] as [' + @table + '_' + @col + ']'
end

select @sql = 'select ' + @sql + ' from table_1, table_2'

exec sp_executesql @sql

【讨论】:

到我写的时候,我可以手动编写 sql 查询。等一下……我本可以这样做而不是在这里。开玩笑。我不知道哪种语言/数据库/库,但我确实记得必须使用完全限定的列名并且讨厌输入完整的列名。现在我遇到了相反的问题。 @zaf:然后适应它。你正在寻找的东西是不可能的。我为您提供了至少实现您正在寻找的东西,即使它不太理想。【参考方案4】:

是的,你可以。最简单的方法是使用 pdo,尽管至少还有一些其他扩展可以做到这一点。

pdo

在PDO 对象上设置属性,而不是PDOStatment。

$PDO->setAttribute(PDO::ATTR_FETCH_TABLE_NAMES, true);

就是这样。然后你会得到像$row['myTable.myColumn'] 这样的关联数组键。如果你也获取一个对象(例如通过PDO::FETCH_OBJECT),它会起作用,所以要小心,因为你需要访问像$obj-&gt;'myTable.myColumn'这样的属性

*manual says PDO::ATTR_FETCH_TABLE_NAMES 属性仅受某些驱动程序支持。如果上述方法不起作用,则可能可以替代。

$pdoStatement->setFetchMode(PDO::FETCH_NUM);
$pdoStatement->execute();
//build our associative array keys
$qualifiedColumnNames = array();
for ($i = 0; $i < $pdoStatement->columnCount(); $i++) 
    $columnMeta = $pdoStatement->getColumnMeta($i);
    $qualifiedColumnNames[] = "$columnMeta[table].$columnMeta[name]";


//fetch results and combine with keys
while ($row = $pdoStatement->fetch()) 
    $qualifiedRow = array_combine($qualifiedColumnNames, $row);
    print_r($qualifiedRow);


相同的基本模式用于其他数据库扩展

mysql

$res = mysql_query($sql);
//build our associative array keys
$qualifiedColumnNames = array();
for ($i = 0; $i < mysql_num_fields($res); $i++) 
    $columnMeta = mysql_fetch_field($res, $i);
    $qualifiedColumnNames[] = "$columnMeta[table].$columnMeta[name]";


//fetch results and combine with keys
while ($row = mysql_fetch_row($res)) 
    $qualifiedRow = array_combine($qualifiedColumnNames, $row);
    print_r($qualifiedRow);


mysqli

$res = $mysqli->query($sql);
//build our associative array keys
$qualifiedColumnNames = array();
foreach ($res->fetch_fields() as $columnMeta) 
    $qualifiedColumnNames[] = "$columnMeta->table.$columnMeta->name";


//fetch results and combine with keys
while ($row = $res->fetch_row()) 
    $qualifiedRow = array_combine($qualifiedColumnNames, $row);
    print_r($qualifiedRow);

这也应该适用于表别名(在 php 7.1 中测试) - 合格的列名将使用表别名。

【讨论】:

这是个好主意,不幸的是它没有得到表别名 @FullDecent PDO 方法和我提出的 mysqli 方法都适用于表别名。我在 php 7.1 上测试过。我没有测试mysql 方法,因为它太老了,现在不受支持。【参考方案5】:

从@goat 无耻地重新包装:

// Workaround for setAttribute(PDO::ATTR_FETCH_TABLE_NAMES, true);
function pdoStatementExecuteAndFetchObjWithTableNames(PDOStatement $statement)

  $statement->setFetchMode(PDO::FETCH_NUM);
  $statement->execute();

  //build our associative array keys
  $qualifiedColumnNames = array();
  for ($i = 0; $i < $statement->columnCount(); $i++) 
      $columnMeta = $statement->getColumnMeta($i);
      $qualifiedColumnNames[] = "$columnMeta[table].$columnMeta[name]";
  

  //fetch results and combine with keys
  while ($row = $statement->fetch()) 
      $qualifiedRow = array_combine($qualifiedColumnNames, $row);
      yield (object) $qualifiedRow;
    

注意:如果您使用:

从 my_table 中选择 1 作为 my_table_alias

然后你会得到my_table。我本来希望my_table_alias。我使用 PHP 5.6 和 sqlite 驱动程序得到了这个结果。

【讨论】:

以上是关于SQL 从多个表中选择 *的主要内容,如果未能解决你的问题,请参考以下文章

SQL查询从键表中选择多个条目到同一行的不同列中

HANA SQL 从一个表中找到的多个表中选择计数 (*)

如何从 SQL 表中选择特定行并连接 SQL 服务器中的多个表?

SQL 从多个表中选择返回可用的内容

如何从SQL表中选择特定行并在SQL Server中连接多个表?

使用嵌套查询从表列表/多个表中选择 - MS SQL