วิธีตรวจสอบว่ามีตารางอยู่ในสคีมาที่กำหนดหรือไม่


149

Postgres 8.4 และฐานข้อมูลที่สูงกว่ามีตารางทั่วไปในpublicสคีมาและตารางเฉพาะ บริษัท ในcompanyสคีมา
companyชื่อสคีมามักเริ่มต้นด้วย'company'และลงท้ายด้วยหมายเลข บริษัท
ดังนั้นอาจมีสกีมาเช่น:

public
company1
company2
company3
...
companynn

แอปพลิเคชันจะทำงานกับ บริษัท เดียวเสมอ ระบุไว้ตามใน ODBC หรือสตริงการเชื่อมต่อ npgsql เช่น:
search_path

search_path='company3,public'

คุณจะตรวจสอบว่าตารางที่กำหนดนั้นมีอยู่ในcompanynสคีมาที่ระบุได้อย่างไร

เช่น:

select isSpecific('company3','tablenotincompany3schema')

ควรกลับมาfalseและ

select isSpecific('company3','tableincompany3schema')

trueควรกลับ

ในกรณีใด ๆ ฟังก์ชันควรตรวจสอบเฉพาะcompanynสคีมาที่ผ่านไม่ใช่สคีมาอื่น ๆ

ถ้าตารางที่กำหนดมีอยู่ทั้งในและสคีผ่านฟังก์ชั่นควรกลับpublic ควรใช้กับ Postgres 8.4 หรือใหม่กว่าtrue

คำตอบ:


284

มันขึ้นอยู่กับสิ่งที่คุณต้องการที่จะทดสอบว่า

สคีมาข้อมูล?

ในการค้นหา "มีตารางอยู่หรือไม่ " ( ไม่ว่าใครจะขอ ) การสอบถาม schema ของข้อมูล ( information_schema.tables) นั้นไม่ถูกต้องพูดอย่างเคร่งครัดเพราะ ( ต่อเอกสารประกอบ ):

เฉพาะตารางและมุมมองเหล่านั้นเท่านั้นที่แสดงว่าผู้ใช้ปัจจุบันมีสิทธิ์เข้าถึง (โดยเป็นเจ้าของหรือมีสิทธิ์บางอย่าง)

เคียวรีที่จัดเตรียมโดย @kongสามารถส่งคืนFALSEได้ แต่ตารางยังคงมีอยู่ มันตอบคำถาม:

วิธีตรวจสอบว่ามีตาราง (หรือมุมมอง) อยู่และผู้ใช้ปัจจุบันสามารถเข้าถึงได้หรือไม่

SELECT EXISTS (
   SELECT FROM information_schema.tables 
   WHERE  table_schema = 'schema_name'
   AND    table_name   = 'table_name'
   );

สคีมาข้อมูลมีประโยชน์เป็นหลักในการพกพาข้ามรุ่นหลักและข้าม RDBMS ที่แตกต่างกัน แต่การนำไปปฏิบัตินั้นช้าเพราะ Postgres ต้องใช้มุมมองที่ซับซ้อนเพื่อให้สอดคล้องกับมาตรฐาน ( information_schema.tablesเป็นตัวอย่างที่ค่อนข้างง่าย) และข้อมูลบางอย่าง (เช่น OIDs) ได้รับหายไปในการแปลจากแคตตาล็อกระบบ - ซึ่งจริง ๆ แล้วนำข้อมูลทั้งหมด

แคตตาล็อกระบบ

คำถามของคุณคือ:

จะตรวจสอบว่ามีตารางอยู่ได้อย่างไร

SELECT EXISTS (
   SELECT FROM pg_catalog.pg_class c
   JOIN   pg_catalog.pg_namespace n ON n.oid = c.relnamespace
   WHERE  n.nspname = 'schema_name'
   AND    c.relname = 'table_name'
   AND    c.relkind = 'r'    -- only tables
   );

ใช้แคตตาล็อกระบบpg_classและpg_namespaceโดยตรงซึ่งเร็วกว่ามากเช่นกัน อย่างไรก็ตามตามเอกสารเกี่ยวกับpg_class :

แคpg_classตตาล็อกตารางแคตตาล็อกและทุกอย่างอื่น ๆ ที่มีคอลัมน์หรือเป็นอย่างอื่นคล้ายกับตาราง ซึ่งรวมถึงดัชนี (แต่เห็นด้วยpg_index), ลำดับ , มุมมอง , มุมมองที่เป็นรูปธรรม , ประเภทประกอบและตาราง TOAST ;

สำหรับคำถามนี้โดยเฉพาะนอกจากนี้คุณยังสามารถใช้มุมมองระบบ pg_tablesเรียบง่ายขึ้นและพกพาได้มากกว่าในรุ่นหลัก ๆ ของ Postgres (ซึ่งแทบจะไม่ต้องกังวลกับแบบสอบถามพื้นฐานนี้):

SELECT EXISTS (
   SELECT FROM pg_tables
   WHERE  schemaname = 'schema_name'
   AND    tablename  = 'table_name'
   );

ตัวระบุต้องไม่ซ้ำกันในวัตถุทั้งหมดที่กล่าวถึงข้างต้น หากคุณต้องการถาม:

วิธีการตรวจสอบว่าชื่อสำหรับตารางหรือวัตถุที่คล้ายกันในสคีมาที่กำหนดจะถูกนำมาใช้?

SELECT EXISTS (
   SELECT FROM pg_catalog.pg_class c
   JOIN   pg_catalog.pg_namespace n ON n.oid = c.relnamespace
   WHERE  n.nspname = 'schema_name'
   AND    c.relname = 'table_name'
   );

