it-swarm.asia

SQL: حدد كافة الأعمدة باستثناء بعض

هل هناك طريقة لـ SELECT جميع الأعمدة في الجدول ، باستثناء أعمدة محددة؟ سيكون من الملائم للغاية اختيار جميع الأعمدة غير الهندسية أو غير الهندسية من الجدول.

شيء مثل:

SELECT * -the_geom FROM segments;
  • سمعت ذات مرة أنه تم استبعاد هذه الوظيفة عمدا من معيار SQL لأن تغيير إضافة أعمدة إلى الجدول سيغير نتائج الاستعلام. هل هذا صحيح؟ هل الحجة صحيحة؟
  • هل هناك حل ، خاصة في PostgreSQL؟
119
Adam Matan

لا توجد مثل هذه الميزة في Postgres ولا في معيار SQL (AFAIK). أعتقد أن هذا سؤال مثير للاهتمام إلى حد ما ، لذا فقد بحثت في Google قليلًا وعثرت على مقالة مثيرة للاهتمام حول 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

يمكنك إنشاء وظيفة تقوم بشيء من هذا القبيل. كما نوقشت مثل هذه المواضيع في القوائم البريدية ، لكن الإجماع العام كان إلى حد كبير هو نفسه: الاستعلام عن المخطط.

أنا متأكد من وجود حلول أخرى ولكن أعتقد أنها ستشمل جميعًا نوعًا من المخطط السحري - queriying-foo.

راجع للشغل: كن حذرا مع SELECT * ... حيث يمكن أن يكون لهذا عقوبات على الأداء

59
DrColossos

الجواب الحقيقي هو أنك لا تستطيع عمليا. لقد كانت هذه ميزة مطلوبة منذ عقود ويرفض المطورون تنفيذها.

الإجابة الشائعة التي تقترح الاستعلام عن جداول المخطط لن تكون قادرة على العمل بكفاءة لأن مُحسِّن 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 مللي ثانية.) هذه الأنواع من الوظائف ستنقل أي نوع من الاستعلام المعقد الذي يحتاجه لاستخدام الفهارس أو ربط الجداول بالترتيب الصحيح.

18
user17130

إنه في الواقع ممكن إلى حد ما مع PostgreSQL بدءًا من 9.4 حيث تم تقديم JSONB. كنت أفكر في سؤال مماثل حول كيفية إظهار جميع السمات المتاحة في خريطة Google (عبر GeoJSON).

اقترح johto على قناة irc لمحاولة حذف عنصر من JSONB.

ها هي الفكرة

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

بينما تحصل على json بدلاً من الأعمدة الفردية ، كان ذلك بالضبط ما أردت. ربما يمكن توسيع json مرة أخرى إلى أعمدة فردية.

14
mlt

الطريقة الوحيدة التي يمكنك القيام بها (لا تقل أنه يجب عليك) هي باستخدام عبارات SQL الديناميكية. من السهل (كما كتب DrColossos) الاستعلام عن طرق عرض النظام والعثور على هيكل الجدول وبناء العبارات المناسبة.

ملاحظة: لماذا تريد تحديد كل/بعض الأعمدة دون معرفة/كتابة هيكل الجدول الخاص بك بالضبط؟

6
Marian

في تعليق تشرح أن دافعك هو أن تكون ملائمًا لعدم عرض محتويات أعمدة ذات محتوى طويل ، بدلاً من ذلك من عدم عرض العمود نفسه:

... أحيانًا أرغب في الاستعلام عن جدول بعمود هندسي ، دون عرض سلسلة هندسية طويلة جدًا تحجب الناتج. لا أريد تحديد جميع الأعمدة لأنه قد يكون هناك العشرات.

هذا ممكن ، بمساعدة وظيفة المساعد التي تستبدل المحتوى الطويل بـ 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 | شريط | باز 
 -: | -: | : ---------------------------- 
 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 هنا

4
Jack says try topanswers.xyz

إذا كان هدفك هو إزالة الفوضى من الشاشة أثناء التصحيح عن طريق عدم عرض الأعمدة بقيم بيانات كبيرة ، فيمكنك استخدام الحيلة التالية:

(قم بتثبيت حزمة مساهمة "hstore" إذا لم يكن لديك بالفعل: "CREATE EXTENSION hstore; ")

