您可以选择除 1 或 2 个字段之外的所有内容,而不会出现作家抽筋吗?

Posted

技术标签:

【中文标题】您可以选择除 1 或 2 个字段之外的所有内容,而不会出现作家抽筋吗?【英文标题】:Can you SELECT everything, but 1 or 2 fields, without writer's cramp? 【发布时间】:2012-02-03 17:50:30 【问题描述】:

是否可以在 PLSQL 中选择表中除 1 或 2 之外的所有字段,而不必指定所需的字段?

例如,员工表有以下字段:

身份证 名字 姓氏 爱好

是否还能写出类似

的查询
select * from employee

离开现场hobbies而不必写这样的东西?

select id, firstname, lastname from employee

【问题讨论】:

请不要在问题中签名(请参阅***.com/faq#signatures) 如果您经常只需要某些字段,那么您可以只在这些列上创建一个视图。 【参考方案1】:

否 - you either get all fields (*) OR specify the fields you want。

【讨论】:

有些人认为最好避免使用SELECT *,并始终指定所需的列。 @AdamHawkes 我确实认为避免 SELECT * 是最佳实践的一部分,但我不能 100% 确定这是一个普遍的观点...... @Yahia:是的,SELECT * 没有任何问题,只要你能保证没有人会修改或重新创建表结构。曾经。 /讽刺 :) 在 12c 中,您可以通过将列设置为 INVISIBLE 来从 SELECT * 中排除列。请参阅下面的答案。 当我加入一个动态生成的表时我该怎么做(我不知道列名,除了我用来加入的外键)?【参考方案2】:

如果你想避免作者的抽筋,你可以使用 SQL Developer 并让它为你生成列列表:

select column_name||','
from all_tab_columns
where table_name = 'YourTableName'

然后只取出你不想要的一两列。

你也可以使用

SELECT listagg(column_name, ',') within group (order by column_name) columns
FROM all_tab_columns
WHERE table_name = 'TABLE_NAME'
GROUP BY table_name;

【讨论】:

很高兴知道,但我希望在阅读生成的 INSERT 时避免“眼睛疲劳”以及作家抽筋:)。说真的,这很有用。 我一直都在做这个……太有用了。 Michael 是对的,但您可以像这样改进它:select wm_concat(column_name) from all_tab_cols where table_name = 'your_Table' 我很懒,这样可以节省很多打字时间!【参考方案3】:

一个旧线程,但是,是的...在 Oracle 中有一种方法:

with

employee(id, firstname, lastname, hobbies) as
(
  select 1, 'a', 'b', '1' from dual union 
  select 2, 'a', 'b', '2' from dual union 
  select 3, 'a', 'b', '3' from dual union 
  select 4, 'c', 'd', '3' from dual union 
  select 5, 'e', 'f', '2' from dual  
)

select * 
from employee 
pivot
( 
  max(1) -- fake  
  for (hobbies) -- put the undesired columns here
  IN () -- no values here...
) 
where 1=1 -- and your filters here...
order by id

要了解 PIVOT 的工作原理以及它解决问题的原因,让我们为employee 示例表举一个更好的例子:

select * 
from employee 
pivot
(
  max(id) foo,
  max(1)  bar
  for (hobbies) 
  IN ('2' as two, '3' as three)
)

这里的结果是:

名字 |姓氏 |两个_FOO |两栏 |三富 |三栏 c d null null 4 1 e f 5 1 null null 乙 2 1 3 1

使用这个更易于理解的查询可以获得完全相同的输出:

select 
  firstname,
  lastname,
  max(case when hobbies = '2' then id end) two_foo,
  max(case when hobbies = '2' then 1  end) two_bar,
  max(case when hobbies = '3' then id end) three_foo,
  max(case when hobbies = '3' then 1  end) three_bar
from employee 
group by
  firstname,
  lastname

因此,永远不会选择列 hobbies,就像列 id 一样,两者都在 PIVOT 子句中指定。所有其他列都被分组和选择。

好吧,回到第一个查询,它有两个原因: 1-您不会在分组过程中丢失任何行,因为 id 列是唯一的,并且没有为聚合指定列; 2- 当枢轴生成 N * M 新列时,其中 N = IN 子句的值数,M = 指定的聚合数,因此没有过滤器并且单个无害聚合将产生 0 * 1 = 0 个新列,并将删除 PIVOT 子句中指定的列,这只是 爱好

对评论 1 的回答

这个问题的第一行说:“...无需指定你想要的字段”。在所有其他答案中,建议的查询在 SELECT 子句中指定所需的字段,实际上我的除外。

