SQL: SELECT Bazı hariç tüm sütunlar


108

SELECTBir tablodaki tüm sütunların, özel sütunlar dışında bir yolu var mı ? Bir tablodan blob olmayan veya geometrik olmayan tüm sütunları seçmek için çok uygun olacaktır.

Gibi bir şey:

SELECT * -the_geom FROM segments;
  • Bir keresinde bu işlevin kasıtlı olarak SQL standardından çıkarıldığını duydum, çünkü tabloya sütun eklemek, sorgu sonuçlarını değiştirecek. Bu doğru mu? Argüman geçerli mi?
  • Özellikle PostgreSQL'de bir geçici çözüm var mı?

Bazıları hariç tüm sütunları bilmek istediğiniz kullanım durumu hangisidir? Bazı manuel sorgular yaparken sadece ekranda göstermek için mi? Bir programın parçası mı?
joanolo

2
6, anlamlı, kısa kolon (a-la ile bir tablo name, age, siduzun ikili Alongwith ekran genişliği sığar) geomsütunu. İkili geometri dışındaki tüm alanları sorgulamak istiyorum ve isimlerini birer birer yazmak sıkıcı.
Adam Matan

Bu durumda, etkileşimli sorgu ile kullandığınız araçla SQL ile karşılaştırıldığında daha fazlası olabilir ...
joanolo

1
@joanolo Düz PostgreSQL kabuğu.
Adam Matan

3
Bu çok açık görünüyor. Bazen bir veya iki sütun yazdırmak istemezsiniz, çünkü bunlar ilgi çekici değildir veya sonuç tablosunun ekrana sığmasını istersiniz (özellikle bir komut satırı istemcisi kullanılıyorsa). Bunun gibi bir sözdizimi beklenirselect (!coluns2,!column5) from sometable;
gumkins

Yanıtlar:


54

Böyle bir özellik Postgres'te veya SQL Standardında (AFAIK) yoktur. Bunun oldukça ilginç bir soru olduğunu düşünüyorum, bu yüzden biraz googledim ve postgresonline.com'da ilginç bir yazıyla karşılaştım .

Doğrudan şemadan sütunları seçen bir yaklaşım gösterirler:

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öyle bir şey yapan bir fonksiyon yaratabilirsiniz. Bu tür konular posta listelerinde de tartışıldı, ancak genel görüş birliği hemen hemen aynıydı: şema sorgusu.

Başka çözümler olduğuna eminim ama hepsinin bir tür sihirli şema-sorgulama-foo içereceğini düşünüyorum.

BTW: SELECT * ...Performans cezaları olabileceğinden dikkatli olun


Böyle bir fonksiyon nasıl oluşturulur? Bilinmeyen bir sorgu döndüren bir işlev yaratmanın hiçbir yolunu bulamıyorum, her zaman önceden bir tablo bildirmek zorunda kalacağım.
ePascoal

17

Asıl cevap, pratikte yapamayacağınız. Bu, onlarca yıldır talep edilen bir özellik olmuştur ve geliştiriciler uygulamayı yapmayı reddetmektedir.

Şema tablolarını sorgulamayı öneren popüler cevap, Postgres optimizer'ın dinamik işlevleri siyah bir kutu olarak gördüğü için verimli bir şekilde çalışamaz (aşağıdaki test durumuna bakın). Bu, endekslerin kullanılmayacağı ve birleştirmelerin akıllıca yapılmayacağı anlamına gelir. M4 gibi bir çeşit makro sistemle çok daha iyi olursunuz. En azından optimize edicinin kafasını karıştırmayacak (ama yine de sizi şaşırtabilir.) Kod yazmadan ve özelliği kendiniz yazmadan veya takılı kaldığınız bir programlama dili arayüzü kullanmadan.

Performansın plpgsql'de çok basit bir dinamik uygulamada ne kadar kötü olacağını gösteren basit bir kavram kanıtı yazdım. Ayrıca, aşağıda, genel bir kaydı belirli bir satır türüne döndüren bir işlevi zorlamak ve sütunları numaralandırmak zorunda olduğumu fark ettim. Bu nedenle, bu fonksiyonu tüm tablolarınız için tekrar yapmak istemediğiniz sürece 'hepsini seç' için çalışmaz.

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)

