คำถามง่าย ๆ วิธีเพิ่มคอลัมน์x
ในตารางy
แต่เฉพาะเมื่อx
ไม่มีคอลัมน์อยู่ ฉันพบวิธีแก้ไขที่นี่เท่านั้นวิธีการตรวจสอบว่ามีคอลัมน์อยู่หรือไม่
SELECT column_name
FROM information_schema.columns
WHERE table_name='x' and column_name='y';
คำถามง่าย ๆ วิธีเพิ่มคอลัมน์x
ในตารางy
แต่เฉพาะเมื่อx
ไม่มีคอลัมน์อยู่ ฉันพบวิธีแก้ไขที่นี่เท่านั้นวิธีการตรวจสอบว่ามีคอลัมน์อยู่หรือไม่
SELECT column_name
FROM information_schema.columns
WHERE table_name='x' and column_name='y';
คำตอบ:
นี่เป็นเวอร์ชั่นสั้นและหวานโดยใช้คำสั่ง "DO":
DO $$
BEGIN
BEGIN
ALTER TABLE <table_name> ADD COLUMN <column_name> <column_type>;
EXCEPTION
WHEN duplicate_column THEN RAISE NOTICE 'column <column_name> already exists in <table_name>.';
END;
END;
$$
คุณไม่สามารถผ่านสิ่งเหล่านี้เป็นพารามิเตอร์ได้คุณจะต้องทำการทดแทนตัวแปรในสตริงที่ฝั่งไคลเอ็นต์ แต่นี่เป็นแบบสอบถามแบบใช้ตัวเองที่จะส่งข้อความเฉพาะเมื่อคอลัมน์มีอยู่แล้วเพิ่มถ้ามันไม่ได้และ จะยังคงล้มเหลวในข้อผิดพลาดอื่น ๆ (เช่นประเภทข้อมูลที่ไม่ถูกต้อง)
ฉันไม่แนะนำให้ทำวิธีใด ๆ เหล่านี้หากสิ่งเหล่านี้เป็นสตริงแบบสุ่มที่มาจากแหล่งภายนอก ไม่ว่าคุณจะใช้วิธีใด (สตริงแบบไดนามิกด้านฝั่งหรือฝั่งเซิร์ฟเวอร์ที่ดำเนินการเป็นแบบสอบถาม) มันจะเป็นสูตรสำหรับภัยพิบัติเมื่อมันเปิดให้คุณโจมตี SQL injection
DO $$
ล้มเหลว ฉันได้พยายามDO $$;
ที่ล้มเหลวเกินไปจนผมเพิ่งเริ่มต้นบล็อกด้วยDO $$DECLARE r record;
ซึ่งจะได้รับในตัวอย่างในเอกสาร dev postgres
END; $$
เป็นข้อผิดพลาดทางไวยากรณ์ (Postgres 9.3) ฉันต้องใช้END $$;
แทน
EXCEPTION
) เป็นบิตทั่วไปมากขึ้นและสามารถใช้สำหรับงานที่ไม่มีIF NOT EXISTS
ไวยากรณ์ - ALTER TABLE ... ADD CONSTRAINT
ตัวอย่างเช่น
ด้วยPostgres 9.6สิ่งนี้สามารถทำได้โดยใช้ตัวเลือกif not exists
ALTER TABLE table_name ADD COLUMN IF NOT EXISTS column_name INTEGER;
ADD CONSTRAINT IF NOT EXISTS
เลย
CREATE OR REPLACE function f_add_col(_tbl regclass, _col text, _type regtype)
RETURNS bool AS
$func$
BEGIN
IF EXISTS (SELECT 1 FROM pg_attribute
WHERE attrelid = _tbl
AND attname = _col
AND NOT attisdropped) THEN
RETURN FALSE;
ELSE
EXECUTE format('ALTER TABLE %s ADD COLUMN %I %s', _tbl, _col, _type);
RETURN TRUE;
END IF;
END
$func$ LANGUAGE plpgsql;
โทร:
SELECT f_add_col('public.kat', 'pfad1', 'int');
ส่งคืนTRUE
ตามความสำเร็จมิฉะนั้นFALSE
(มีคอลัมน์อยู่แล้ว)
เพิ่มข้อยกเว้นสำหรับชื่อตารางหรือประเภทที่ไม่ถูกต้อง
สิ่งนี้สามารถทำได้ด้วยDO
คำDO
สั่ง แต่งบไม่สามารถส่งคืนอะไร และถ้าเป็นการใช้ซ้ำฉันจะสร้างฟังก์ชั่น
ผมใช้ประเภทระบุวัตถุ regclass
และregtype
สำหรับ_tbl
และ_type
ที่) ช่วยป้องกันการฉีด SQL และ b) การตรวจสอบความถูกต้องของทั้งสองทันที (วิธีที่ถูกที่สุด) ชื่อคอลัมน์_col
ได้ยังคงที่จะปรุงแต่งสำหรับกับEXECUTE
quote_ident()
คำอธิบายเพิ่มเติมในคำตอบที่เกี่ยวข้องนี้:
format()
ต้องใช้ Postgres 9.1+ สำหรับรุ่นที่เก่ากว่าต่อกันด้วยตนเอง:
EXECUTE 'ALTER TABLE ' || _tbl || ' ADD COLUMN ' || quote_ident(_col) || ' ' || _type;
คุณสามารถกำหนดชื่อตารางของคุณได้ แต่ไม่จำเป็น
คุณสามารถอ้างตัวระบุในการเรียกใช้ฟังก์ชันเพื่อรักษาอูฐและคำสงวนไว้ (แต่คุณไม่ควรใช้ตัวเลือกนี้)
ผมสอบถามแทนpg_catalog
information_schema
คำอธิบายโดยละเอียด:
บล็อกที่มีEXCEPTION
อนุประโยคเช่นคำตอบที่ยอมรับในปัจจุบันนั้นช้ากว่าอย่างมาก โดยทั่วไปแล้วจะง่ายกว่าและเร็วกว่า เอกสารประกอบ:
เคล็ดลับ: บล็อกที่มีส่วน
EXCEPTION
คำสั่งมีราคาแพงกว่าการเข้าและออกมากกว่าบล็อกที่ไม่มีบล็อก ดังนั้นอย่าใช้EXCEPTION
โดยไม่จำเป็น
DO
คำสั่งการแก้ไขเล็กน้อยเพื่อยอมรับDEFAULT
และสิ่งนี้ใช้ได้อย่างสมบูรณ์แบบ!
แบบสอบถามเลือกต่อไปนี้จะกลับมาtrue/false
โดยใช้EXISTS()
ฟังก์ชั่น
EXISTS () :
อาร์กิวเมนต์ของ EXISTS เป็นคำสั่ง SELECT โดยพลการหรือแบบสอบถามย่อย แบบสอบถามย่อยจะถูกประเมินเพื่อพิจารณาว่าจะส่งคืนแถวหรือไม่ ถ้ามันส่งกลับอย่างน้อยหนึ่งแถวผลลัพธ์ของ EXISTS คือ "true"; หากเคียวรีย่อยส่งคืนแถวไม่ได้ผลลัพธ์ของ EXISTS คือ "false"
SELECT EXISTS(SELECT column_name
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'x'
AND column_name = 'y');
และใช้คำสั่ง SQL แบบไดนามิกต่อไปนี้เพื่อปรับเปลี่ยนตารางของคุณ
DO
$$
BEGIN
IF NOT EXISTS (SELECT column_name
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'x'
AND column_name = 'y') THEN
ALTER TABLE x ADD COLUMN y int DEFAULT NULL;
ELSE
RAISE NOTICE 'Already exists';
END IF;
END
$$
สำหรับผู้ที่ใช้ Postgre 9.5+ (ฉันเชื่อว่าส่วนใหญ่ของคุณทำ) มีวิธีแก้ปัญหาที่ค่อนข้างง่ายและสะอาด
ALTER TABLE if exists <tablename> add if not exists <columnname> <columntype>
ฟังก์ชั่นด้านล่างจะตรวจสอบคอลัมน์หากมีอยู่ส่งคืนข้อความที่เหมาะสมมิฉะนั้นมันจะเพิ่มคอลัมน์ในตาราง
create or replace function addcol(schemaname varchar, tablename varchar, colname varchar, coltype varchar)
returns varchar
language 'plpgsql'
as
$$
declare
col_name varchar ;
begin
execute 'select column_name from information_schema.columns where table_schema = ' ||
quote_literal(schemaname)||' and table_name='|| quote_literal(tablename) || ' and column_name= '|| quote_literal(colname)
into col_name ;
raise info ' the val : % ', col_name;
if(col_name is null ) then
col_name := colname;
execute 'alter table ' ||schemaname|| '.'|| tablename || ' add column '|| colname || ' ' || coltype;
else
col_name := colname ||' Already exist';
end if;
return col_name;
end;
$$
นี่เป็นวิธีแก้ปัญหาจากโซล่า แต่เพิ่งทำความสะอาดนิดหน่อย มันแตกต่างกันมากพอที่ฉันไม่เพียงแค่ต้องการ "ปรับปรุง" วิธีแก้ปัญหาของเขา (บวกฉันคิดว่ามันหยาบคาย)
ความแตกต่างที่สำคัญคือมันใช้รูปแบบ EXECUTE ซึ่งฉันคิดว่ามันค่อนข้างสะอาดกว่า แต่ฉันเชื่อว่าหมายความว่าคุณต้องอยู่ใน PostgresSQL 9.1 หรือใหม่กว่า
สิ่งนี้ได้รับการทดสอบบน 9.1 และใช้งานได้ หมายเหตุ: มันจะเพิ่มข้อผิดพลาดหาก schema / table_name / หรือ data_type ไม่ถูกต้อง นั่นอาจ "คงที่" แต่อาจเป็นพฤติกรรมที่ถูกต้องในหลายกรณี
CREATE OR REPLACE FUNCTION add_column(schema_name TEXT, table_name TEXT,
column_name TEXT, data_type TEXT)
RETURNS BOOLEAN
AS
$BODY$
DECLARE
_tmp text;
BEGIN
EXECUTE format('SELECT COLUMN_NAME FROM information_schema.columns WHERE
table_schema=%L
AND table_name=%L
AND column_name=%L', schema_name, table_name, column_name)
INTO _tmp;
IF _tmp IS NOT NULL THEN
RAISE NOTICE 'Column % already exists in %.%', column_name, schema_name, table_name;
RETURN FALSE;
END IF;
EXECUTE format('ALTER TABLE %I.%I ADD COLUMN %I %s;', schema_name, table_name, column_name, data_type);
RAISE NOTICE 'Column % added to %.%', column_name, schema_name, table_name;
RETURN TRUE;
END;
$BODY$
LANGUAGE 'plpgsql';
การใช้งาน:
select add_column('public', 'foo', 'bar', 'varchar(30)');
สามารถเพิ่มลงในสคริปต์การย้ายข้อมูลเพื่อเรียกใช้ฟังก์ชันและปล่อยเมื่อเสร็จสิ้น
create or replace function patch_column() returns void as
$$
begin
if exists (
select * from information_schema.columns
where table_name='my_table'
and column_name='missing_col'
)
then
raise notice 'missing_col already exists';
else
alter table my_table
add column missing_col varchar;
end if;
end;
$$ language plpgsql;
select patch_column();
drop function if exists patch_column();
ในกรณีของฉันสำหรับวิธีการสร้างเหตุผลมันเป็นเรื่องยากเล็กน้อยที่สคริปต์การย้ายข้อมูลของเราจะตัดผ่านสคีมาที่แตกต่างกัน
ในการหลีกเลี่ยงปัญหานี้เราใช้ข้อยกเว้นที่เพิ่งตรวจพบและละเว้นข้อผิดพลาด สิ่งนี้ยังมีผลข้างเคียงที่ดีจากการมองดูได้ง่ายขึ้นมาก
อย่างไรก็ตามโปรดระวังว่าโซลูชันอื่นมีข้อดีของตัวเองซึ่งอาจมีค่าเกินกว่าโซลูชันนี้:
DO $$
BEGIN
BEGIN
ALTER TABLE IF EXISTS bobby_tables RENAME COLUMN "dckx" TO "xkcd";
EXCEPTION
WHEN undefined_column THEN RAISE NOTICE 'Column was already renamed';
END;
END $$;
คุณสามารถทำได้โดยทำตามวิธี
ALTER TABLE tableName drop column if exists columnName;
ALTER TABLE tableName ADD COLUMN columnName character varying(8);
ดังนั้นมันจะวางคอลัมน์หากมีอยู่แล้ว จากนั้นเพิ่มคอลัมน์ลงในตารางที่ต้องการ
เพียงตรวจสอบว่าแบบสอบถามส่งคืน column_name หรือไม่
ถ้าไม่ทำสิ่งนี้:
ALTER TABLE x ADD COLUMN y int;
ที่ที่คุณใส่สิ่งที่มีประโยชน์สำหรับ 'x' และ 'y' และแน่นอนประเภทข้อมูลที่เหมาะสมที่ฉันใช้ int
DO $$ BEGIN BEGIN CREATE INDEX type_idx ON table1 USING btree (type); EXCEPTION WHEN duplicate_table THEN RAISE NOTICE 'Index exists.'; END; END;$$;
วิธีการเดียวกันในCREATE INDEX
;) ขอบคุณสำหรับคำตอบของคุณ