选择除MySQL中的一列以外的所有列?


365

我正在尝试使用select语句从某个MySQL表中获取除一个以外的所有列。有没有简单的方法可以做到这一点?

编辑:此表中有53列(不是我的设计)


6
53列?在那种情况下,我会坚持使用SELECT *,就像Thomas所建议的那样……除非该额外的列包含大量的数据,这些数据将是不可取的……?
Mike Stone,

除了一个列...我想您知道应该忽略哪个列,因此,INFORMATION_SCHEMA.columns是这种方式。
Alfabravo 2010年

看看这个答案,它确实可以满足您的需求!
donL 2012年

当保存地理数据时,大数据列是一个实际问题。列的大小可以为许多兆字节。它们在where子句中可以很好地找到行,但是您通常不希望结果中包含该数据。
杰森

这样做的常见用法是排除自动递增ID列。例如,选择要插入不同表的数据,该表具有自己的ID。
ToolmakerSteve

Answers:


222

实际上有一种方法,您当然需要具有执行此操作的权限...

SET @sql = CONCAT('SELECT ', (SELECT REPLACE(GROUP_CONCAT(COLUMN_NAME), '<columns_to_omit>,', '') FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '<table>' AND TABLE_SCHEMA = '<database>'), ' FROM <table>');

PREPARE stmt1 FROM @sql;
EXECUTE stmt1;

更换 <table>, <database> and <columns_to_omit>


20
警告:INFORMATION_SCHEMA查询的性能很差,因此请注意,这种查询对于任何事物都不是关键路径。
Bill Karwin 2011年

9
就像下面的@Jan Koritak一样,如果要删除的标题的列也是要保留的任何列的标题的子字符串,则此答案实际上不起作用。有一个更好的答案与此类似,可以在这里找到
donL 2012年

8
这比仅指定列(这是已知的最佳做法)更糟糕。
HLGEM 2014年

3
如果列名中有空格,则此方法无效。应该对其进行更新以始终在反引号中<column_name>
包含

3
如果要忽略的列是表结构中列出的最后一个列,则这也不起作用(因为替换将不匹配尾随的逗号)。
Vincent Pazeller

61

在mysql定义(手动)中,没有这样的东西。但是,如果您有非常多的列col1,... col100,,则可能会有用:

DROP TABLE IF EXISTS temp_tb;
CREATE TEMPORARY TABLE ENGINE=MEMORY temp_tb SELECT * FROM orig_tb;
ALTER TABLE temp_tb DROP col_x;
SELECT * FROM temp_tb;

4
在第3步给了我错误消息:“列数与第1行的值数不匹配”。因此,我将步骤2更改为“ UPDATE temp_tb SET id = NULL”,然后它起作用了。
oyvey '16

1
好的,这可行。但是,当我再次运行此查询时,它给我一个错误,即temp_tb已经存在。temp_tb在内存中保留了多少时间?抱歉,这是一个愚蠢的问题。PS赞成您的回答。
卡兰

2
@Karan要重复运行此查询,请在开头添加另一个命令:DROP TABLE IF EXISTS temp_tb;
gregn3

