Gibt es eine Möglichkeit, SELECT
alle Spalten in einer Tabelle außer bestimmten zu _? IT wäre sehr praktisch, um alle nicht blobigen oder nicht geometrischen Spalten aus einer Tabelle auszuwählen.
Etwas wie:
SELECT * -the_geom FROM segments;
Eine solche Funktion gibt es weder in Postgres noch im SQL Standard (AFAIK). Ich denke, das ist eine ziemlich interessante Frage, also habe ich ein bisschen gegoogelt und bin auf einen interessanten Artikel auf postgresonline.com gestoßen.
Sie zeigen einen Ansatz, bei dem die Spalten direkt aus dem Schema ausgewählt werden:
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
Sie könnten eine Funktion erstellen, die so etwas tut. Solche Themen wurden auch auf den Mailinglisten diskutiert, aber der allgemeine Konsens war ziemlich gleich: Fragen Sie das Schema ab.
Ich bin mir sicher, dass es andere Lösungen gibt, aber ich denke, dass sie alle eine Art magisches Schema-Abfrage-Foo beinhalten werden.
Übrigens: Seien Sie vorsichtig mit SELECT * ...
da dies zu Leistungseinbußen führen kann
Die wirkliche Antwort ist, dass man einfach praktisch nicht kann. Dies ist seit Jahrzehnten eine gefragte Funktion, und die Entwickler weigern sich, sie zu implementieren.
Die beliebte Antwort, die vorschlägt, die Schematabellen abzufragen, kann nicht effizient ausgeführt werden, da der Postgres-Optimierer dynamische Funktionen als Black Box betrachtet (siehe Testfall unten). Das bedeutet, dass Indizes nicht verwendet werden und Verknüpfungen nicht intelligent durchgeführt werden. Mit einem Makrosystem wie m4 wären Sie viel besser dran. Zumindest wird es den Optimierer nicht verwirren (aber es kann Sie dennoch verwirren). Ohne den Code zu forken und die Funktion selbst zu schreiben oder eine Programmiersprachenschnittstelle zu verwenden, stecken Sie fest.
Ich habe unten einen einfachen Proof of Concept geschrieben, der zeigt, wie schlecht die Leistung bei einer sehr einfachen dynamischen Ausführung in plpgsql wäre. Beachten Sie auch, dass ich unten eine Funktion erzwingen muss, die einen generischen Datensatz in einen bestimmten Zeilentyp zurückgibt, und die Spalten auflisten muss. Diese Methode funktioniert also nicht für "Alle auswählen außer", es sei denn, Sie möchten diese Funktion für alle Ihre Tabellen neu erstellen.
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)
Wie Sie sehen können, hat der Funktionsaufruf die gesamte Tabelle gescannt, während die direkte Abfrage den Index verwendet hat (95,46 ms gegenüber 00,07 ms.). Diese Art von Funktionen würde jede Art von komplizierter Abfrage tanken, die zur Verwendung von Indizes erforderlich ist oder verbinden Sie Tabellen in der richtigen Reihenfolge.
Mit PostgreSQL ab 9.4, wo JSONB eingeführt wurde, ist dies tatsächlich einigermaßen möglich. Ich habe über eine ähnliche Frage nachgedacht, wie alle verfügbaren Attribute in Google Map (über GeoJSON) angezeigt werden können.
johto on irc channel schlug vor, das Element aus JSONB zu löschen.
Hier ist die Idee
select the_geom,
to_jsonb(foo) - 'the_geom'::text attributes
from (
select * from
segments
) foo
Während Sie json anstelle einzelner Spalten erhalten, war es genau das, was ich wollte. Vielleicht kann json wieder in einzelne Spalten erweitert werden.
Sie können dies nur mithilfe dynamischer SQL-Anweisungen tun (sagen Sie nicht, dass Sie dies tun sollten). Es ist einfach (wie DrColossos schrieb), die Systemansichten abzufragen, die Struktur der Tabelle zu finden und die richtigen Anweisungen zu erstellen.
PS: Warum sollten Sie alle/einige Spalten auswählen wollen, ohne Ihre Tabellenstruktur genau zu kennen/zu schreiben?
In ein Kommentar erklären Sie, dass Ihr Motiv darin besteht, den Inhalt von Spalten mit langem Inhalt nicht anzuzeigen als die Spalte selbst nicht anzuzeigen:
… Manchmal möchte ich eine Tabelle mit einer geometrischen Spalte abfragen, ohne die sehr lange Geometriezeichenfolge anzuzeigen, die die Ausgabe beeinträchtigt. Ich möchte nicht alle Spalten angeben, da es möglicherweise einige Dutzend gibt.
Dies ist mit Hilfe einer Hilfsfunktion möglich, die den langen Inhalt durch null
ersetzt (eine beliebige text
-Spalte in meinem Beispiel, aber Sie würden dies für die Typen ändern, die Sie unterdrücken möchten):
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 | bla bla bla bla bla bla 3 | 4 | bla bla
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 | null 3 | 4 | null
dbfiddle hier
Wenn Sie beim Debuggen Unordnung vom Bildschirm entfernen möchten, indem Sie keine Spalten mit großen Datenwerten anzeigen, können Sie den folgenden Trick verwenden:
(Installieren Sie das Contrib-Paket "hstore", falls Sie es noch nicht haben: "CREATE EXTENSION hstore;
")
Für eine Tabelle "test" mit col1, col2, col3 können Sie den Wert von "col2" auf null setzen, bevor Folgendes angezeigt wird:
select (r).* from (select (test #= hstore('col2',null)) as r from test) s;
Oder setzen Sie zwei Spalten auf null, bevor Sie Folgendes anzeigen:
select (r).* from (select (test #= hstore('col2',null) #= hstore('col1',null)) as r from test) s;
die Einschränkung ist, dass "Test" eine Tabelle sein muss (ein Alias oder eine Unterauswahl funktioniert nicht), da der Datensatztyp, der in hstore eingespeist wird, definiert werden muss.
Es gibt eine Problemumgehung, die ich gerade entdeckt habe, die jedoch das Senden von SQL-Abfragen aus R heraus erfordert. Sie kann für R-Benutzer von Nutzen sein.
Grundsätzlich sendet das Paket dplyr
SQL-Abfragen (und insbesondere PostgreSQL-Abfragen) und akzeptiert das Argument -(column_name)
.
Ihr Beispiel könnte also wie folgt geschrieben werden:
select(segments, -(the_geom))
Dynamisch wie oben angegeben ist die einzige Antwort, aber ich werde es nicht empfehlen. Was ist, wenn Sie auf lange Sicht weitere Spalten hinzufügen, diese jedoch nicht unbedingt für diese Abfrage erforderlich sind?
Sie würden anfangen, mehr Spalte zu ziehen, als Sie benötigen.
Was ist, wenn die Auswahl Teil einer Einfügung wie in ist?
In Tabelle A einfügen (Spalte1, Spalte2, Spalte3 .. Spalte) Wählen Sie alles außer 2 Spalten aus TabelleB aus
Die Spaltenübereinstimmung ist falsch und Ihre Einfügung schlägt fehl.
Es ist möglich, aber ich empfehle trotzdem, jede benötigte Spalte für jede ausgewählte Auswahl zu schreiben, auch wenn fast jede Spalte erforderlich ist.
Aus Sicht der Anwendung ist dies eine träge Lösung. Es ist unwahrscheinlich, dass eine Anwendung automatisch weiß, was mit den neuen Spalten zu tun ist.
Datenbrowseranwendungen können die Metadaten für die Daten abfragen und die Spalten von den ausgeführten Abfragen ausschließen oder eine Teilmenge der Daten der Spalte auswählen. Neue BLOBs können beim Hinzufügen ausgeschlossen werden. BLOB-Daten für bestimmte Zeilen können bei Bedarf ausgewählt werden.
In jeder SQL-Variante, die dynamische Abfragen unterstützt, kann die Abfrage mithilfe einer Abfrage für die Metadaten der Tabellen erstellt werden. Für Ihre Absicht würde ich Spalten ausschließen, die eher auf dem Typ als auf dem Namen basieren.
Sie sehen *
In SQL-VIEWS nie ... überprüfen Sie \d any_view
In Ihrem psql
. Es gibt ein (introspektives) Vorverarbeitung für die interne Darstellung.
Alle Diskussionen hier zeigen, dass der Issue-Vorschlag (implizit in der Frage und den Diskussionen enthalten) ein Syntaxzucker für Programmierer ist, kein echtes "SQL-Optimierungsproblem" ... Nun, ich denke, es ist für 80% der Programmierer.
Kann also als " Pre-Parsing mit Introspektion" implementiert werden ... Sehen Sie, was PostgreSQL tut, wenn Sie eine SQL-VIEW mit SELECT *
Deklarieren: Der VIEW-Konstruktor transformiert *
In eine Liste aller Spalten (durch Selbstbeobachtung und in dem Moment, in dem Sie den Quellcode CREATE VIEW ausführen).
Es ist eine praktikable Implementierung. Angenommen, Tabelle t
mit Feldern (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;
Gleiches gilt für PREPARE-Anweisung .
... also, das ist möglich, und genau das brauchen 80% der Programmierer, einen Syntaxzucker für PREPARE und VIEWS!
HINWEIS: Natürlich ist die praktikable Syntax möglicherweise nicht - column_name
, Wenn es in PostgreSQL einen Konflikt gibt, können wir EXCEPT column_name
Vorschlagen.EXCEPT (column_name1, column_name2, ..., column_nameN)
oder andere.
Dies ist meine Funktion, um alle Spalten auszuwählen, die eine erwarten. Ich habe Ideen aus postgresonline.com und postgresql tuturial und aus anderen Quellen kombiniert.
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)