SQL: SELECT คอลัมน์ทั้งหมดยกเว้นบางคอลัมน์


108

มีวิธีSELECTใดคอลัมน์ทั้งหมดในตารางยกเว้นคอลัมน์ที่เฉพาะเจาะจง มันจะสะดวกมากสำหรับการเลือกคอลัมน์ที่ไม่ใช่หยดหรือไม่ใช่รูปทรงเรขาคณิตทั้งหมดจากตาราง

สิ่งที่ต้องการ:

SELECT * -the_geom FROM segments;
  • ฉันเคยได้ยินว่าฟังก์ชั่นนี้ถูกยกเว้นโดยเจตนาจากมาตรฐาน SQLเพราะการเปลี่ยนการเพิ่มคอลัมน์ลงในตารางจะเปลี่ยนผลการสืบค้น มันเป็นเรื่องจริงเหรอ? อาร์กิวเมนต์นั้นใช้ได้หรือไม่
  • มีวิธีแก้ปัญหาเฉพาะอย่างยิ่งใน PostgreSQL หรือไม่

กรณีการใช้งานใดที่คุณต้องการทราบคอลัมน์ทั้งหมดยกเว้นบางคอลัมน์ มันเป็นเพียงการแสดงบนหน้าจอในขณะที่ทำแบบสอบถามด้วยตนเอง? มันเป็นส่วนหนึ่งของโปรแกรมหรือไม่?
joanolo

2
ตารางที่มี 6 ความหมายคอลัมน์สั้น (a-La name, age, sid) ที่เหมาะกับอย่างเป็นความกว้างหน้าจอ alongwith ไบนารียาวgeomคอลัมน์ ฉันต้องการสอบถามทุกฟิลด์ยกเว้นเรขาคณิตไบนารีและการเขียนชื่อทีละตัวนั้นน่าเบื่อ
Adam Matan

ในกรณีนี้สิ่งนี้อาจจะเกี่ยวข้องกับเครื่องมือที่คุณใช้กับคิวรีแบบโต้ตอบมากกว่า SQL เอง ...
joanolo

1
@joanolo เปลือก PostgreSQL ธรรมดา
Adam Matan

3
มันดูชัดเจนมาก บางครั้งคุณไม่ต้องการที่จะพิมพ์หนึ่งหรือสองคอลัมน์เพราะพวกเขาไม่น่าสนใจหรือคุณเพียงต้องการตารางผลลัพธ์ให้พอดีกับหน้าจอ (โดยเฉพาะอย่างยิ่งถ้าลูกค้าใช้บรรทัดคำสั่ง) ฉันคาดว่าจะมีรูปแบบดังนี้select (!coluns2,!column5) from sometable;
gumkins

คำตอบ:


54

คุณลักษณะดังกล่าวมีอยู่ในทั้ง Postgres หรือ SQL Standard (AFAIK) ผมคิดว่านี่เป็นคำถามที่น่าสนใจมากดังนั้นฉัน googled นิด ๆ หน่อย ๆ และมาข้ามบทความที่น่าสนใจเกี่ยวกับ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

คุณสามารถสร้างฟังก์ชั่นที่ทำสิ่งนั้นได้ หัวข้อดังกล่าวยังถูกกล่าวถึงในรายการส่งเมล แต่ฉันทามติโดยรวมก็ค่อนข้างเหมือนกัน: เคียวรีสกีมา

ฉันแน่ใจว่ามีวิธีแก้ปัญหาอื่น ๆ แต่ฉันคิดว่าพวกเขาทั้งหมดจะเกี่ยวข้องกับเวทมนต์สคีมา

BTW: ระวังให้ดีSELECT * ...เพราะอาจมีการลงโทษได้


วิธีการสร้างฟังก์ชั่นดังกล่าว? ฉันไม่สามารถหาวิธีสร้างฟังก์ชันที่ส่งกลับแบบสอบถามที่ไม่รู้จักฉันจะต้องประกาศตารางล่วงหน้าเสมอ
ePascoal

17

คำตอบที่แท้จริงคือคุณไม่สามารถทำได้จริง นี่เป็นคุณสมบัติที่ขอมานานหลายทศวรรษและนักพัฒนาปฏิเสธที่จะใช้มัน

คำตอบที่ได้รับความนิยมซึ่งแนะนำให้ทำการสอบถามตาราง schema จะไม่สามารถทำงานได้อย่างมีประสิทธิภาพเนื่องจากเครื่องมือเพิ่มประสิทธิภาพ 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 ms เทียบกับ 00.07ms ) ฟังก์ชั่นเหล่านี้จะแท็งก์ที่ซับซ้อนใด ๆ ที่จำเป็นต้องใช้ดัชนีหรือเข้าร่วมตารางในลำดับที่ถูกต้อง .


1
มุมมองที่น่าสนใจ นี่เป็นคุณลักษณะสำหรับผู้ใช้ที่เป็นมนุษย์แทนที่จะเป็นรหัส (หรืออย่างนั้นฉันควรหวัง!) ดังนั้นฉันสามารถเห็นจุดที่ทำให้ลูกค้าต้องรับผิดชอบ สันนิษฐานว่าสิ่งต่าง ๆ เช่นการขยายการแสดงผล (\ x บน) มีการใช้งานอย่างหมดจดในไคลเอนต์และคอลัมน์ควรละเว้นการดำเนินการในสถานที่ที่คล้ายกัน
Max Murphy

