SQL: CHỌN Tất cả các cột ngoại trừ một số cột


108

Có cách nào cho SELECTtất cả các cột trong một bảng, ngoại trừ các cột cụ thể không? CNTT sẽ rất thuận tiện cho việc chọn tất cả các cột không phải là blob hoặc phi hình học từ một bảng.

Cái gì đó như:

SELECT * -the_geom FROM segments;
  • Tôi đã từng nghe rằng chức năng này được cố tình loại trừ khỏi tiêu chuẩn SQL vì việc thay đổi thêm cột vào bảng sẽ thay đổi kết quả truy vấn. Điều này có đúng không? Đối số có hợp lệ không?
  • Có một cách giải quyết, đặc biệt là trong PostgreSQL?

Đó là trường hợp sử dụng mà bạn muốn biết tất cả các cột ngoại trừ một số cột? Có phải chỉ để hiển thị trên màn hình trong khi thực hiện một số truy vấn thủ công? Đây có phải là một phần của chương trình không?
joanolo

2
Một bảng với 6 ý nghĩa, cột ngắn (a-la name, age, sid) mà vừa vặn vào độ rộng màn hình, alongwith một nhị phân dài geomcột. Tôi muốn truy vấn tất cả các trường ngoại trừ nhị phân hình học và viết tên của chúng từng cái một là tẻ nhạt.
Adam Matan

Trong trường hợp đó, đây có thể là một cái gì đó liên quan đến công cụ bạn đang sử dụng với truy vấn tương tác hơn là với chính SQL ...
joanolo

1
@joanolo Vỏ đồng bằng PostgreSQL.
Adam Matan

3
Điều này có vẻ quá rõ ràng. Đôi khi bạn không muốn in một hoặc hai cột vì chúng không thú vị hoặc bạn chỉ muốn bảng kết quả vừa với màn hình (đặc biệt là nếu máy khách dòng lệnh được sử dụng). Tôi mong đợi một cú pháp nhưselect (!coluns2,!column5) from sometable;
kẹo cao su

Câu trả lời:


54

Một tính năng như vậy tồn tại trong cả Postgres và SQL Standard (AFAIK). Tôi nghĩ rằng đây là một câu hỏi khá thú vị vì vậy tôi đã googled một chút và tình cờ thấy một bài viết thú vị trên postgresonline.com .

Chúng hiển thị một cách tiếp cận chọn các cột trực tiếp từ lược đồ:

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

Bạn có thể tạo một chức năng làm một cái gì đó như thế. Các chủ đề như vậy cũng đã được thảo luận trong danh sách gửi thư, nhưng sự đồng thuận chung là khá giống nhau: truy vấn lược đồ.

Tôi chắc chắn rằng có những giải pháp khác nhưng tôi nghĩ tất cả chúng sẽ liên quan đến một loại lược đồ ma thuật-queriying-foo.

BTW: Hãy cẩn thận SELECT * ...vì điều này có thể có hình phạt hiệu suất


Làm thế nào để tạo chức năng như vậy? Tôi không thể tìm thấy bất kỳ cách nào để tạo một hàm trả về một truy vấn không xác định mà tôi luôn phải khai báo trước.
ePas Than

17

Câu trả lời thực sự là bạn không thể thực tế. Đây là một tính năng được yêu cầu trong nhiều thập kỷ và các nhà phát triển từ chối thực hiện nó.

Câu trả lời phổ biến đề xuất truy vấn các bảng lược đồ sẽ không thể chạy hiệu quả vì trình tối ưu hóa Postgres coi các hàm động là một hộp đen (xem trường hợp thử nghiệm bên dưới). Điều đó có nghĩa là các chỉ mục sẽ không được sử dụng và việc tham gia sẽ không được thực hiện một cách thông minh. Bạn sẽ tốt hơn nhiều với một số loại hệ thống macro như m4. Ít nhất nó sẽ không gây nhầm lẫn cho trình tối ưu hóa (nhưng nó vẫn có thể làm bạn bối rối.) Không cần tự mình viết mã và tự viết tính năng hoặc sử dụng giao diện ngôn ngữ lập trình mà bạn đang mắc kẹt.

Tôi đã viết một bằng chứng đơn giản về khái niệm dưới đây cho thấy hiệu suất sẽ tệ như thế nào với một thực thi động rất đơn giản trong plpgsql. Cũng lưu ý rằng bên dưới tôi phải ép buộc một hàm trả về một bản ghi chung thành một loại hàng cụ thể và liệt kê các cột. Vì vậy, phương pháp này sẽ không hoạt động cho 'chọn tất cả trừ' trừ khi bạn muốn làm lại chức năng này cho tất cả các bảng của mình.

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)