Gördüğünüz gibi, doğrudan sorgu dizini kullanırken ( 95.46 ms vs. 00.07 ms .) Fonksiyon çağrısını tüm tabloyu tararken . Bu tür işlevler, dizinleri kullanmak veya tabloları doğru sırayla birleştirmek için gereken her türlü karmaşık sorguyu depolar . .


1
İlginç bakış açısı. Bu kesinlikle koddan ziyade insan kullanıcıları için bir özelliktir (ya da öyle umuyorum ki!) Bu yüzden müşteriyi sorumlu kılma noktasını görebiliyorum. Muhtemelen genişletilmiş ekran (\ x on) gibi şeyler tamamen istemcide uygulanır ve sütunların çıkarılması benzer bir yerde uygulanmalıdır.
Max Murphy,

13

JSONB'nin tanıtıldığı 9.4 ile başlayan PostgreSQL ile bir şekilde mümkün. Google Map’te mevcut tüm özelliklerin nasıl gösterileceğine (GeoJSON aracılığıyla) benzer bir soru soruluyordu.

irc kanalındaki johto, JSONB'den öğe silmeyi önerdi.

İşte fikir

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

Tek tek sütunlar yerine json alırken, tam olarak istediğim buydu. Belki json ayrı ayrı sütunlara geri genişletilebilir.


Evet, belki buradan bir şey olabilir, ama bunu işe henüz almadım- stackoverflow.com/questions/36174881/…
chrismarx

6

Bunu yapmanın tek yolu (söylemen gerektiğini söyleme) dinamik sql ifadeleri kullanmak. Sistem görüşlerini sorgulamak ve tablonun yapısını bulmak ve uygun ifadeler oluşturmak kolaydır (DrColossos'un yazdığı gibi).

Not: Neden tablo yapınızı tam olarak bilmeden / yazmadan tüm / bazı sütunları seçmek istiyorsunuz?


7
PS'inizle ilgili: Bazen, çıktıyı saran çok uzun geometri dizesini görüntülemeden, geometrik sütunlu bir tabloyu sorgulamak istiyorum. Tüm sütunları belirtmek istemiyorum, çünkü onlarca olabilir.
Adam Matan

Yani sadece dinamik sql sizi yazarak çok :-) kaydedebilirsiniz.
Marian