13

จริงๆแล้วมันค่อนข้างเป็นไปได้ด้วย PostgreSQL ที่เริ่มต้นด้วย 9.4 ซึ่ง JSONB ถูกนำเสนอ ฉันไตร่ตรองเกี่ยวกับคำถามที่คล้ายกันเกี่ยวกับวิธีแสดงแอตทริบิวต์ที่มีอยู่ทั้งหมดใน Google Map (ผ่าน GeoJSON)

johto บนช่อง irc แนะนำให้ลองลบองค์ประกอบออกจาก JSONB

นี่คือความคิด

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

ในขณะที่คุณได้ json แทนที่จะเป็นคอลัมน์เดี่ยวมันเป็นสิ่งที่ฉันต้องการ บางที json สามารถขยายกลับเข้าไปในแต่ละคอลัมน์


ใช่บางทีบางอย่างจากที่นี่ แต่ฉันยังไม่ได้รับในการทำงาน
chrismarx

6

วิธีเดียวที่คุณจะทำได้ (อย่าบอกว่าควรทำ) คือใช้คำสั่ง sql แบบไดนามิก เป็นเรื่องง่าย (เช่น DrColossos wrote) เพื่อสืบค้นมุมมองระบบและค้นหาโครงสร้างของตารางและสร้างคำสั่งที่เหมาะสม

PS: ทำไมคุณต้องการเลือกคอลัมน์ทั้งหมด / บางคอลัมน์โดยไม่ทราบ / เขียนโครงสร้างตารางของคุณอย่างแม่นยำ


7
เกี่ยวกับ PS ของคุณ: บางครั้งฉันต้องการสืบค้นตารางที่มีคอลัมน์รูปทรงเรขาคณิตโดยไม่แสดงสตริงรูปทรงเรขาคณิตที่ยาวมากซึ่งทำให้อ่านไม่ออก ฉันไม่ต้องการระบุคอลัมน์ทั้งหมดเพราะอาจมีหลายสิบ
Adam Matan

ดังนั้น sql แบบไดนามิกเท่านั้นที่สามารถช่วยคุณประหยัดจากการพิมพ์ :-)
Marian

ทุกคนคิดว่าผู้ที่สร้างแบบสอบถามนั้นเป็นผู้ออกแบบฐานข้อมูล :-) สมมติว่าคุณต้องการสืบค้นฐานข้อมูลเก่าที่มีฟิลด์จำนวนมาก (มากกว่า 30) เพื่อสร้าง excel แต่มีหนึ่งหรือสองฟิลด์ที่มีข้อมูลที่ละเอียดอ่อนซึ่งคุณไม่ต้องการส่งมอบ
yucer

3

แบบไดนามิกตามที่ระบุไว้ข้างต้นเป็นคำตอบเดียว แต่ฉันจะไม่แนะนำ ถ้าคุณเพิ่มคอลัมน์เพิ่มเติมในระยะยาว แต่ไม่จำเป็นต้องมีสำหรับแบบสอบถามนั้น

คุณจะเริ่มดึงคอลัมน์มากกว่าที่คุณต้องการ

จะเกิดอะไรขึ้นถ้าตัวเลือกเป็นส่วนหนึ่งของส่วนแทรกเช่น

แทรกลงใน tableA (col1, col2, col3 .. coln) เลือกทุกอย่างยกเว้น 2 คอลัมน์จาก tableB

การจับคู่คอลัมน์จะผิดและการแทรกของคุณจะล้มเหลว

เป็นไปได้ แต่ฉันยังคงแนะนำให้เขียนทุกคอลัมน์ที่จำเป็นสำหรับทุกการเลือกที่เขียนแม้ว่าจะต้องมีเกือบทุกคอลัมน์


เห็นได้ชัดว่าวิธีการนี้มีความผิดพลาดในเชิงโปรแกรม แต่ไม่เป็นอันตรายและมีประโยชน์ในฐานะที่เป็นคำสั่งคอนโซลสำหรับSELECTs
Adam Matan

3

หากเป้าหมายของคุณคือการลบความยุ่งเหยิงออกจากหน้าจอระหว่างการดีบักโดยไม่แสดงคอลัมน์ที่มีค่าข้อมูลจำนวนมากคุณสามารถใช้เคล็ดลับต่อไปนี้:

(ติดตั้งแพคเกจ contrib "hstore" หากคุณยังไม่มี: " CREATE EXTENSION hstore;")

สำหรับตาราง "ทดสอบ" ด้วย col1, col2, col3 คุณสามารถตั้งค่าของ "col2" เป็นโมฆะก่อนที่จะแสดง:

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

หรือตั้งค่าสองคอลัมน์เป็น null ก่อนแสดง:

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

caveats คือ "การทดสอบ" จะต้องเป็นตาราง (นามแฝงหรือการเลือกย่อยจะไม่ทำงาน) เนื่องจากต้องกำหนดประเภทการบันทึกลงใน hstore