另外,在问题标题中说“...没有作家的抽筋”。那么,识别作家抽筋的正确方法是什么?我最大的努力是预见一个好的 SQL 标准来解决这个问题并与我的答案进行比较。实际上,我认为这个“标准”可能类似于 SELECT * NOT IN ([col1], [col2], ...)

现在,我可以在两个查询中看到:

不需要的列列表; 一个IN子句; 三个字符的子句 - FORNOT;

这意味着你需要在我的方法中多写一点,因为你需要一个虚假的聚合和 PIVOT 子句......但它确实是几个字符......

【讨论】:

在帖子底部添加了我对作家抽筋的看法,因为它的评论文字有点长...... 我无法想象在很多情况下我真的想要执行此数据透视而不是写出列列表的性能开销。但这是一个迷人的 SQL 解决方案! 仅仅因为它激发了我的想象力 - 我更喜欢一个允许删除不需要的列和重命名列的标准:SELECT * REMOVE COL1, COL2 RENAME COL5 as NEW_COL FROM...(编辑到成为主题:这个解决方案可以用来重命名列以及删除吗?) 谢谢,这是一个很酷的技巧。但是,如果您想使用where 子句中的排除列之一,它不会像描述的那样工作。解决方案是像这样使用“包装器”选择:select * from (select * from ... where ...) pivot (...) 这简直就是错误报告的黄金!我必须从很多表中导出数据,同时过滤掉所有错误数据并将其导出到不同的表/视图。这种方法可以很容易地创建一个“错误”列,在该列中我会通知存在某种错误的行,然后我可以执行一般的create view correct_data as select * from (select * from mytable where error = 0 ) pivot (...) 来获取正确的数据,并使用相应的create view incorrect_data as select * from ( ... error <> 0 ... 来获取其余数据.【参考方案4】:

您是否在 Oracle 12c 上运行?

如果是,请考虑这是否满足您的需求:

alter table mytable modify column undesired_col_name INVISIBLE;

在这种情况下,undesired_col_name 列将完全可用,但它将从任何 SELECT * 语句等(例如,%ROWTYPE)中排除,就好像它不存在一样。

【讨论】:

【参考方案5】:

Oracle 18c 多态表函数可以从表中选择所有内容并排除列列表:

select * from everything_but(employee, columns(hobbies));

ID   FIRSTNAME   LASTNAME
--   ---------   --------
1    John        Smith

创建该函数需要以下包,该包复制自 Tim Hall 的网站 https://oracle-base.com/articles/18c/polymorphic-table-functions-18c。请注意,该包不包含特定于该表的任何内容 - 此解决方案适用于任何 Oracle 表。

CREATE OR REPLACE PACKAGE poly_pkg AS

  FUNCTION everything_but(tab IN TABLE,
                          col IN COLUMNS)
  RETURN TABLE PIPELINED
  ROW POLYMORPHIC USING poly_pkg;

  FUNCTION describe (tab IN OUT DBMS_TF.table_t,
                     col IN     dbms_tf.columns_t)
    RETURN DBMS_TF.describe_t;

END poly_pkg;
/


CREATE OR REPLACE PACKAGE BODY poly_pkg AS

  FUNCTION describe (tab IN OUT DBMS_TF.table_t,
                     col IN     dbms_tf.columns_t)
    RETURN DBMS_TF.describe_t
  AS
  BEGIN
    -- Loop through all the table columns.
    FOR i IN 1 .. tab.column.count() LOOP
      -- Loop through all the columns listed in the second parameter.
      FOR j IN 1 .. col.count() LOOP
        -- Set pass_through to true for any columns not in the exclude list.
        tab.column(i).pass_through := (tab.column(i).description.name != col(j));
        -- Exit inner loop if you find a column that shouldn't be included.
        EXIT WHEN NOT tab.column(i).pass_through;
      END LOOP;
    END LOOP;

    RETURN NULL;
  END;

END poly_pkg;
/

我还创建了这个简单的包装函数来给它一个更好的名字。并创建了一个简单的示例表。

CREATE OR REPLACE FUNCTION everything_but(tab IN TABLE, col in COLUMNS)
  RETURN TABLE PIPELINED
  ROW POLYMORPHIC USING poly_pkg;
/

create table employee as
select 1 id, 'John' firstname, 'Smith' lastname, 'fishing' hobbies from dual;

【讨论】:

这个话题很老,但现在应该是公认的答案。【参考方案6】:

query_generator 是一个 PL/SQL 函数,它为表(第一个参数)返回一个选择 字符串,但不包括某些列(第二个参数)。

stringlistputil.join 来自 PL/SQL Commons。

stringlist 是一个简单的字符串列表:create type StringList as table of varchar2(32767);putil.join 只是一个普通的连接函数。

create or replace function quote_list(p_list in stringlist)
return stringlist as
  v_list stringlist := stringlist();
begin
  v_list.extend(p_list.last);
  for i in p_list.first .. p_list.last loop
    v_list(i) := '''' || p_list(i) || '''';
  end loop;

  return v_list;
end;
/
show errors

create or replace function query_generator(
  p_table in varchar2,
  p_exclude in stringlist
) return varchar2 as
  v_table constant varchar2(31) := upper(p_table);
  v_exclude constant varchar2(32676) :=
    upper(putil.join(quote_list(p_exclude), ','));
  v_stmt_str constant varchar2(32676) :=
    'select column_name from all_tab_columns where table_name = ''' ||
    v_table || ''' and column_name not in (' || v_exclude ||
    ') order by column_id';
  type stmt_cur_t is ref cursor;
  v_stmt_cur stmt_cur_t;
  v_column_name varchar2(31);
  v_query varchar2(32676) := 'select ';
begin
  open v_stmt_cur for v_stmt_str;

  loop
    fetch v_stmt_cur into v_column_name;
    exit when v_stmt_cur%notfound;
    v_query := v_query || lower(v_column_name) || ', ';
  end loop;

  close v_stmt_cur;

  select rtrim(v_query, ', ') into v_query from dual;

  v_query := v_query || ' from ' || p_table || ';';

  return v_query;
end;
/
show errors

使用示例:

exec dbms_output.put_line(query_generator('all_tables', stringlist('segment_created', 'result_cache')))

【讨论】:

如果您想完全自动化此过程,您可以使用 Oracle 数据磁带执行字符串。看这个页面的例子:oracle-developer.net/display.php?id=422 这是一个非常非常强大的方法,但是它也非常复杂并且有一些问题。【参考方案7】:

OP 正在寻找的内容类似于:

SELECT * MINUS hobbies from...

避免大量输入(并使所有列名正确)的最佳方法是打开表描述并剪切并粘贴所有列名并删除不需要的列名,逗号分隔剩下的放在一两行。

它简单、快速、准确,您不会混淆下一个必须处理您的代码的人。

【讨论】:

我猜你的意思是won't confuse ;-) 这在 12 c 中不起作用,我得到 ORA-00923: FROM keyword not found where expected Uday - 我说这行不通。只需从表描述中剪切并粘贴列名即可。【参考方案8】:

创建视图:-

创建视图 view_name 为 从员工中选择 id、first_name、last_name where id in ('','','')

注意:- 这就像数据库中的虚拟表,但是它可以影响实际表中的值。

【讨论】:

【参考方案9】:
WITH O AS
(
SELECT 'SELECT ' || rtrim('NULL AS "Dummy",' || LISTAGG('"'||column_name || '"', ',' ) within group (ORDER BY COLUMN_NAME),',')|| ' FROM "'||TABLE_NAME||'"' AS SQL, TABLE_NAME  FROM USER_TAB_COLUMNS  GROUP BY (TABLE_NAME)
)
SELECT DBMS_XMLGEN.GETXMLTYPE ((SELECT REPLACE(SQL,',COLUMNNAME','') FROM O WHERE TABLE_NAME = 'TABLENAME')) FROM DUAL

【讨论】:

【参考方案10】:

这就是几十年来 SAS 在其隐式 SQL 和 DATA STEP 中提供 DROP 子句的原因。

从 myDB.mytable(drop=hobbies) t 中选择 t.*

create table /* 或查看 /mytable(drop=hobbies) as select t. from myDB.mytable t

无论有多少列,也无论“myDB”指向什么 RDMB,即 ORACLE、Teradata、SAS、DB2、Netezza 等。在向 RDMB 提交显式数据库 SQL 之前,SAS 会收集所有列名并删除您指定的列名,以编程方式创建仅包含所需列的 SELECT 语句。

【讨论】:

【参考方案11】:

这是获取允许您指定分隔符的字段列表的另一个选项:

select listagg(column_name, ', ') WITHIN GROUP (ORDER BY rownum)
from all_tab_columns
where table_name='table'

【讨论】:

【参考方案12】:

这里是解决方案...我需要除密码以外的所有列

(选择列名 ||',' 从 user_tab_columns where table_name ='USERS' and column_name 'PASSWORD')

【讨论】:

以上是关于您可以选择除 1 或 2 个字段之外的所有内容,而不会出现作家抽筋吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何更新除该组中最新项目之外的一组行

选择除基于 MySQL 中的两个字段之外的所有行

如何使用 MySQL 查询从表中选择除一列之外的所有内容? [复制]

在 BigQuery 中选择除 ARRAY_AGG + STRUCT 中的一列之外的所有内容

SQL 选择除最近日期之外的所有内容

如何在 xcode 中快速取消选择除您想要的构建目标之外的所有构建目标