SQL:SELECT一部を除くすべての列


108

SELECT特定の列を除く、テーブル内のすべての列への方法はありますか?ITは、テーブルからすべての非ブロブ列または非幾何列を選択するのに非常に便利です。

何かのようなもの:

SELECT * -the_geom FROM segments;
  • テーブルへの列の追加を変更するとクエリ結果が変更されるため、この機能が意図的にSQL標準から除外されたと聞いたことがあります。これは本当ですか?引数は有効ですか?
  • 特にPostgreSQLで回避策はありますか?

一部を除くすべての列を知りたいユースケースはどれですか?いくつかの手動クエリを実行中に画面に表示するだけですか?プログラムの一部ですか?
joanolo

2
6意味のある、短いカラムを持つテーブル(ラnameagesid長いバイナリ伴って含む、画面幅にうまくフィット)geomカラム。ジオメトリバイナリを除くすべてのフィールドを照会したいのですが、名前を1つずつ書くのは面倒です。
アダムマタン

その場合には、これはあなたが... SQLそのものよりも、対話型のクエリで使用しているツールで行うにはもっと何かあるかもしれない
joanolo

1
@joanoloプレーンPostgreSQLシェル。
アダムMatan

3
これはとても明らかです。1列または2列を表示したくない場合や、結果テーブルを画面に合わせたい場合(特にコマンドラインクライアントを使用する場合)に、1列または2列を印刷したくない場合があります。私のような構文を期待するselect (!coluns2,!column5) from sometable;
gumkins

回答:


54

このような機能は、PostgresにもSQL Standard(AFAIK)にも存在しません。これは非常に興味深い質問だと思うので、少しグーグルで調べてpostgresonline.comで興味深い記事を見つけました。

それらは、スキーマから列を直接選択するアプローチを示しています。

SELECT 'SELECT ' || array_to_string(ARRAY(SELECT 'o' || '.' || c.column_name
        FROM information_schema.columns As c
            WHERE table_name = 'officepark' 
            AND  c.column_name NOT IN('officeparkid', 'contractor')
    ), ',') || ' FROM officepark As o' As sqlstmt

そのようなことをする関数を作成できます。そのようなトピックもメーリングリストで議論されましたが、全体的なコンセンサスはほぼ同じでした:スキーマのクエリ。

他の解決策もあると確信していますが、それらはすべて、何らかの魔法のスキーマ-クエリ-fooを含むと思います。

ところで:SELECT * ...これにはパフォーマンスのペナルティがありますので注意してください


そのような関数を作成する方法は?未知のクエリを返す関数を作成する方法を見つけることができません。常に事前にテーブルを宣言する必要があります。
ePascoal

17

本当の答えは、実際にはできないということです。これは何十年もの間要求された機能であり、開発者はそれを実装することを拒否しました。

Postgresオプティマイザーは動的関数をブラックボックスと見なしているため、スキーマテーブルのクエリを提案する一般的な回答は効率的に実行できません(以下のテストケースを参照)。つまり、インデックスは使用されず、結合はインテリジェントに実行されません。m4のような何らかのマクロシステムを使用する方がはるかに良いでしょう。少なくともオプティマイザーを混乱させることはありません(しかし、それでも混乱を招く可能性があります)。

plpgsqlでの非常に単純な動的実行ではパフォーマンスがどのように低下​​するかを示す、簡単な概念実証を以下に記述しました。また、以下では汎用レコードを特定の行タイプに返す関数を強制し、列を列挙する必要があることに注意してください。したがって、このメソッドは、すべてのテーブルに対してこの関数を作り直す場合を除き、「すべて選択」では機能しません。

test=# create table atest (i int primary key);
CREATE TABLE
test=# insert into atest select generate_series(1,100000);
INSERT 0 100000

test=# create function get_table_column(name text) returns setof record as
$$
    declare r record;
    begin
    for r in execute 'select  * from ' || $1 loop
    return next r;
    end loop;
    return; 
    end; 
$$ language plpgsql; 