Herkes sorguyu yapan kişinin veritabanını tasarlayan olduğunu varsayar. :-) Bir excel oluşturmak için çok fazla alanlı (30'dan fazla) eski bir veritabanını sorgulamanız gerektiğini, ancak vermek istemediğiniz hassas bilgilere sahip bir veya iki alanın olduğunu varsayalım.
yucer

3

Dinamik olarak yukarıda belirtildiği gibi tek cevap ama bunu önermeyeceğim. Uzun vadede daha fazla sütun eklerseniz, ancak bu sorgu için gerekli olmaları gerekmezse ne olur?

İhtiyacınız olandan daha fazla sütun çekmeye başlarsınız.

Seçim, aşağıdaki gibi bir ekin parçasıysa

TableA'ya ekleyin (col1, col2, col3 .. coln) tableB'dan 2 sütun dışında her şeyi seçin

Sütun eşleşmesi yanlış olacak ve yazınız başarısız olacak.

Mümkün, ancak neredeyse her sütun gerekli olsa bile, seçilen her yazı için gereken her sütunu yazmanızı öneririm.


Bu yaklaşım açıkça programatik olarak yanlıştır, ancak SELECTs için bir konsol sorgusu olarak zararsız ve kullanışlıdır .
Adam Matan

3

Amacınız hata ayıklama sırasında büyük veri değerlerine sahip sütunları görüntülemeyerek dağınıklığı ekrandan kaldırmaksa, aşağıdaki numarayı kullanabilirsiniz:

(zaten yoksa, "hstore" katkı paketini kurun: " CREATE EXTENSION hstore;")

Col1, col2, col3 ile "test" bir tablo için, göstermeden önce "col2" değerini boş olarak ayarlayabilirsiniz:

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

Veya, görüntülemeden önce iki sütunu boş olarak ayarlayın:

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

uyarılar, "test" in bir tablo olması gerektiğidir (takma ad veya alt seçim çalışmaz) çünkü hstore'a beslenen kayıt türü tanımlanmalıdır.


3

Yeni keşfettiğim bir geçici çözüm var, ancak R içinden SQL sorguları göndermesi gerekiyor. R kullanıcılarının kullanımına açık olabilir.

Temel olarak dplyrpaket SQL (ve özellikle PostgreSQL) sorguları gönderir ve -(column_name)argümanı kabul eder .

Yani örneğiniz şöyle yazılabilir:

select(segments, -(the_geom))

3

Gelen bir açıklama size neden değil görüntüleme kolaylık olması olduğunu açıklamak içeriği uzun içerikli sütunların yerine sütun kendisi göstermiyor:

… Bazen bir tabloyu geometrik sütunlarla sorgulamak istiyorum, çıktıyı kesen çok uzun geometri dizesini görüntülemeden. Tüm sütunları belirtmek istemiyorum, çünkü onlarca olabilir.

Bu, uzun içeriği değiştiren bir yardımcı işlev yardımıyla (örneğime göre nullherhangi bir textsütun, ancak bastırmak istediğiniz türler için bunu değiştirirsiniz) mümkündür:

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 | bar | baz                          
-: | -: | : ----------------------------
  1 | 2 | filan filan filan filan
  3 | 4 | falan filan                    
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 | bar | baz
-: | -: | : ---
  1 | 2 | boş 
  3 | 4 | boş

dbfiddle burada


2
  • Bir uygulama açısından bakıldığında, bu tembel bir çözümdür. Bir uygulamanın yeni sütunlarla ne yapılacağını otomatik olarak bilmesi olası değildir.

    Veri tarayıcı uygulamaları, verilerin meta verilerini sorgulayabilir ve sütunları çalışmakta olan sorguların dışında tutabilir veya sütun verilerinin bir alt kümesini seçebilir. Yeni BLOB'lar eklendiğinde hariç tutulabilir. Belirli satırlar için BLOB verileri talep üzerine seçilebilir.

  • Dinamik sorguları destekleyen herhangi bir SQL değişkeninde, sorgu meta verileri tablolarındaki bir sorgu kullanılarak oluşturulabilir. Niyetiniz için, isim yerine yazıyı temel alan sütunları hariç tutardım.


2

*SQL-VIEWS'te hiç görmediniz ... kontrol \d any_viewedin psql. Bir (özeleştirisel) vardır ön işleme iç gösterimi için.


Buradaki tüm tartışmalar , sorun önerisinin (soru ve tartışmalarda örtük olan), programcılar için gerçek bir "SQL optimizasyon sorunu" değil, sözdizimi şekeri olduğunu göstermektedir.

Öyleyse " ön inceleme ile ayrıştırma " olarak uygulanabilir ... Bir SQL-VIEW'ı şu şekilde ilan ettiğinizde PostgreSQL'in ne yaptığını görün SELECT *: VIEW-yapıcı *tüm sütunların listesine dönüştürür (içe dönük olarak ve çalıştırdığınız anda VIEW kaynak kodunu CREATE ().

CREATE VIEW ve PREPARE Uygulaması

Uygulanabilir bir uygulamadır. tAlanları olan tabloyu varsayalım (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 deyimi için aynı .

... yani, bu mümkün ve programcıların% 80’inin ihtiyacı budur, HAZIRLIK ve GÖRÜNÜMLER için bir sözdizimi şekeri!


NOT: Tabii canlı sözdizimi belki değil - column_nameorada PostgreSQL bazı çatışma, bu yüzden önerebilirsiniz eğer EXCEPT column_name,
EXCEPT (column_name1, column_name2, ..., column_nameN)ya da diğer.


1

Bu benim fonksiyonum olan tüm sütunlardan birini seçmeyi seçmektir. Postgresonline.com ve postgresql tuturial ile diğer kaynaklardan gelen fikirleri birleştirdim .

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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.