Như bạn có thể thấy lệnh gọi quét toàn bộ bảng trong khi truy vấn trực tiếp sử dụng chỉ mục ( 95,46 ms so với 00,07ms .) Các loại hàm này sẽ chứa bất kỳ loại truy vấn phức tạp nào cần sử dụng chỉ mục hoặc nối các bảng theo đúng thứ tự .


1
Quan điểm thú vị. Đây chắc chắn là một tính năng cho người dùng chứ không phải mã (hoặc vì vậy tôi nên hy vọng!) Để tôi có thể thấy quan điểm về việc khiến khách hàng chịu trách nhiệm. Có lẽ những thứ như màn hình mở rộng (\ x bật) được triển khai hoàn toàn trong máy khách và bỏ qua các cột nên được thực hiện ở một nơi tương tự.
Max Murphy

13

Điều này thực sự có thể xảy ra với PostgreQuery bắt đầu từ 9,4 nơi JSONB được giới thiệu. Tôi đã suy nghĩ về câu hỏi tương tự về cách hiển thị tất cả các thuộc tính có sẵn trong Google Map (thông qua GeoJSON).

johto trên kênh irc đề nghị thử xóa phần tử khỏi JSONB.

Đây là ý tưởng

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

Trong khi bạn nhận được json thay vì các cột riêng lẻ, đó là chính xác những gì tôi muốn. Có lẽ json có thể được mở rộng trở lại thành các cột riêng lẻ.


Vâng, có thể là một cái gì đó từ đây, nhưng tôi vẫn chưa làm được điều này - stackoverflow.com/questions/36174881/
Lần

6

Cách duy nhất bạn có thể (không nói rằng bạn nên làm) là sử dụng các câu lệnh sql động. Thật dễ dàng (như DrColossos đã viết) để truy vấn các khung nhìn hệ thống và tìm cấu trúc của bảng và xây dựng các câu lệnh thích hợp.

PS: Tại sao bạn muốn chọn tất cả / một số cột mà không biết / ghi chính xác cấu trúc bảng của mình?


7
Về PS của bạn: Đôi khi tôi muốn truy vấn một bảng có cột hình học, mà không hiển thị chuỗi hình học rất dài cắt xén đầu ra. Tôi không muốn chỉ định tất cả các cột, vì có thể có hàng tá.
Adam Matan

Vì vậy, chỉ có sql động có thể giúp bạn tiết kiệm rất nhiều từ gõ :-).
Mary

Mọi người đều cho rằng người thực hiện truy vấn là người đã thiết kế cơ sở dữ liệu. :-) Giả sử rằng bạn cần truy vấn cơ sở dữ liệu cũ có nhiều trường (hơn 30) để tạo excel, nhưng có một hoặc hai trường có thông tin nhạy cảm mà bạn không muốn cung cấp.
yucer

3

Động như đã nêu ở trên là câu trả lời duy nhất nhưng tôi sẽ không đề xuất nó. Điều gì xảy ra nếu bạn thêm nhiều cột trong thời gian dài nhưng chúng không nhất thiết phải có cho truy vấn đó?

Bạn sẽ bắt đầu kéo nhiều cột hơn bạn cần.

Điều gì xảy ra nếu lựa chọn là một phần của phần chèn như trong

Chèn vào bảngA (col1, col2, col3 .. coln) Chọn mọi thứ trừ 2 cột TỪ bảngB

Trận đấu cột sẽ sai và chèn của bạn sẽ thất bại.

Điều đó là có thể nhưng tôi vẫn khuyên bạn nên viết mọi cột cần thiết cho mỗi lựa chọn được viết ngay cả khi gần như mọi cột được yêu cầu.


Cách tiếp cận này rõ ràng là sai về mặt lập trình, nhưng nó vô hại và hữu ích như một truy vấn bàn điều khiển cho SELECTs.
Adam Matan

3

Nếu mục tiêu của bạn là loại bỏ sự lộn xộn khỏi màn hình trong quá trình gỡ lỗi bằng cách không hiển thị các cột có giá trị dữ liệu lớn, thì bạn có thể sử dụng mẹo sau:

(cài đặt gói đóng góp "hstore" nếu bạn chưa có nó: " CREATE EXTENSION hstore;")