1
@Karan要指定内存引擎,请使用命令:CREATE TEMPORARY TABLE temp_tb ENGINE=MEMORY (SELECT * FROM orig_tb); ,否则默认情况下会将其保存到磁盘,并且在服务器重新启动后仍然有效。(感谢
gregn3

极好的答案。要删除多列,请参考此答案
S3DEV

45

在这种情况下,View能更好地工作吗?

CREATE VIEW vwTable
as  
SELECT  
    col1  
    , col2  
    , col3  
    , col..  
    , col53  
FROM table

15
哈哈!当然可以。现在如何构造视图以包括所有但其中一列。我想您知道这是如何提出原始问题的。实际上,我之所以找到此线程,是因为我想创建一个排除某些列的视图,而不必强制在视图定义中显式列出所有其余列。
克里斯·纳多维奇

31

你可以做:

SELECT column1, column2, column4 FROM table WHERE whatever

没有获得column3,尽管也许您正在寻找更通用的解决方案?


3
大多数评分较高的答案大多只是在寻找无需手动输入即可生成此确切查询的方法
mehtunguh 2014年

5
实际上,出于维护原因,具有“除此以外的所有内容”类型的查询很有用。否则,如果以后添加新字段,则必须更新显式字段列表。
leiavoia 2014年

1
@lev,没错,应该!!!因为您不知道是否要增加将来的列(它们可能是元数据列,也可能不适用于特定屏幕)。你不 希望通过返回比您需要的更多的值来损害性能(在使用内部联接时,这确实是100%的时间),我建议您阅读一下为什么select *是SQL反模式的原因。
HLGEM

26
OP指出有> 50列,因此这是不切实际的。
augurar 2015年

1
@HLGEM并非如此,我想在每次添加新列时都选择不维护查询的所有列,但其中一个除外,这在我的情况下是行不通的。但不管怎么说,我申请肖恩·解决方案为自己
OverCoder

26

如果您想排除某个字段的值(例如出于安全考虑/敏感信息),则可以将该列检索为null。

例如

SELECT *, NULL AS salary FROM users

13
为什么?没用 如果您有一列薪水,则此查询最终将得到结果,其中包含两列名为薪水的列,其中一列为空,而另一列为实际薪水。
Myforwik

4
@Myforwik此查询确实确实添加了第二salary列。但是,由于它是在*之后检索的,因此它将覆盖原始文件。它虽然不漂亮,但确实有效。
肖恩·奥

67
SQL始终允许结果集中重复的列名。如果要使其正常工作,则需要使用不支持重复对象优先于最后一个重复对象的客户端应用程序运行它。官方命令行客户端支持dupes。HeidiSQL也支持它们。SQL Fiddle不会,但是显示第一个重复项,而不显示最后一个。总结:这是行不通的
阿尔瓦罗·冈萨雷斯

9
当然不行了。这是一个很好的例子,说明了为什么应该在mysql本身而不是通过与mysql通讯的库中测试答案。
2013年

8
@ SeanO,@ Alastair,@每个人,都不要重写。服务器仍然返回敏感数据。
Pacerier,2015年

22

据我所知,没有。您可以执行以下操作:

SELECT col1, col2, col3, col4 FROM tbl

并手动选择所需的列。但是,如果您需要很多列,则可能只需要执行以下操作:

SELECT * FROM tbl 

只是忽略你不想要的

对于您的特殊情况,我建议:

SELECT * FROM tbl

除非您只需要几列。如果只需要四列,则:

SELECT col3, col6, col45, col 52 FROM tbl

会很好,但是如果您想要50列,那么构成查询的任何代码都将变得(太难理解)。


3
选择*始终是一个糟糕的选择。不推荐它。阅读有关为什么它是SQl反模式的信息。
HLGEM 2014年

18

在尝试@Mahomedalid和@Junaid的解决方案时,我发现了一个问题。因此想共享它。如果列名包含空格或连字符(例如,签入),则查询将失败。一种简单的解决方法是在列名前后使用反引号。修改后的查询如下

SET @SQL = CONCAT('SELECT ', (SELECT GROUP_CONCAT(CONCAT("`", COLUMN_NAME, "`")) FROM
INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'users' AND COLUMN_NAME NOT IN ('id')), ' FROM users');
PREPARE stmt1 FROM @SQL;
EXECUTE stmt1;

11

如果您不想选择的列中包含大量数据,并且由于速度问题而不想包含它,并且您经常选择其他列,则建议您创建一个新表使用您通常不会用原始表的键选择的一个字段,然后从原始表中删除该字段。当实际需要该额外字段时,请加入表。



8

我的主要问题是加入表时获得的许多列。尽管这不是您的问题(如何从一个表中选择所有列,而是选择所有列)的答案,但我认为值得一提的是,您可以指定从特定表中获取所有列,而不仅仅是指定table.

这是一个如何可能非常有用的示例:

选择用户。*,phone.meta_value作为电话,zipcode.meta_value作为邮政编码

来自用户

左加入user_meta作为电话
on((users.user_id = phone.user_id)AND(phone.meta_key ='phone'))

左加入user_meta作为邮政编码
on((users.user_id = zipcode.user_id)AND(zipcode.meta_key ='zipcode')))

结果是用户表中的所有列,以及从元表中联接的另外两个列。


2
谢谢,我需要选择第一个表的所有列,而从第二个表中选择一个字段,所以您的回答对我有所帮助。
mohammad falahat

5

@Mahomedalid除了来自的评论中告知的这一事实之外,我还喜欢答案@Bill Karwin@Jan Koritak我所面对的问题确实是我所面对的,但是我已经找到了解决的办法,只想在这里与面临此问题的任何人分享。

我们可以使用Prepared语句的子查询中的where子句替换REPLACE函数,如下所示:

使用我的表名和列名