test=# explain analyze select i from atest where i=999999;
                                                      QUERY PLAN                                    
----------------------------------------------------------------------------------------------------
-------------------
 Index Only Scan using atest_pkey on atest  (cost=0.29..8.31 rows=1 width=4) (actual time=0.024..0.0
24 rows=0 loops=1)
   Index Cond: (i = 999999)
   Heap Fetches: 0
 Planning time: 0.130 ms
 Execution time: 0.067 ms
(5 rows)

test=# explain analyze
    select * from get_table_column('atest') as arowtype(i int) where i = 999999;
                                                        QUERY PLAN                                  
----------------------------------------------------------------------------------------------------
-----------------------
 Function Scan on get_table_column arowtype  (cost=0.25..12.75 rows=5 width=4) (actual time=92.636..
92.636 rows=0 loops=1)
   Filter: (i = 999999)
   Rows Removed by Filter: 100000
 Planning time: 0.080 ms
 Execution time: 95.460 ms
(5 rows)

直接クエリがインデックスを使用している間に関数呼び出しがテーブル全体をスキャンしたのを見ることができます(95.46ミリ秒と00.07ミリ秒)。 。


1
興味深い視点。これは間違いなくコードではなく人間のユーザー向けの機能です(または、そう願っています!)。おそらく、拡張表示(\ x on)などは、純粋にクライアントで実装され、列の省略は同様の場所で実装する必要があります。
マックスマーフィー

13

JSONBが導入された9.4以降のPostgreSQLでは、実際にはある程度可能です。(GeoJSON経由で)Googleマップで利用可能なすべての属性を表示する方法に関する同様の質問について考えていました。

ircチャネルのjohtoは、JSONBから要素を削除しようとすることを提案しました。

ここにアイデアがあります

select the_geom,
  row_to_json(foo)::jsonb - 'the_geom'::text attributes
from (
  select * from
  segments
) foo

個々の列ではなくjsonを取得しますが、まさに私が望んでいたものでした。おそらく、jsonは個々の列に拡張して戻すことができます。


うん、多分ここから何かが、私はyet-動作するようにこれをもらっていないstackoverflow.com/questions/36174881/...
chrismarx

6

あなたができる唯一の方法は、動的なSQL文を使用することです。DrColossosが書いたように、システムビューを照会し、テーブルの構造を見つけて適切なステートメントを作成するのは簡単です。

PS:テーブル構造を正確に知らない/書き込むことなく、すべて/一部の列を選択するのはなぜですか?


7
PSに関して:時々、出力を文字化けする非常に長いジオメトリ文字列を表示せずに、ジオメトリック列を持つテーブルをクエリしたいことがあります。数十個ある可能性があるため、すべての列を指定する必要はありません。
アダムマタン

したがって、動的SQLのみが、多くの入力からあなたを救うことができます:-)。
マリアン

クエリを作成するのはデータベースを設計した人であるとみなされます。:-) Excelを生成するために多くのフィールド(30を超える)を持つ古いデータベースを照会する必要があるが、デリバリしたくない機密情報を持つフィールドが1つまたは2つあるとします。
yucer

3

上記のように動的に解決することが唯一の答えですが、お勧めしません。長期的にさらに列を追加しても、そのクエリに必ずしも必要ではない場合はどうなりますか?

必要以上の列をプルし始めるでしょう。

選択が次のように挿入の一部である場合

tableA(col1、col2、col3 .. coln)に挿入します。tableBから2列を除くすべてを選択します。

列の一致が間違っているため、挿入は失敗します。

可能ですが、ほぼすべての列が必要な場合でも、書き込まれたすべての選択に対して必要なすべての列を書き込むことをお勧めします。


このアプローチは明らかにプログラム的に間違っていますが、SELECTsのコンソールクエリとしては無害で便利です。
アダムMatan

3

目標が大きなデータ値を持つ列を表示しないことでデバッグ中に画面から混乱を取り除くことである場合、次のトリックを使用できます。

(「hstore」contribパッケージがインストールされていない場合はインストールします:CREATE EXTENSION hstore;」)