ทางเลือก: cast to regclass

SELECT 'schema_name.table_name'::regclass

สิ่งนี้ทำให้เกิดข้อยกเว้นถ้าตาราง (ทางเลือกที่มีคุณสมบัติของสคีมา) (หรือวัตถุอื่น ๆ ที่ใช้ชื่อนั้น) ไม่มีอยู่

หากคุณไม่ได้กำหนดชื่อสคีมาให้เพี้ยนไปที่regclassค่าดีฟอลต์search_pathและและส่งคืน OID สำหรับตารางแรกที่พบ - หรือข้อยกเว้นถ้าตารางอยู่ในสกีมาที่ไม่มีอยู่ในรายการ หมายเหตุว่า schemas ระบบpg_catalogและpg_temp(schema สำหรับวัตถุชั่วคราวของเซสชั่นปัจจุบัน) search_pathเป็นส่วนหนึ่งของการทำงานโดยอัตโนมัติ

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

ข้อความค้นหาด้านบนหลีกเลี่ยงข้อยกเว้นที่เป็นไปได้และเร็วขึ้นเล็กน้อย

to_regclass(rel_name) ใน Postgres 9.4+

ง่ายกว่ามากตอนนี้:

SELECT to_regclass('schema_name.table_name');

เหมือนกับนักแสดงแต่มันกลับมา ...

... ว่างเปล่าแทนที่จะทิ้งข้อผิดพลาดหากไม่พบชื่อ


4
จาก shell:[[ `psql dbname -tAc "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'ejabberd' AND table_name = 'users');"` = 't' ]]
brauliobo

1
มีเหตุผลใดที่คุณไม่ได้ใช้pg_tables ?
m0meni

1
pg_tablesเป็นความคิดที่ดีสำหรับ"วิธีตรวจสอบว่ามีตารางอยู่หรือไม่" (ตรวจสอบตารางเท่านั้น . ไม่ได้สำหรับวัตถุประสงค์อื่น ๆ เช่นอธิบายข้างต้นนอกจากนี้ยังpg_tablesเป็นมุมมองที่เกี่ยวข้องกับหลายตาราง ( pg_class, pg_namespace, pg_tablespace) ซึ่งมีราคาแพงมากขึ้นอีกนิดเหตุผลที่สำคัญที่สุด:. ฉันกำลังนำมาใช้เพื่อการสอบถามpg_classโดยตรงและไม่ได้ คิดว่าpg_tablesเมื่อเขียนคำตอบนี้ฉันเพิ่มไปข้างต้นแล้วขอบคุณ
Erwin Brandstetter

1
@ sage88: ใช่ฉันลบความคิดเห็นที่ไม่ถูกต้อง คุณสามารถใช้pg_my_temp_schema()เพื่อรับ OID ของ schema temp จริงหากมีอยู่ (แต่มุมมองในการinformation_schemaไม่รวม OID คุณทำได้SELECT nspname FROM pg_namespace WHERE OID = pg_my_temp_schema()) การทดสอบของคุณมีจุดอ่อนหลายประการ การทดสอบที่ถูกต้องจะเป็นหรือเข้มงวด:table_schema LIKE 'pg\_temp\_%' table_schema ~ '^pg_temp_\d+$'
Erwin Brandstetter

1
@PeterKrauss คุณจะได้รับข้อผิดพลาดนั้นหากคุณพยายามใช้ฟังก์ชัน to_regclass กับรุ่น postgres ที่เก่ากว่า 9.4 ต้องมี
9.4+


0

สำหรับ PostgreSQL 9.3 หรือน้อยกว่า ... หรือผู้ที่ชื่นชอบการปรับข้อความให้เป็นมาตรฐาน

สามรสชาติของห้องสมุด SwissKnife เก่าของฉัน: relname_exists(anyThing), และrelname_normalized(anyThing) relnamechecked_to_array(anyThing)การตรวจสอบทั้งหมดจากpg_catalog.pg_classตารางและผลตอบแทนประเภทข้อมูลมาตรฐานสากล ( บูล , ข้อความหรือข้อความ [])

/**
 * From my old SwissKnife Lib to your SwissKnife. License CC0.
 * Check and normalize to array the free-parameter relation-name.
 * Options: (name); (name,schema), ("schema.name"). Ignores schema2 in ("schema.name",schema2).
 */
CREATE FUNCTION relname_to_array(text,text default NULL) RETURNS text[] AS $f$
     SELECT array[n.nspname::text, c.relname::text]
     FROM   pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace,
            regexp_split_to_array($1,'\.') t(x) -- not work with quoted names
     WHERE  CASE
              WHEN COALESCE(x[2],'')>'' THEN n.nspname = x[1]      AND c.relname = x[2]
              WHEN $2 IS NULL THEN           n.nspname = 'public'  AND c.relname = $1
              ELSE                           n.nspname = $2        AND c.relname = $1
            END
$f$ language SQL IMMUTABLE;

CREATE FUNCTION relname_exists(text,text default NULL) RETURNS boolean AS $wrap$
  SELECT EXISTS (SELECT relname_to_array($1,$2))
$wrap$ language SQL IMMUTABLE;

CREATE FUNCTION relname_normalized(text,text default NULL,boolean DEFAULT true) RETURNS text AS $wrap$
  SELECT COALESCE(array_to_string(relname_to_array($1,$2), '.'), CASE WHEN $3 THEN '' ELSE NULL END)
$wrap$ language SQL IMMUTABLE;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.