选择所有列除了...

70
有没有某个关系型数据库管理系统实现了像SELECT * EXCEPT这样的功能?我想要的是获取除了特定的TEXT/BLOB字段之外的所有字段,我希望只选择其他所有内容。
几乎每天我都向同事抱怨,应该有人实现这个功能...它不存在真是太糟糕了。
编辑:我理解大家对于SELECT *的担忧。我知道与SELECT *相关的风险。然而,在我的情况下,这不会用于任何生产级别的代码,甚至不是开发级别的代码;仅用于调试,当我需要轻松查看所有值时。
正如我在一些评论中所述,我们公司严格使用命令行进行工作,通过ssh完成所有操作。这使得使用任何GUI工具(不允许连接到数据库外部)等非常困难。
谢谢你们的建议。

在SQL Server中确实存在EXCEPT关键字,尽管它不是用于你在问题中想要的方式。它执行两个结果集之间的DIFFERENCE UNION,以便为您提供一个“记录”的结果集,这些记录存在于第一个结果集中,但不存在于第二个结果集中。 - Russ Cam
7
很遗憾这个东西不存在。 - VISQL
@VISQL 现在已经存在。 - Lukasz Szozda
@lad2025 你用的是哪个SQL变体?能分享一下链接吗?这非常有趣。 - VISQL
1
@VISQL 请检查我的答案 https://dev59.com/_HRC5IYBdhLWcg3wFdBx#49015504 - Lukasz Szozda
15个回答

39

正如其他人所说,不建议在查询中这样做,因为未来有人更改表结构时容易出问题。然而,有一种方法可以做到这一点...我简直不敢相信我会提出这个建议,但基于回答实际问题的精神...

使用动态SQL来完成... 这将处理除“description”列之外的所有列。你可以轻松地将其转换为函数或存储过程。

declare @sql varchar(8000),
    @table_id int,
    @col_id int

set @sql = 'select '

select @table_id = id from sysobjects where name = 'MY_Table'

select @col_id = min(colid) from syscolumns where id = @table_id and name <> 'description'
while (@col_id is not null) begin
    select @sql = @sql + name from syscolumns where id = @table_id and colid = @col_id

    select @col_id = min(colid) from syscolumns where id = @table_id and colid > @col_id and name <> 'description'
    if (@col_id is not null) set @sql = @sql + ','
    print @sql
end

set @sql = @sql + ' from MY_table'

exec @sql

18
为了回答实际问题的精神,你赢得了奖品。 - Glen Solsberry
3
我可以想到几个理由,为什么你需要这样做而不会被认为是疯了。尽管存在问题,但这是一个有趣的问题,找出答案只是好玩罢了 :) - Jasmine
3
首先,感谢您回答问题而非就该人是否应该做某事发表意见。其中一种进行这项工作的主要情境是创建视图,您希望其能够在底层表格列更改时自动更新,并将在其他选择特定列的语句中使用。 - Mark Shapiro
"exec @sql" 给我返回了 "名称 '(在此处插入我 SQL 语句变量的 644/680 个字符)' 不是有效标识符"。应该是 "exec (@sql)"。 - Rebeccah
@Jasmine,我目前正在研究如何编写触发器,以便在表格的任何列更改时触发,但除了一列之外。我真的希望所有其他列都是“动态的”,这样我就不需要针对表格结构中的每个更改都更改触发器函数。所以谢谢你回答这个问题。尽管我不太确定这在触发器中是否有效。 - exhuma
显示剩余2条评论

25
在表上创建一个视图,该视图不包括 BLOB 列。

2
+1:这很合理 - 这里也不允许使用SELECT *。 - Otávio Décio
3
如果源表发生变化,就需要修改视图。如果你打算在此使用SELECT *,最好一开始就只选择你真正需要的列。 - Brian Knoblauch
1
它可以避免你一遍又一遍地输入那些列。对于开发人员在调试时使用,这不是一个坏主意。 - Tom H
4
我在我的新款高级电脑上使用了剪切和粘贴功能。 :-) - Brian Knoblauch
如果你总是想保留那个select语句(以及其他类似的语句),那就太好了。但我桌面上已经有足够多的杂物了。而且打开文件、复制和粘贴要比执行简单的select语句更费力。 - Tom H