col1、col2、col3を持つテーブル "test"の場合、表示する前に "col2"の値をnullに設定できます。

select (r).* from (select (test #= hstore('col2',null)) as r from test) s;

または、表示する前に2つの列をnullに設定します。

select (r).* from (select (test #= hstore('col2',null) #= hstore('col1',null)) as r from test) s;

hstoreに入力するレコードタイプを定義する必要があるため、「テスト」はテーブルである必要があります(エイリアスまたはサブセレクトは機能しません)。


3

私が発見したばかりの回避策がありますが、R内からSQLクエリを送信する必要があります。これはRユーザーに役立つかもしれません。

基本的にはdplyrパッケージには、SQL(特にPostgreSQLの)クエリーを送信し、受け入れ-(column_name)引数を。

したがって、次のように例を書くことができます。

select(segments, -(the_geom))

3

コメントあなたの動機が表示されないの利便持つことであることを説明する内容ではなく、列自体を表示しないよりも、長いコンテンツと列のを:

…場合によっては、出力を文字化けする非常に長いジオメトリ文字列を表示せずに、ジオメトリック列を持つテーブルをクエリしたいことがあります。数十個ある可能性があるため、すべての列を指定する必要はありません。

これは、長いコンテンツを置換するヘルパー関数の助けを借りて可能ですnulltext私の例では任意の列ですが、抑制したいタイプに合わせて変更します)。

create table my_table(foo integer, bar integer, baz text);
insert into my_table(foo,bar,baz) values (1,2,'blah blah blah blah blah blah'),(3,4,'blah blah');
select * from my_table;
foo | バー| バズ                          
-:| -:| :----------------------------
  1 | 2 | 何とか何とか何とか何とか何とか何とか
  3 | 4 | 何とか                    
create function f(ttype anyelement) returns setof anyelement as
$$
declare
  toid oid;
  tname text;
  nname text;
  cols text;
begin
  --
  select pg_type.oid, pg_namespace.nspname, pg_type.typname
  into toid, nname, tname
  from pg_type join pg_namespace on pg_namespace.oid=pg_type.typnamespace
  where pg_type.oid=pg_typeof(ttype);
  --
  select string_agg((case when data_type<>'text' 
                          then column_name 
                          else 'null::'||data_type||' "'||column_name||'"' end)
                   ,', ' order by ordinal_position)
  into cols
  from information_schema.columns 
  where table_schema=nname and table_name=tname;
  --
  return query execute 'select '||cols||' from '||nname||'.'||tname;
  --
end
$$ language plpgsql;
select * from f(null::my_table);
foo | バー| バズ
-:| -:| :---
  1 | 2 | null 
  3 | 4 | ヌル

ここに dbfiddle


2
  • アプリケーションの観点から見ると、これは怠solutionなソリューションです。アプリケーションが新しい列をどうするかを自動的に知ることはまずありません。

    データブラウザアプリケーションは、データのメタデータをクエリし、実行中のクエリから列を除外するか、列のデータのサブセットを選択します。新しいBLOBは、追加時に除外できます。特定の行のBLOBデータは、オンデマンドで選択できます。

  • 動的クエリをサポートするSQLバリアントでは、テーブルメタデータのクエリを使用してクエリを構築できます。あなたの意図のために、名前ではなくタイプに基づいて列を除外します。


2

*SQL-VIEWSには表示されません...で確認 \d any_viewしてくださいpsql。内部表現には(内省的)前処理があります。


ここでのすべての議論は問題の提案(質問と議論で暗黙的)がプログラマにとって構文の砂糖であり、実際の「SQL最適化の問題」ではないことを示しています。

だから、「として実装することができ、プリパース ...あなたとSQL-VIEWを宣言するときにPostgreSQLが何を参照してください。イントロスペクションで」SELECT *VIEW-コンストラクタは変換:*イントロスペクションによって(すべての列のリストに、あなたが実行した瞬間にCREATE VIEW source-code)。

CREATE VIEWおよびPREPAREの実装

それは実行可能な実装です。tフィールドを持つテーブルを想定します(id serial, name text, the_geom geom)

CREATE VIEW t_full AS SELECT * FROM t;
-- is transformed into SELECT id,name,the_geom FROM t;

CREATE VIEW t_exp_geom AS SELECT * -the_geom FROM t;
-- or other syntax as EXCEPT the_geom
-- Will be transformed into SELECT id,name FROM t;

PREPAREステートメントについても同じです。

...だから、それは可能であり、それはプログラマの80%が必要とするものであり、PREPAREとVIEWSのための構文シュガーです!


注:もちろん、実行可能な構文は、おそらくではありません- column_namePostgreSQLのいくつかの競合がある場合は、ので、我々は提案することができEXCEPT column_name
EXCEPT (column_name1, column_name2, ..., column_nameN)または他の。


1

これは、1つの列をすべて選択するための私の機能です。postgresonline.compostgresql tuturialおよび他のソースからのアイデアを組み合わせました。

CREATE TABLE phonebook(phone VARCHAR(32), firstname VARCHAR(32),
lastname VARCHAR(32), address VARCHAR(64));
INSERT INTO phonebook(phone, firstname, lastname, address) 
VALUES ('+1 123 456 7890', 'John', 'Doe', 'North America'), 
('+1 321 456 7890', 'Matti', 'Meikeläinen', 'Finland'), 
('+1 999 456 7890', 'Maija', 'Meikeläinen', 'Finland'), 
('+9 123 456 7890', 'John', 'Doe', 'Canada'), 
('+1 123 456 7890', 'John', 'Doe', 'Sweden'), 
('+1 123 456 7890', 'John', 'Doe2', 'North America');

drop function all_except_one(text,text);
CREATE OR REPLACE FUNCTION all_except_one(to_remove TEXT, table_name1 TEXT) 
RETURNS void AS $$

 DECLARE 
 rec_row RECORD;
 curs1 refcursor ;

 BEGIN
  --print column names:
  raise notice '%', ('|'|| ARRAY_TO_STRING(ARRAY(SELECT 
  COLUMN_NAME::CHAR(20) FROM INFORMATION_SCHEMA.COLUMNS WHERE
  TABLE_NAME=table_name1 AND COLUMN_NAME NOT IN (to_remove) ), 
  '|') ||'|') ; 

  OPEN curs1 FOR
  EXECUTE 'select table_1  from (SELECT ' || ARRAY_TO_STRING(ARRAY(
  SELECT COLUMN_NAME::VARCHAR(50) FROM INFORMATION_SCHEMA.COLUMNS 
  WHERE TABLE_NAME=table_name1 AND COLUMN_NAME NOT IN (to_remove)    
  ), ', ') || ' FROM ' || table_name1 || ' limit 30)   table_1 ';

  LOOP
  -- fetch row into the rec_row
  FETCH curs1 INTO rec_row;

  -- exit when no more row to fetch
  EXIT WHEN NOT FOUND;

  -- build and print the row output

  raise notice '%',(select'| '|| regexp_replace( array_to_string(
  array_agg(a::char(20)),'|'),'["\(.*\)]+',   '','g') ||'|'  from 
  unnest(string_to_array(replace(replace(replace(trim(rec_row::text,
  '()'),'"',''), ', ','|'),')',' '),',')) as a);

  END LOOP;

  -- Close the cursor

  CLOSE curs1;

  END; $$ LANGUAGE plpgsql;

select  all_except_one('phone','phonebook');

--output:
--NOTICE:  |firstname           |lastname            |address             |
--NOTICE:  | John               |Doe                 |North America       |
--NOTICE:  | Matti              |Meikeläinen         |Finland             |
--NOTICE:  | Maija              |Meikeläinen         |Finland             |
--NOTICE:  | John               |Doe                 |Canada              |
--NOTICE:  | John               |Doe                 |Sweden              |
--NOTICE:  | John               |Doe2                |North America       |
-- all_except_one 
-- ----------------
-- (1 row)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.