SET @SQL = CONCAT('SELECT ', (SELECT GROUP_CONCAT(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'users' AND COLUMN_NAME NOT IN ('id')), ' FROM users');
PREPARE stmt1 FROM @SQL;
EXECUTE stmt1;

因此,这将仅排除该字段id,而不排除company_id


4

即使您查询所有列,也要指定要查询的列是一个好习惯。

因此,我建议您在语句中写出每一列的名称(不包括不需要的列)。

SELECT
    col1
    , col2
    , col3
    , col..
    , col53

FROM table

它就像代码的契约,在查看查询时,您确切地知道可以从中提取哪些数据而无需查看表的架构。
mbillard

6
@kodecraft:出于相同的原因,这是一个好习惯,因为总是从函数返回相同的类型(即使您使用的语言未强制执行)也是一个好习惯。基本上只是“最小惊喜原则”。
Daniel Pryden 09年

4

做就是了

SELECT * FROM table WHERE whatever

然后将该列放入您喜欢的编程语言中:php

while (($data = mysql_fetch_array($result, MYSQL_ASSOC)) !== FALSE) {
   unset($data["id"]);
   foreach ($data as $k => $v) { 
      echo"$v,";
   }      
}

6
除非您要排除的列是巨大的BLOB之类。
Bill Karwin 2011年

1
如果您要避免浪费数据,这是很糟糕的。
Kristopher Ives

53中的1列应该没有任何区别。如果这样做,可能是一个错误的设计。
Petr Peller

1
SElect *是SQL反模式,切勿在生产代码中使用。
HLGEM 2015年

1
我不同意SELECT *是某种不应在生产代码中使用的反模式。与交叉引用一列很长的列表(实际上可能是不是所有字段)相比,您已经检索了所有列,这一点要清楚得多。显然,到目前为止,它的编写速度也更快。在很多情况下,表的字段将完全对应于视图或表单中的字段。
杰夫·肯德尔

4

我同意列出所有列的“简单”解决方案,但这可能很麻烦,而且错别字会浪费很多时间。我使用函数“ getTableColumns”来检索适合粘贴到查询中的列的名称。然后,我要做的就是删除不需要的内容。

CREATE FUNCTION `getTableColumns`(tablename varchar(100)) 
          RETURNS varchar(5000) CHARSET latin1
BEGIN
  DECLARE done INT DEFAULT 0;
  DECLARE res  VARCHAR(5000) DEFAULT "";

  DECLARE col  VARCHAR(200);
  DECLARE cur1 CURSOR FOR 
    select COLUMN_NAME from information_schema.columns 
    where TABLE_NAME=@table AND TABLE_SCHEMA="yourdatabase" ORDER BY ORDINAL_POSITION;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
  OPEN cur1;
  REPEAT
       FETCH cur1 INTO col;
       IF NOT done THEN 
          set res = CONCAT(res,IF(LENGTH(res)>0,",",""),col);
       END IF;
    UNTIL done END REPEAT;
  CLOSE cur1;
  RETURN res;

您的结果返回一个逗号分隔的字符串,例如...

col1,col2,col3,col4,... col53



4

是的,尽管它可能是高I / O(具体取决于表),但是这是我找到的解决方法。

SELECT *
INTO #temp
FROM table

ALTER TABLE #temp DROP COlUMN column_name

SELECT *
FROM #temp

对于任何中等大小的桌子,这将非常慢。
乔尔

3

我同意这还不足以 Select *,如果您不需要的(如其他地方提到的那样)是BLOB,则您不希望增加这种开销。

我将使用所需的数据创建一个视图,如果Select *数据库软件支持它们,则可以放心使用。否则,将大量数据放在另一个表中。


3

起初,我以为您可以使用正则表达式,但是由于我一直在阅读MYSQL文档,所以似乎不能。如果您是我,那么我将使用另一种语言(例如PHP)来生成要获取的列的列表,将其存储为字符串,然后使用该列表生成SQL。


3

我也想要这个,所以我创建了一个函数。

public function getColsExcept($table,$remove){
    $res =mysql_query("SHOW COLUMNS FROM $table");

    while($arr = mysql_fetch_assoc($res)){
        $cols[] = $arr['Field'];
    }
    if(is_array($remove)){
        $newCols = array_diff($cols,$remove);
        return "`".implode("`,`",$newCols)."`";
    }else{
        $length = count($cols);
        for($i=0;$i<$length;$i++){
            if($cols[$i] == $remove)
                unset($cols[$i]);
        }
        return "`".implode("`,`",$cols)."`";
    }
}

因此,它的工作方式是先输入表,然后输入不需要的列或数组中的列:array(“ id”,“ name”,“ whatevercolumn”)

因此,在选择中,您可以像这样使用它:

mysql_query("SELECT ".$db->getColsExcept('table',array('id','bigtextcolumn'))." FROM table");

要么

mysql_query("SELECT ".$db->getColsExcept('table','bigtextcolumn')." FROM table");

3

我同意@Mahomedalid的回答,但我不想做类似准备好的语句的操作,也不想键入所有字段,所以我的解决方案很愚蠢。

转到phpmyadmin-> sql-> select中的表,它转储查询:复制,替换并完成!:)