19

有没有任何RDBMS实现类似于SELECT * EXCEPT的功能?

是的,Google Big Query实现了SELECT * EXCEPT

SELECT * EXCEPT语句指定要从结果中排除的一个或多个列的名称。所有匹配的列名都将从输出中省略。

WITH orders AS(
  SELECT 5 as order_id,
  "sprocket" as item_name,
  200 as quantity
)
SELECT * EXCEPT (order_id)
FROM orders;

输出:

+-----------+----------+
| item_name | quantity |
+-----------+----------+
| sprocket  | 200      |
+-----------+----------+

编辑:

H2数据库还支持SELECT * EXCEPT (col1, col2, ...)语法。

通配符表达式

在SELECT语句中使用的通配符表达式。通配符表达式表示所有可见列。某些列可以使用可选的EXCEPT子句进行排除。


编辑2:

Hive支持:正则表达式列规范

在Hive 0.13.0之前的版本中,或者在配置属性hive.support.quoted.identifiers设置为none的0.13.0及以后的版本中,SELECT语句可以使用基于正则表达式的列规范。

以下查询选择除ds和hr之外的所有列。

SELECT `(ds|hr)?+.+` FROM sales

编辑3:

Snowflake现在也支持:now SELECT * EXCEPT(以及在BigQuery中等效于REPLACERENAME选项)

EXCLUDE col_name EXCLUDE(col_name,col_name,...)

当您选择所有列(SELECT *)时,指定应从结果中排除的列。

RENAME col_name AS col_alias RENAME(col_name AS col_alias,col_name AS col_alias,...)

当您选择所有列(SELECT *)时,指定应在结果中使用的列别名。


而且 Databricks SQL 也是如此(自 Runtime 11.0 起)

star_clause
   [ { table_name | view_name } . ] * [ except_clause ]

except_clause
   EXCEPT ( { column_name | field_name } [, ...] )

并且也 DuckDB

-- select all columns except the city column from the addresses table
SELECT * EXCLUDE (city) FROM addresses;
-- select all columns from the addresses table, but replace city with LOWER(city)
SELECT * REPLACE (LOWER(city) AS city) FROM addresses;
-- select all columns matching the given regex from the table
SELECT COLUMNS('number\d+') FROM addresses;

我现在生活在 T-SQL 的世界里。有些东西我真的很想念 BigQuery!这是他们的遗留版还是标准方言? - Dodecaphone
1
@Dodecaphone 这是 BigQuery 的标准方言。 - Lukasz Szozda

10

DB2允许这样做。列有一个Hidden属性/标识符。

根据syscolumns文档

HIDDEN
CHAR(1) NOT NULL WITH DEFAULT 'N'
表示该列是否被隐式隐藏:

P 部分隐藏。该列从SELECT *中隐式隐藏。

N 不隐藏。该列对所有SQL语句可见。

创建表文档在创建列的过程中,您需要指定IMPLICITLY HIDDEN修饰符

以下是Implicitly Hidden Columns的DDL示例

CREATE TABLE T1
(C1 SMALLINT NOT NULL,
C2 CHAR(10) IMPLICITLY HIDDEN,
C3 TIMESTAMP)
IN DB.TS;

未来的读者可以自行判断这种能力是否足以推动DB2的采用。


5

是的,终于有了 :) SQL标准2016定义了多态表函数