Đối với bảng "kiểm tra" với col1, col2, col3, bạn có thể đặt giá trị của "col2" thành null trước khi hiển thị:

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

Hoặc, đặt hai cột thành null trước khi hiển thị:

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

lưu ý là "kiểm tra" phải là một bảng (bí danh hoặc tập hợp con sẽ không hoạt động) vì loại hồ sơ cho ăn vào kho phải được xác định.


3

Có một cách giải quyết mà tôi vừa phát hiện ra, nhưng nó yêu cầu gửi các truy vấn SQL từ bên trong R. Nó có thể được sử dụng cho người dùng R.

Về cơ bản dplyrgói gửi các truy vấn SQL (và cụ thể là PostgreSQL) và chấp nhận -(column_name)đối số.

Vì vậy, ví dụ của bạn có thể được viết như sau:

select(segments, -(the_geom))

3

Trong một bình luận, bạn giải thích rằng động cơ của bạn là để thuận tiện không hiển thị nội dung của các cột có nội dung dài, thay vì không hiển thị chính cột đó:

Đôi khi tôi muốn truy vấn một bảng có cột hình học, mà không hiển thị chuỗi hình học rất dài bị cắt xén đầu ra. Tôi không muốn chỉ định tất cả các cột, vì có thể có hàng tá.

Điều này là có thể, với sự trợ giúp của chức năng trợ giúp thay thế nội dung dài bằng null(bất kỳ textcột nào trong ví dụ của tôi, nhưng bạn sẽ sửa đổi điều đó cho các loại bạn muốn loại bỏ):

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 | thanh | baz                          
-: | -: | : ----------------------------
  1 | 2 | blah blah blah blah blah blah
  3 | 4 | blah blah                    
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 | thanh | baz
-: | -: | : ---
  1 | 2 | null 
  3 | 4 | vô giá trị

dbfiddle ở đây


2
  • Từ góc độ ứng dụng, đây là một giải pháp lười biếng. Một ứng dụng không có khả năng tự động biết phải làm gì với (các) cột mới.

    Các ứng dụng trình duyệt dữ liệu có thể truy vấn siêu dữ liệu cho dữ liệu và loại trừ các cột khỏi các truy vấn đang chạy hoặc chọn một tập hợp con của dữ liệu của cột. BLOB mới có thể được loại trừ khi thêm. Dữ liệu BLOB cho các hàng cụ thể có thể được chọn theo yêu cầu.

  • Trong bất kỳ biến thể SQL nào hỗ trợ truy vấn động, truy vấn có thể được xây dựng bằng cách sử dụng truy vấn trên dữ liệu meta của bảng. Đối với mục đích của bạn, tôi sẽ loại trừ các cột dựa trên loại chứ không phải tên.


2

Bạn không bao giờ thấy *trong SQL-VIEWS ... kiểm tra \d any_viewtại của bạn psql. Có một tiền xử lý (hướng nội) cho biểu diễn bên trong.


Tất cả các cuộc thảo luận ở đây cho thấy rằng các đề nghị vấn đề (ngầm trong câu hỏi và thảo luận) là một đường cú pháp cho các lập trình viên, không phải là một thực "tối ưu hóa SQL vấn đề" ... Vâng, tôi đoán, nó là dành cho 80% của lập trình viên.

Vì vậy, có thể được triển khai dưới dạng " phân tích cú pháp trước với phần hướng nội" ... Xem PostgreQuery làm gì khi bạn khai báo SQL-VIEW với SELECT *: hàm tạo VIEW chuyển đổi *thành một danh sách tất cả các cột (bằng cách hướng nội và tại thời điểm bạn chạy TẠO XEM mã nguồn).

Triển khai để TẠO XEM và CHUẨN BỊ

Đó là một thực hiện khả thi. Bảng giả sử tvới các trường (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;

Tương tự cho câu lệnh PREPARE .

... Vì vậy, điều đó là có thể, và đó là điều mà 80% lập trình viên cần, một cú pháp đường cho CHUẨN BỊ và XEM!


LƯU Ý: tất nhiên các cú pháp khả thi có lẽ không phải là - column_name, nếu có một số mâu thuẫn trong PostgreSQL, vì vậy chúng tôi có thể gợi ý EXCEPT column_name,
EXCEPT (column_name1, column_name2, ..., column_nameN)hoặc khác.


1

Đây là chức năng của tôi để chọn tất cả các cột mong đợi một. Tôi đã kết hợp các ý tưởng từ postgresonline.compostgresql tuturial và từ các nguồn khác.

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.