لجدول "test" مع col1 ، col2 ، col3 ، يمكنك تعيين قيمة "col2" على قيمة فارغة قبل عرض:

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

أو ، قم بتعيين عمودين على قيمة فارغة قبل عرض:

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

التحذيرات هي أن "الاختبار" يجب أن يكون جدولًا (لن يعمل الاسم المستعار أو الاختيار الفرعي) نظرًا لأنه يجب تحديد نوع السجل الذي يتغذى على المتجر.

3
Sean

يوجد حل بديل اكتشفته للتو ، ولكنه يتطلب إرسال استعلامات SQL من داخل R. قد يكون مفيدًا لمستخدمي R.

ترسل الحزمة dplyr بشكل أساسي استعلامات SQL (وتحديدا PostgreSQL) وتقبل الوسيطة -(column_name).

لذا يمكن كتابة المثال الخاص بك على النحو التالي:

select(segments, -(the_geom))
3
Dario Lacan

ديناميكيًا كما هو مذكور أعلاه هو الجواب الوحيد لكنني لن أوصي به. ماذا لو قمت بإضافة المزيد من الأعمدة على المدى الطويل ولكنها ليست ضرورية بالضرورة لهذا الاستعلام؟

ستبدأ في سحب عمود أكثر مما تحتاج.

ماذا لو كان التحديد جزءًا من إدراج كما في

إدراج في الجدول أ (col1 ، col2 ، col3 .. coln) حدد كل شيء باستثناء عمودين من الجدول B

ستكون مطابقة العمود خاطئة وسيفشل إدراجك.

هذا ممكن ولكن ما زلت أوصي بكتابة كل عمود مطلوب لكل تحديد مكتوب حتى إذا كان كل عمود مطلوبًا تقريبًا.

3
Nicolas de Fontenay
  • من منظور التطبيق ، هذا حل كسول. من غير المحتمل أن يعرف التطبيق تلقائيًا ما يجب فعله مع العمود (الأعمدة) الجديد.

    قد تقوم تطبيقات متصفح البيانات بالاستعلام عن البيانات الوصفية للبيانات واستبعاد الأعمدة من الاستعلامات الجاري تشغيلها ، أو تحديد مجموعة فرعية من بيانات العمود. يمكن استبعاد BLOBs الجديدة عند إضافتها. يمكن تحديد بيانات BLOB لصفوف معينة عند الطلب.

  • في أي متغير SQL يدعم الاستعلامات الديناميكية ، يمكن إنشاء الاستعلام باستخدام استعلام في البيانات الوصفية للجداول. لهدفك ، سأستبعد الأعمدة بناءً على النوع بدلاً من الاسم.

3
BillThor

لن ترى مطلقًا * في SQL-VIEWS ... تحقق \d any_view في psql. هناك (استبطاني) المعالجة المسبقة للتمثيل الداخلي.


تظهر جميع المناقشات هنا أن - مقترح المشكلة (ضمنيًا في السؤال والمناقشات) هو عبارة عن بناء جملة للمبرمجين ، ليست "مسألة تحسين SQL" حقيقية ... حسنًا ، على ما أعتقد ، إنها لـ 80٪ من المبرمجين.

لذا يمكن تنفيذها على النحو التالي " pre-parsing with introspection" ... شاهد ما يفعله PostgreSQL عند إعلان SQL-VIEW باستخدام SELECT *: يتحول مُنشئ VIEW * في قائمة بجميع الأعمدة (عن طريق الاستبطان وفي اللحظة التي تقوم فيها بتشغيل رمز مصدر CREATE VIEW).

تنفيذ CREATE VIEW والاستعداد

إنه تطبيق قابل للتطبيق. لنفترض أن الجدول 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٪ من المبرمجين ، بناء جملة من أجل التحضير والمشاهدة!


ملحوظة: بالطبع viable syntax ربما ليس - column_name ، إذا كان هناك بعض التضارب في PostgreSQL ، لذا يمكننا اقتراح EXCEPT column_name ،
EXCEPT (column_name1, column_name2, ..., column_nameN) أو غيرها.

2
Peter Krauss

هذه هي وظيفتي لتحديد جميع الأعمدة تتوقع واحدة. جمعت الأفكار من 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)
1
Veli-Matti Sorvala