我正在尝试使用 select 语句从某个 MySQL 表中获取除一个之外的所有列。有没有一种简单的方法可以做到这一点?
编辑:此表中有 53 列(不是我的设计)
实际上有一种方法,您当然需要拥有这样做的权限......
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>
(不要在大桌子上尝试这个,结果可能......令人惊讶!)
临时表
DROP TABLE IF EXISTS temp_tb;
CREATE TEMPORARY TABLE ENGINE=MEMORY temp_tb SELECT * FROM orig_tb;
ALTER TABLE temp_tb DROP col_a, DROP col_f,DROP col_z; #// MySQL
SELECT * FROM temp_tb;
DROP 语法可能因数据库而异@Denis Rozhnev
在这种情况下,视图会更好吗?
CREATE VIEW vwTable
as
SELECT
col1
, col2
, col3
, col..
, col53
FROM table
你可以做:
SELECT column1, column2, column4 FROM table WHERE whatever
没有得到 column3,但也许您正在寻找更通用的解决方案?
据我所知,没有。您可以执行以下操作:
SELECT col1, col2, col3, col4 FROM tbl
并手动选择所需的列。但是,如果您想要很多列,那么您可能只想执行以下操作:
SELECT * FROM tbl
并且忽略你不想要的。
在您的特定情况下,我建议:
SELECT * FROM tbl
除非你只想要几列。如果你只想要四列,那么:
SELECT col3, col6, col45, col 52 FROM tbl
会很好,但如果你想要 50 列,那么任何使查询的代码都会变得(太?)难以阅读。
如果您希望排除某个字段的值,例如出于安全考虑/敏感信息,您可以将该列检索为空。
例如
SELECT *, NULL AS salary FROM users
在尝试@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;
如果您不想选择的列中有大量数据,并且由于速度问题不想包含它并且您经常选择其他列,我建议您创建一个新表使用您通常不使用原始表的键选择的一个字段,然后从原始表中删除该字段。当实际需要该额外字段时加入表。
您可以使用DESCRIBE my_table并使用其结果动态生成 SELECT 语句。
我的主要问题是加入表格时我得到了很多列。虽然这不是您问题的答案(如何从一个表中选择除某些列之外的所有列),但我认为值得一提的是,您可以指定从特定表中获取所有列,而不仅仅是指定.table.
这是一个非常有用的示例:
选择用户。*,phone.meta_value 作为电话,zipcode.meta_value 作为邮政编码 来自用户 left join user_meta as phone on ( (users.user_id = phone.user_id) AND (phone.meta_key = 'phone') ) left join user_meta as zipcode on ( (users.user_id = zipcode.user_id) AND (zipcode.meta_key = 'zipcode') )
结果是用户表中的所有列,以及从元表中连接的另外两个列。
@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
是的,虽然它可能是高 I/O,但取决于这里的表,这是我找到的一种解决方法。
SELECT *
INTO #temp
FROM table
ALTER TABLE #temp DROP COlUMN column_name
SELECT *
FROM #temp
即使查询所有列,最好指定要查询的列。
所以我建议你在语句中写下每一列的名称(不包括你不想要的那一个)。
SELECT
col1
, col2
, col3
, col..
, col53
FROM table
我同意列出所有列的“简单”解决方案,但这可能很麻烦,而且拼写错误会浪费大量时间。我使用函数“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
我同意Select *
,如果你不需要的那个是 BLOB 是不够的,正如在别处提到的那样,你不想让开销蔓延。
我会创建一个包含所需数据的视图Select *
,然后您可以放心——如果数据库软件支持它们。否则,将庞大的数据放在另一个表中。
起初我以为你可以使用正则表达式,但是当我一直在阅读MYSQL 文档时,似乎你不能。如果我是你,我会使用另一种语言(例如 PHP)来生成你想要获取的列列表,将其存储为字符串,然后使用它来生成 SQL。
基于@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;
如果您确实有很多列,请使用此 sql 更改 group_concat_max_len
SET @@group_concat_max_len = 2048;
我同意@Mahomedalid 的回答,但我不想做类似准备好的声明,也不想输入所有字段,所以我有一个愚蠢的解决方案。
转到 phpmyadmin->sql->select 中的表,它会转储查询:复制、替换并完成!:)
虽然我同意 Thomas 的回答 (+1 ;)),但我想补充一点,我假设您不想要的列几乎不包含任何数据。如果它包含大量文本、xml 或二进制 blob,请花时间单独选择每一列。否则你的表现会受到影响。干杯!
做就是了
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,";
}
}
Mahomedalid 发布的答案有一个小问题:
内部替换功能代码是用“ <columns_to_delete>,
”替换“”,如果要替换的字段是concat字符串中的最后一个,由于最后一个没有char逗号“,”并且没有从字符串。
我的建议:
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”替换,因为我试图保护内存。(我要删除的字段是一个大约 1MB 的 BLOB)
我也想要这个,所以我创建了一个函数。
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");
也许我对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'
如果它总是相同的一列,那么您可以创建一个没有它的视图。
否则,不,我不这么认为。
如果您愿意,可以使用 SQL 生成 SQL 并评估它生成的 SQL。这是一个通用解决方案,因为它从信息模式中提取列名。这是来自 Unix 命令行的示例。
替代
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
字符串。
我想添加另一个观点来解决这个问题,特别是如果你有少量的列要删除。
您可以使用MySQL Workbench之类的数据库工具来为您生成 select 语句,因此您只需手动删除生成语句的这些列并将其复制到您的 SQL 脚本中。
在 MySQL Workbench 中,生成它的方法是:
右键单击表 -> 发送到 Sql 编辑器 -> 选择所有语句。
公认的答案有几个缺点。
所有这些问题都可以通过简单地在SEPARATOR
for your中包含反引号GROUP_CONCAT
并使用WHERE
条件而不是REPLACE()
. 出于我的目的(我想还有很多其他人的目的),我希望以与它们在表本身中出现的顺序相同的顺序返回列名。为此,我们ORDER BY
在函数内部使用了一个显式子句GROUP_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';
我有一个建议,但不是解决方案。如果您的某些列具有更大的数据集,那么您应该尝试以下操作
SELECT *, LEFT(col1, 0) AS col1, LEFT(col2, 0) as col2 FROM table
如果您使用 MySQL Workbench,您可以右键单击您的表并单击Send to sql editor
,然后Select All Statement
这将创建一个列出所有字段的语句,如下所示:
SELECT `purchase_history`.`id`,
`purchase_history`.`user_id`,
`purchase_history`.`deleted_at`
FROM `fs_normal_run_2`.`purchase_history`;
SELECT * FROM fs_normal_run_2.purchase_history;
现在你可以删除那些你不想要的。
问题是关于 MySQL 的,但我仍然认为值得一提的是,至少Google BigQuery 和 H2 原生支持一种* EXCEPT
语法,例如
SELECT * FROM actor
生产:
|actor_id|first_name|last_name |last_update |
|--------|----------|------------|-----------------------|
|1 |PENELOPE |GUINESS |2006-02-15 04:34:33.000|
|2 |NICK |WAHLBERG |2006-02-15 04:34:33.000|
|3 |ED |CHASE |2006-02-15 04:34:33.000|
然而
SELECT * EXCEPT (last_update) FROM actor
生产:
|actor_id|first_name|last_name |
|--------|----------|------------|
|1 |PENELOPE |GUINESS |
|2 |NICK |WAHLBERG |
|3 |ED |CHASE |
也许,未来版本的 MySQL 也会支持这种语法?
我很晚才找到答案,这是我一直这样做的方式,坦率地说,它比最佳答案好 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]);
Select * 是一种 SQL 反模式。它不应该在生产代码中使用,原因有很多,包括:
处理时间稍长。当事情运行数百万次时,这些微小的部分可能很重要。由这种类型的草率编码导致的缓慢数据库是最难调整性能的类型。
这意味着您发送的数据可能比您需要的多,这会导致服务器和网络瓶颈。如果你有一个内部连接,发送比你需要的更多数据的机会是 100%。
它会导致维护问题,尤其是当您添加了不想随处可见的新列时。此外,如果您有一个新列,您可能需要对界面进行一些操作以确定如何处理该列。
它可以破坏视图(我知道这在 SQl 服务器中是正确的,在 mysql 中可能是也可能不是)。
如果有人愚蠢到以不同的顺序重建列的表(你不应该这样做,但它总是会发生),那么各种代码都会中断。特别是插入代码,例如突然将城市放入 address_3 字段,因为没有指定,数据库只能按列的顺序排列。当数据类型发生变化时这已经够糟糕了,但当交换的列具有相同的数据类型时更糟糕,因为您有时可以插入糟糕的数据,而这些数据很容易清理。您需要关心数据完整性。
如果在插入中使用它,如果在一个表中添加新列而不在另一个表中添加新列,它将中断插入。
它可能会破坏触发器。触发问题可能难以诊断。
将所有这些与添加列名所需的时间相加(哎呀,您甚至可能有一个界面允许您拖动列名(我知道我在 SQL Server 中这样做,我敢打赌有一些方法可以这样做是你用来编写 mysql 查询的一些工具。)让我们看看,“我会导致维护问题,我会导致性能问题,我会导致数据完整性问题,但是我节省了五分钟的开发时间。”真的只是把在您想要的特定列中。