2

尽管我同意Thomas的答案(+1;)),但我想补充一点,我假设您希望使用的列几乎包含任何数据。如果它包含大量的文本,xml或二进制blob,则请花时间分别选择每个列。否则,您的演奏会受到影响。干杯!


2

Mahomedalid发布的答案有一个小问题:

内部替换功能代码正在替换“<columns_to_delete>, ” ”,如果要替换的字段是concat字符串中的最后一个字段,则替换存在问题,因为最后一个字段没有字符逗号“,”并且未从中删除字符串。

我的建议:

SET @sql = CONCAT('SELECT ', (SELECT REPLACE(GROUP_CONCAT(COLUMN_NAME),
                  '<columns_to_delete>', '\'FIELD_REMOVED\'')
           FROM INFORMATION_SCHEMA.COLUMNS
           WHERE TABLE_NAME = '<table>'
             AND TABLE_SCHEMA = '<database>'), ' FROM <table>');

更换<table><database>和`

在我的情况下,删除的列将替换为字符串“ FIELD_REMOVED”,因为我正尝试保护内存,所以这行得通。(我删除的字段的BLOB约为1MB)


2

基于@Mahomedalid的答案,我做了一些改进以支持“选择除mysql中的某些以外的所有列”

SET @database    = 'database_name';
SET @tablename   = 'table_name';
SET @cols2delete = 'col1,col2,col3';

SET @sql = CONCAT(
'SELECT ', 
(
    SELECT GROUP_CONCAT( IF(FIND_IN_SET(COLUMN_NAME, @cols2delete), NULL, COLUMN_NAME ) )
    FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @tablename AND TABLE_SCHEMA = @database
), 
' FROM ',
@tablename);

SELECT @sql;

如果确实有很多cols,请使用此sql更改group_concat_max_len

SET @@group_concat_max_len = 2048;

2

可能是我对扬·科里塔克(Jan Koritak)指出的差异的解决方案

SELECT CONCAT('SELECT ',
( SELECT GROUP_CONCAT(t.col)
FROM
(
    SELECT CASE
    WHEN COLUMN_NAME = 'eid' THEN NULL
    ELSE COLUMN_NAME
    END AS col 
    FROM INFORMATION_SCHEMA.COLUMNS 
    WHERE TABLE_NAME = 'employee' AND TABLE_SCHEMA = 'test'
) t
WHERE t.col IS NOT NULL) ,
' FROM employee' );

表:

SELECT table_name,column_name 
FROM INFORMATION_SCHEMA.COLUMNS 
WHERE TABLE_NAME = 'employee' AND TABLE_SCHEMA = 'test'

===============================

table_name  column_name
employee    eid
employee    name_eid
employee    sal

===============================

查询结果:

'SELECT name_eid,sal FROM employee'

1

如果始终是同一列,则可以创建一个没有该视图的视图。

否则,不,我不这么认为。


1

如果愿意,可以使用SQL生成SQL并评估它生成的SQL。这是一个通用解决方案,因为它从信息模式中提取列名。这是Unix命令行的示例。

替代

  • MYSQL与您的mysql命令
  • 带有表名的TABLE
  • EXCLUDEDFIELD,排除了字段名称
echo $(echo 'select concat("select ", group_concat(column_name) , " from TABLE") from information_schema.columns where table_name="TABLE" and column_name != "EXCLUDEDFIELD" group by "t"' | MYSQL | tail -n 1) | MYSQL

您实际上只需要以这种方式提取一次列名即可构造排除该列的列列表,然后仅使用您已构建的查询。

所以像:

column_list=$(echo 'select group_concat(column_name) from information_schema.columns where table_name="TABLE" and column_name != "EXCLUDEDFIELD" group by "t"' | MYSQL | tail -n 1)

现在,您可以$column_list在构造的查询中重用该字符串。


1

我想添加另一种观点来解决此问题,特别是如果要删除的列数较少时。

您可以使用MySQL Workbench之类的数据库工具来为您生成select语句,因此您只需手动删除生成的语句的那些列,然后将其复制到SQL脚本中即可。

在MySQL Workbench中,生成它的方式是:

右键单击表->发送到Sql编辑器->选择所有语句。


1

公认的答案有几个缺点。

  • 在表或列名称需要反引号的地方失败
  • 如果要忽略的列在列表的最后,则失败
  • 它需要两次列出表名(一次用于选择,另一次用于查询文本),这是多余的,而且不必要
  • 它可能以错误的顺序返回列名称

所有这些问题都可以通过简单地包括在反引号来克服SEPARATORGROUP_CONCAT和使用WHERE条件,而不是REPLACE()。出于我的目的(我想还有很多其他人),我希望返回的列名与其在表本身中出现的顺序相同。为此,我们ORDER BYGROUP_CONCAT()函数内部使用一个显式子句:

SELECT CONCAT(
    'SELECT `',
    GROUP_CONCAT(COLUMN_NAME ORDER BY `ORDINAL_POSITION` SEPARATOR '`,`'),
    '` FROM `',
    `TABLE_SCHEMA`,
    '`.`',
    TABLE_NAME,
    '`;'
)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE `TABLE_SCHEMA` = 'my_database'
    AND `TABLE_NAME` = 'my_table'
    AND `COLUMN_NAME` != 'column_to_omit';