SQL:2016引入了多态表函数(PTF),它们不需要事先指定结果类型。相反,它们可以提供一个描述组件过程,在运行时确定返回类型。PTF的作者和用户都不需要事先声明返回的列。按照SQL:2016描述的方式,PTF尚未在任何经过测试的数据库中使用。感兴趣的读者可以参考ISO发布的免费技术报告“SQL中的多态表函数”。以下是报告中讨论的一些示例:CSVreader,它读取CVS文件的标题行以确定返回列的数量和名称;Pivot(实际上是unpivot),它将列组转换为行(例如:phonetype,phonenumber)--我:不再有硬编码的字符串 :);TopNplus,它通过每个分区传递N行和一行额外的总计行。
Oracle 18c 实现了这个机制。 18c Skip_col Polymorphic Table Function Example Oracle Live SQLSkip_col Polymorphic Table Function Example
本示例展示了如何根据名称/特定数据类型跳过数据。
CREATE PACKAGE skip_col_pkg AS  
  -- OVERLOAD 1: Skip by name 
  FUNCTION skip_col(tab TABLE,  col columns)  
           RETURN TABLE PIPELINED ROW POLYMORPHIC USING skip_col_pkg;  
  
  FUNCTION describe(tab IN OUT dbms_tf.table_t,   
                    col        dbms_tf.columns_t)  
           RETURN dbms_tf.describe_t;  
  
  -- OVERLOAD 2: Skip by type --  
  FUNCTION skip_col(tab       TABLE,   
                    type_name VARCHAR2,  
                    flip      VARCHAR2 DEFAULT 'False')   
           RETURN TABLE PIPELINED ROW POLYMORPHIC USING skip_col_pkg;  
  
  FUNCTION describe(tab       IN OUT dbms_tf.table_t,   
                    type_name        VARCHAR2,   
                    flip             VARCHAR2 DEFAULT 'False')   
           RETURN dbms_tf.describe_t;  
END skip_col_pkg;

和正文:

CREATE PACKAGE BODY skip_col_pkg AS  
  
/* OVERLOAD 1: Skip by name   
 * NAME:  skip_col_pkg.skip_col   
 * ALIAS: skip_col_by_name  
 *  
 * PARAMETERS:  
 * tab - The input table  
 * col - The name of the columns to drop from the output  
 *  
 * DESCRIPTION:  
 *   This PTF removes all the input columns listed in col from the output  
 *   of the PTF.  
*/   
  FUNCTION  describe(tab IN OUT dbms_tf.table_t,   
                     col        dbms_tf.columns_t)  
            RETURN dbms_tf.describe_t  
  AS   
    new_cols dbms_tf.columns_new_t;  
    col_id   PLS_INTEGER := 1;  
  BEGIN   
    FOR i IN 1 .. tab.column.count() LOOP  
      FOR j IN 1 .. col.count() LOOP  
      tab.column(i).pass_through := tab.column(i).description.name != col(j);  
        EXIT WHEN NOT tab.column(i).pass_through;  
      END LOOP;  
    END LOOP;  
  
    RETURN NULL;  
  END;  
    
 /* OVERLOAD 2: Skip by type  
 * NAME:  skip_col_pkg.skip_col   
 * ALIAS: skip_col_by_type  
 *  
 * PARAMETERS:  
 *   tab       - Input table  
 *   type_name - A string representing the type of columns to skip  
 *   flip      - 'False' [default] => Match columns with given type_name  
 *               otherwise         => Ignore columns with given type_name  
 *  
 * DESCRIPTION:  
 *   This PTF removes the given type of columns from the given table.   
*/   
  
  FUNCTION describe(tab       IN OUT dbms_tf.table_t,   
                    type_name        VARCHAR2,   
                    flip             VARCHAR2 DEFAULT 'False')   
           RETURN dbms_tf.describe_t   
  AS   
    typ CONSTANT VARCHAR2(1024) := upper(trim(type_name));  
  BEGIN   
    FOR i IN 1 .. tab.column.count() LOOP  
       tab.column(i).pass_through :=   
         CASE upper(substr(flip,1,1))  
           WHEN 'F' THEN dbms_tf.column_type_name(tab.column(i).description)
     !=typ  
           ELSE          dbms_tf.column_type_name(tab.column(i).description) 
     =typ  
         END /* case */;  
    END LOOP;  
  
    RETURN NULL;  
  END;  
  
END skip_col_pkg;  

并且样例使用:

