Câu trả lời:
Đây là phiên bản ngắn và ngọt bằng cách sử dụng câu lệnh "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;
$$
Bạn không thể truyền các tham số này dưới dạng tham số, bạn sẽ cần thay thế chuỗi trong phía máy khách, nhưng đây là truy vấn độc lập chỉ phát ra một thông báo nếu cột đã tồn tại, thêm nếu nó không và sẽ tiếp tục thất bại với các lỗi khác (như kiểu dữ liệu không hợp lệ).
Tôi không khuyên bạn nên thực hiện BẤT K of các phương pháp này nếu đây là các chuỗi ngẫu nhiên đến từ các nguồn bên ngoài. Bất kể bạn sử dụng phương pháp nào (chuỗi động phía cleint hoặc phía máy chủ được thực thi dưới dạng truy vấn), đó sẽ là một công thức cho thảm họa khi nó mở ra cho bạn các cuộc tấn công SQL SQL.
DO $$
thất bại. Tôi cũng đã thử DO $$;
thất bại, cho đến khi tôi mới bắt đầu khối DO $$DECLARE r record;
được đưa ra trong một ví dụ về tài liệu dev postgres .
END; $$
là một lỗi cú pháp (Postgres 9.3), END $$;
thay vào đó tôi phải sử dụng
EXCEPTION
) tổng quát hơn một chút và có thể được sử dụng cho các tác vụ không có IF NOT EXISTS
cú pháp - ví dụ ALTER TABLE ... ADD CONSTRAINT
.
Với Postgres 9.6, điều này có thể được thực hiện bằng cách sử dụng tùy chọnif 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;
Gọi:
SELECT f_add_col('public.kat', 'pfad1', 'int');
Trả TRUE
về thành công, khác FALSE
(cột đã tồn tại).
Tăng một ngoại lệ cho tên bảng hoặc loại không hợp lệ.
Điều này có thể được thực hiện với một DO
tuyên bố, nhưng các DO
tuyên bố không thể trả lại bất cứ điều gì. Và nếu nó được sử dụng nhiều lần, tôi sẽ tạo ra một chức năng.
Tôi sử dụng loại nhận dạng đối tượng regclass
và regtype
cho _tbl
và _type
đó a) ngăn chặn SQL injection và b) kiểm tra tính hợp lệ của cả hai ngay lập tức (cách giá rẻ nhất có thể). Tên cột _col
vẫn phải được làm vệ sinh cho EXECUTE
với quote_ident()
. Giải thích thêm trong câu trả lời liên quan này:
format()
yêu cầu Postgres 9.1+. Đối với các phiên bản cũ hơn, ghép thủ công:
EXECUTE 'ALTER TABLE ' || _tbl || ' ADD COLUMN ' || quote_ident(_col) || ' ' || _type;
Bạn có thể đủ điều kiện lược đồ tên bảng của bạn, nhưng bạn không phải.
Bạn có thể trích dẫn hai số định danh trong lệnh gọi hàm để bảo vệ trường hợp lạc đà và các từ dành riêng (nhưng dù sao bạn cũng không nên sử dụng bất kỳ từ nào trong số này).
Tôi truy vấn pg_catalog
thay vì information_schema
. Giải thích chi tiết:
Các khối chứa một EXCEPTION
mệnh đề như câu trả lời hiện được chấp nhận chậm hơn đáng kể. Điều này thường đơn giản và nhanh hơn. Các tài liệu:
Mẹo: Một khối chứa một
EXCEPTION
mệnh đề đắt hơn đáng kể để nhập và thoát so với một khối không có một mệnh đề. Do đó, không sử dụngEXCEPTION
mà không cần.
DO
tuyên bố, một sửa đổi nhỏ để chấp nhận DEFAULT
và điều này hoạt động hoàn hảo!
Sau khi chọn truy vấn sẽ trở lại true/false
, sử dụng EXISTS()
chức năng.
EXISTS () :
Đối số của EXISTS là một câu lệnh CHỌN hoặc truy vấn con tùy ý. Truy vấn con được đánh giá để xác định xem nó có trả về bất kỳ hàng nào không. Nếu nó trả về ít nhất một hàng, kết quả của EXISTS là "true"; nếu truy vấn con trả về không có hàng, kết quả của EXISTS là "false"
SELECT EXISTS(SELECT column_name
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'x'
AND column_name = 'y');
và sử dụng câu lệnh SQL động sau đây để thay đổi bảng của bạn
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
$$
hàm dưới đây sẽ kiểm tra cột nếu tồn tại trả về thông báo thích hợp khác, nó sẽ thêm cột vào bảng.
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;
$$
Đây về cơ bản là giải pháp từ sola, nhưng chỉ cần làm sạch một chút. Nó đủ khác biệt đến nỗi tôi không chỉ muốn "cải thiện" giải pháp của anh ấy (cộng với, tôi nghĩ rằng điều đó thật thô lỗ).
Sự khác biệt chính là nó sử dụng định dạng EXECUTE. Mà tôi nghĩ là sạch hơn một chút, nhưng tôi tin rằng bạn phải có trên PostgresQuery 9.1 hoặc mới hơn.
Điều này đã được thử nghiệm trên 9.1 và hoạt động. Lưu ý: Nó sẽ phát sinh lỗi nếu lược đồ / tên_bảng / hoặc data_type không hợp lệ. Điều đó có thể "cố định", nhưng có thể là hành vi đúng trong nhiều trường hợp.
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';
sử dụng:
select add_column('public', 'foo', 'bar', 'varchar(30)');
Có thể được thêm vào các kịch bản di chuyển gọi hàm và thả khi hoàn tất.
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();
Trong trường hợp của tôi, vì lý do nó được tạo ra như thế nào, có một chút khó khăn cho các tập lệnh di chuyển của chúng tôi để cắt qua các lược đồ khác nhau.
Để khắc phục điều này, chúng tôi đã sử dụng một ngoại lệ vừa bắt và bỏ qua lỗi. Điều này cũng có tác dụng phụ tốt đẹp là dễ nhìn hơn rất nhiều.
Tuy nhiên, hãy cảnh giác rằng các giải pháp khác có những ưu điểm riêng có thể vượt trội hơn giải pháp này:
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 $$;
Bạn có thể làm điều đó bằng cách làm theo.
ALTER TABLE tableName drop column if exists columnName;
ALTER TABLE tableName ADD COLUMN columnName character varying(8);
Vì vậy, nó sẽ thả cột nếu nó đã tồn tại. Và sau đó thêm cột vào bảng cụ thể.
Chỉ cần kiểm tra nếu truy vấn trả về một cột_name.
Nếu không, thực hiện một cái gì đó như thế này:
ALTER TABLE x ADD COLUMN y int;
Nơi bạn đặt một cái gì đó hữu ích cho 'x' và 'y' và tất nhiên là một kiểu dữ liệu phù hợp nơi tôi đã sử dụng int.
DO $$ BEGIN BEGIN CREATE INDEX type_idx ON table1 USING btree (type); EXCEPTION WHEN duplicate_table THEN RAISE NOTICE 'Index exists.'; END; END;$$;
cùng một cách tiếp cận trongCREATE INDEX
;) Cảm ơn câu trả lời của bạn,