0

我有一个建议,但没有解决方案。如果您的某些列具有较大的数据集,则应尝试以下操作

SELECT *, LEFT(col1, 0) AS col1, LEFT(col2, 0) as col2 FROM table

-1

我很迟才想出一个答案,坦白地说,这是我一直以来的做法,坦率地说,它比最佳答案要好和整洁100倍,我只希望有人能看到它。并发现它有用

    //create an array, we will call it here. 
    $here = array();
    //create an SQL query in order to get all of the column names
    $SQL = "SHOW COLUMNS FROM Table";
        //put all of the column names in the array
        foreach($conn->query($SQL) as $row) {
            $here[] = $row[0];
        }
    //now search through the array containing the column names for the name of the column, in this case i used the common ID field as an example
    $key = array_search('ID', $here);
    //now delete the entry
    unset($here[$key]);

也许很简洁,但是并不能回答问题:他没有询问过php,您也没有给出选择语句或他想要的结果,而只是给出了包含他想要的列的php数组。
gou13年

2
–1。这不是SQL代码,并且这里没有SELECT语句-这个词甚至不会出现一次。
Frungi

这只会使(a)您比PHP更熟悉PHP,或者(b)您喜欢将代码分布在多行上以提高可读性,从而使内容更整洁。我承认(b)点。您的方法也将不太有效,尽管在许多用例中差异很小。另请参阅有关您问题的其他评论。不值得大喊,因此建议您从原始问题中删除不适当的评论。
mc0e 2013年

-5

选择*是SQL反模式。由于许多原因,不应在生产代码中使用它:

处理需要一点时间。当事情运行数百万次时,这些微小的问题就很重要。缓慢的数据库(其中缓慢是由这种类型的草率编码导致的)是最难调整的性能。

这意味着您发送的数据可能会超出所需,这会导致服务器和网络瓶颈。如果您有内部联接,则发送超出您所需数据的机会为100%。

这会引起维护问题,尤其是当您添加了新的列,而您不想在任何地方都看到它们时。此外,如果您有新的列,则可能需要对界面进行某些操作,以确定如何处理该列。

它可以破坏视图(我知道这在SQl服务器中是正确的,在mysql中可能是正确的,也可能不是)。

如果某人足够愚蠢地以不同的顺序重建带有列的表(您不应该这样做,但是会一直发生),那么各种代码都可能会中断。例如,特别是针对插入代码,例如,突然由于您未指定城市而将城市放入address_3字段中时,数据库只能按列顺序进行。当数据类型改变时,这已经足够糟糕了,但是当交换列具有相同的数据类型时,这变得更糟了,因为您有时可能会插入一些混乱的数据来清理。您需要关心数据完整性。

如果在插入中使用它,则在一个表中而不是另一个表中添加新列时,它将中断插入。

它可能会破坏触发器。触发问题可能很难诊断。

将所有这些与添加列名所需的时间相加(哎呀,您甚至可能具有允许您在列名上拖动的界面(我知道我在SQL Server中这样做,我敢打赌有某种方法可以做这个是您用来编写mysql查询的工具。)让我们看看,“我会导致维护问题,会导致性能问题,并且会导致数据完整性问题,但是嘿,我节省了五分钟的开发时间。”在您想要的特定列中。

我还建议您阅读这本书:http ://www.amazon.com/SQL-Antipatterns-Programming-Pragmatic-Programmers-ebook/dp/B00A376BB2/ref=sr_1_1?s=digital-text& ie= UTF8& qid= 1389896688& sr=1- 1&keywords = sql + antipatterns

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.