-- skip number cols
SELECT * FROM skip_col_pkg.skip_col(scott.dept, 'number'); 

-- only number cols
SELECT * FROM skip_col_pkg.skip_col(scott.dept, 'number', flip => 'True') 

-- skip defined columns
SELECT *   
FROM skip_col_pkg.skip_col(scott.emp, columns(comm, hiredate, mgr))  
WHERE deptno = 20;

我强烈建议阅读整个示例(创建独立函数而不是包调用)。
例如,您可以轻松地重载跳过方法:跳过不以特定前缀/后缀开头/结尾的列。
相关链接:db<>fidde演示 相关文章:如何通过Chris Saxon动态更改SQL查询中的列

5

有没有一种RDBMS实现了类似于SELECT * EXCEPT的功能

是的!真正关系语言Tutorial D允许通过要删除的属性来表达投影,而不是要保留的属性,例如:

my_relvar { ALL BUT description }

事实上,它相当于SQL的SELECT *{ ALL BUT }
你对SQL的提议很有价值,但我听说它已经被用户组提交给SQL标准委员会并被供应商组拒绝 :(
它也已经被明确要求用于SQL Server,但请求被关闭为“不修复”。

2

我认为它不存在的原因是查询的作者应该(为了性能)仅请求他们要查看/需要的内容(因此知道要指定哪些列)- 如果将来有人添加了一些blob,您可能会拉回您不需要的潜在大字段。


2

在这里可以使用临时表选项,只需删除不需要的列并从更改后的临时表中选择 *。

/* Get the data into a temp table */
    SELECT * INTO #TempTable
    FROM 
    table

/* Drop the columns that are not needed */
    ALTER TABLE #TempTable
    DROP COLUMN [columnname]

SELECT * from #TempTable

1

避免使用SELECT *,这会给你带来麻烦。一定要明确指定你想要的列。事实上,“特性”并不存在,这让人感到非常清爽。


幸运的是,我们的表格几乎从不改变。我特别考虑到调试问题,其中我需要所有其他字段,除了BLOB字段。 - Glen Solsberry
在这种特定情况下,我同意这是很好的。 - Michael Haren
这里的关键词是“几乎”。此外,任何好的SQL工具都将允许您点击并选择您想要的内容,而不会用这种装置污染语言。 - Otávio Décio
@gms8994,在调试的情况下,大多数企业级RDBMS都会自动为您编写选择语句,其中包括所有列。然后只需删除您不需要的“糟糕”列,就可以开始了。 - Kon
6
这并没有回答问题。有时,即使此功能容易被滥用,它仍然非常有用,就像使用 * 一样。具体而言,当在包含身份列的表中克隆行时,我想要执行类似于 INSERT ... SELECT ALL BUT id ... 的操作时,这个功能将非常方便。 - Martin Smith
显示剩余3条评论

0

这是一个老问题,但我希望这个答案仍然对其他人有所帮助。它也可以被修改以添加多个除字段。如果您想要展开具有许多列的表,这将非常方便。

DECLARE @SQL NVARCHAR(MAX)
SELECT @SQL = COALESCE(@SQL + ', ', ' ' ) + name FROM sys.columns WHERE name <> 'colName' AND object_id = (SELECT id FROM sysobjects WHERE name = 'tblName')
SELECT @SQL = 'SELECT ' + @SQL + ' FROM ' + 'tblName'
EXEC sp_executesql  @SQL

存储过程:

usp_SelectAllExcept '表名', '列名'

ALTER PROCEDURE [dbo].[usp_SelectAllExcept]
(
  @tblName SYSNAME
 ,@exception VARCHAR(500)
)
AS

DECLARE @SQL NVARCHAR(MAX)

SELECT @SQL = COALESCE(@SQL + ', ', ' ' ) + name from sys.columns where name <> @exception and object_id = (Select id from sysobjects where name = @tblName)
SELECT @SQL = 'SELECT ' + @SQL + ' FROM ' + @tblName

EXEC sp_executesql @SQL

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接