3

มีวิธีแก้ปัญหาที่ฉันเพิ่งค้นพบ แต่มันต้องส่งแบบสอบถาม SQL จากภายใน R มันอาจจะใช้กับผู้ใช้ R

โดยทั่วไปdplyrแพคเกจส่งแบบสอบถาม SQL (และ PostgreSQL โดยเฉพาะ) และยอมรับ-(column_name)อาร์กิวเมนต์

ดังนั้นตัวอย่างของคุณสามารถเขียนได้ดังนี้:

select(segments, -(the_geom))

3

ในความคิดเห็นที่คุณอธิบายว่าแรงจูงใจของคุณคือการมีความสะดวกในการไม่แสดงเนื้อหาของคอลัมน์ที่มีเนื้อหายาวแทนที่จะไม่แสดงคอลัมน์เอง:

…บางครั้งฉันต้องการสืบค้นตารางที่มีคอลัมน์รูปทรงเรขาคณิตโดยไม่แสดงสตริงรูปทรงเรขาคณิตที่ยาวมากซึ่งทำให้อ่านไม่ออก ฉันไม่ต้องการระบุคอลัมน์ทั้งหมดเพราะอาจมีหลายสิบ

สิ่งนี้เป็นไปได้ด้วยความช่วยเหลือของฟังก์ชั่นตัวช่วยซึ่งแทนที่เนื้อหาที่มีความยาวด้วยnull( textคอลัมน์ใด ๆในตัวอย่างของฉัน แต่คุณจะแก้ไขมันสำหรับประเภทที่คุณต้องการระงับ):

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 | บาร์ | 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 | บาร์ | baz
-: | -: | : ---
  1 | 2 | null 
  3 | 4 | โมฆะ

dbfiddle ที่นี่


2
  • จากมุมมองของแอปพลิเคชันนี่เป็นโซลูชันที่ขี้เกียจ แอปพลิเคชันไม่น่าจะรู้ว่าจะทำอย่างไรกับคอลัมน์ใหม่โดยอัตโนมัติ

    แอปพลิเคชันเบราว์เซอร์ข้อมูลอาจสืบค้นข้อมูลเมตาของข้อมูลและแยกคอลัมน์ออกจากแบบสอบถามที่กำลังเรียกใช้หรือเลือกชุดย่อยของข้อมูลคอลัมน์ BLOB ใหม่สามารถยกเว้นได้เมื่อมีการเพิ่ม ข้อมูล BLOB สำหรับแถวเฉพาะสามารถเลือกได้ตามต้องการ

  • ในตัวแปร SQL ใด ๆ ที่สนับสนุนเคียวรีแบบไดนามิกสามารถสร้างเคียวรีโดยใช้เคียวรีบนข้อมูลเมตาของตาราง สำหรับเจตนาของคุณฉันจะยกเว้นคอลัมน์ตามประเภทแทนที่จะเป็นชื่อ


2

คุณไม่เคยเห็น*ใน SQL วิว ... ตรวจสอบ ที่ของคุณ\d any_view psqlมีการประมวลผลล่วงหน้า (ครุ่นคิด) สำหรับการเป็นตัวแทนภายใน


การอภิปรายทั้งหมดที่นี่แสดงให้เห็นว่าข้อเสนอปัญหา (โดยปริยายในคำถามและการอภิปราย) เป็นน้ำตาลไวยากรณ์สำหรับโปรแกรมเมอร์ไม่ได้เป็นจริง "ปัญหาการเพิ่มประสิทธิภาพ SQL" ... ดีเดาของฉันมันเป็น 80% ของการเขียนโปรแกรม

ดังนั้นสามารถนำมาใช้เป็น " pre-parsing with introspection" ... ดูว่า PostgreSQL ทำอะไรเมื่อคุณประกาศ SQL-VIEW ด้วยSELECT *: VIEW-constructor แปลง*เป็นรายการของคอลัมน์ทั้งหมด (โดยวิปัสสนาและในขณะที่คุณเรียกใช้ สร้างซอร์สโค้ดดู)

การติดตั้งเพื่อสร้างมุมมองและเตรียมการ

เป็นการใช้งานได้จริง ตารางสมมติว่ามีเขตข้อมูล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;

เหมือนกันสำหรับการจัดทำงบ

... ดังนั้นนั่นเป็นไปได้และนั่นคือสิ่งที่ 80% ของโปรแกรมเมอร์ต้องการซินแท็กซ์น้ำตาลสำหรับการเตรียมการและการดู!


หมายเหตุ: การเรียนการสอนไวยากรณ์ที่ทำงานอาจจะไม่ได้- column_nameถ้ามีความขัดแย้งใน PostgreSQL บางอย่างเพื่อให้เราสามารถแนะนำEXCEPT column_name,
EXCEPT (column_name1, column_name2, ..., column_nameN)หรืออื่น ๆ


1

นี่คือฟังก์ชั่นของฉันเพื่อเลือกคอลัมน์ทั้งหมดที่คาดหวัง ฉันรวมความคิดจากpostgresonline.comและpostgresql 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.