I am trying to convert this if statement from SQL Server to Postgres:
IF COL_LENGTH('Flex','revision') IS NOT NULL and OBJECT_ID(N'df_revision', N'D') IS NULL
BEGIN
ALTER TABLE Flex ADD CONSTRAINT df_revision DEFAULT 0 FOR revision
END
The purpose of this query is to set the default on the revision column if the table has that column.
Postgres has default values for columns. Those aren't "constraints".
To change the default value of a column you use:
alter table flex alter column revision set default 0;
If for some strange reason you don't know if the column exists in the table, you can use a PL/pgSQL block:
do
$$
begin
if exists (select *
from information_schema.columns
where table_name = 'flex'
and column_name = 'df_revision')
then
alter table flex alter column revision set default 0;
end if;
end;
$$
;
Related
I need to change the type of a column from varchar(255) to uuid. I am using:
ALTER TABLE table_name
ALTER COLUMN col TYPE UUID USING col::UUID;
But if there is some value in that column, which is not in uuid format, I will get this error:
SQL Error [22P02]: ERROR: invalid input syntax for type uuid: "some_text"
Is it possible to delete those rows which have values that cannot be converted to uuid?
Using uuid_or_null function that #JuliusTuskenis suggests you can simply do this:
ALTER TABLE table_name ALTER COLUMN col TYPE UUID USING uuid_or_null(col);
delete from table_name where col is null;
You have to define the function before that.
create function uuid_or_null(s text) returns uuid immutable AS
$$
begin
return s::uuid;
exception when others then
return null;
end;
$$ language plpgsql;
The way uuid_or_null is built is quite generic and more or less a pattern for safe casting - try to cast and if it bangs then react accordingly. There are several SO threads using it.
You can also sanitize the table upfront and then alter column type like this:
delete from table_name
where col !~* '^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$';
ALTER TABLE table_name ALTER COLUMN col TYPE UUID USING col::UUID;
So I have many tables in a db and I want to add two new columns for them.
For example, I have the columns "created_at" and "modified_at" and I want to create the columns "client_created_at" and "client_modified_at"
and at the same time populate these new columns with the values of "created_at" and "modified_at" of each table.
I imagine and have tried something like this:
ALTER TABLE patients, folders, auscultations, auscultations_notes, folder_ausc_association
ADD COLUMN client_created_at bigint, client_modified_at bigint;
UPDATE patients, folders, auscultations, auscultations_notes, folder_ausc_association
SET client_created_at = created_at, client_modified_at = modified_at
I'm not sure about how to structure it, any help would be appreciated!
In addition to the solution from Laurenz Albe, you could create an anonymous code block to do this job. Such a query can be very handy when you have many tables and don't want to create one statement per table.
DO $$
DECLARE
row record;
BEGIN
FOR row IN SELECT * FROM pg_tables WHERE schemaname = 'public'
LOOP
EXECUTE 'ALTER TABLE public.' || quote_ident(row.tablename) || ' ADD COLUMN client_created_at bigint, ADD COLUMN client_modified_at bigint;';
EXECUTE 'UPDATE ' || quote_ident(row.tablename) || ' SET client_created_at = created_at, client_modified_at = modified_at;';
END LOOP;
END;
$$;
Note: This code block adds the columns you want into all tables in the schema public - use it with care! You can adapt it to the tables you need by changing this query in the block:
SELECT * FROM pg_tables WHERE schemaname = 'public'
You'll have to use a statement per table for each of your two statements.
Define a maintenance window, and then perform for each table:
ALTER TABLE patients
ADD client_created_at bigint, client_modified_at bigint;
UPDATE patients
SET client_created_at = created_at, client_modified_at = modified_at;
ALTER TABLE patients
ALTER client_created_at SET NOT NULL,
ALTER client_created_at DEFAULT extract(epoch FROM current_timestamp),
ALTER client_modified_at SET NOT NULL,
ALTER client_modified_at DEFAULT extract(epoch FROM current_timestamp);
Use a different DEFAULT if you have different needs.
I am altering my existing table that is already filled with columns. In my table 'section', I want to set a character limit on the column "name", specifically type VARCHAR(60). However, there has not been a character limit before, and I would like to truncate any existing fields in the name column so that it now matches this restriction before my ALTER script.
I'm still getting several error messages, including in my LEFT statement, which is what I'm using to truncate the string in the "name" column. The LEFT statement is upset how I'm declaring the string to be truncated, whether I put the parameters in parenthesis or not. This is where I'm at so far:
DO $$
DECLARE
_name text;
_id uuid;
BEGIN
FOR _name, _id IN SELECT (name, id) FROM %SCHEMA%.section
LOOP
IF (_name > 60)
THEN
SET name = LEFT (_name, 60) WHERE id = _id;
END IF;
END LOOP;
RETURN NEW;
END $$;
Once I have this done, I know my ALTER script is very simple:
ALTER TABLE IF EXISTS %SCHEMA%.section ALTER COLUMN name TYPE VARCHAR(60);
You can also make use of the the USING syntax to ALTER TABLE. This allows you to do it as part of the ALTER, rather than as two separate commands.
ALTER TABLE myschema.mytable
ALTER COLUMN mycolumn
TYPE VARCHAR(60)
USING LEFT(mycolumn, 60);
https://www.postgresql.org/docs/9.6/static/sql-altertable.html
use an update query, like this:
UPDATE myschema.mytable
SET name = LEFT(mytable.name, 60)
WHERE LENGTH(mytable.name) > 60
I need to create a constraint or something similar, which only allows chars like A-Z, _ and - in a column.
How can this be done?
try this..It Work Fine In ms sql server
CREATE TABLE TestTable
(ID INT, FirstCol VARCHAR(100),
CONSTRAINT FirstCol CHECK (FirstCol NOT LIKE '%[^a-zA-Z\_-]%' ESCAPE '\'))
You can use regex-constraints if they are supported.
An example using PostgreSQL looks as follows:
CREATE TABLE user(
name VARCHAR
CONSTRAINT constraint_name CHECK (name ~* '^[A-Z_-]*$')
);
Note that this regular expression allows empty strings and no lower case characters.
Otherwise (in case of MySQL) you can use an insert-trigger:
DELIMITER $$
CREATE TRIGGER trigger_name BEFORE INSERT ON table_name
FOR EACH ROW
BEGIN
IF (NEW.phone REGEXP '^[A-Z_-]*$' ) = 0 THEN
SIGNAL SQLSTATE '12345'
SET MESSAGE_TEXT = 'Invalid content';
END IF;
END$$
DELIMITER ;
For MS SQL:
CREATE TABLE table_name(
name VARCHAR(100) CHECK (name NOT LIKE '%[^A-Z_-]%'
))
I've got a table that people have been inserting into getting the primary key by doing a
SELECT max(id)+1 from table_a;
I want to add some records to that table using a INSERT INTO table_a SELECT ... FROM table_b, table_c ... simple SQL script, and I'm wondering how to generate the primary keys. My first thought was to create a temporary sequence, but Oracle evidently doesn't have a select setval to set the first value. So how do I get the current value of max(id)+1 to set the "start with" parameter to my sequence?
I found something on-line that I thought would work:
COLUMN S new_value st select max(id)+1 S from table_a;
CREATE SEQUENCE cra_seq start with &st;
But it doesn't actually use st in the CREATE SEQUENCE but instead prompts me to enter it, which isn't what I need.
Is this something like what you want?
1 declare
2 id integer;
3 begin
4 select max(rownum)+1 into id from dual;
5 execute immediate 'create sequence myseq start with '||TO_CHAR(id);
6* end;
7 /
Couldn't you use the row_number function like so:
Insert Destination( Id, ...)
Select row_number() over( order by TableA.Col1... ) + MaxDestination.MaxId + 1 Num
, ....
From TableA, TableB,...
Cross Join ( Select Max(Id) MaxId From Destination ) MaxDestination
You can use the row_number analytical function to generate row numbers (1 through N).
Before you do the insert, get the max id that is in the table, and then add the row number to that max and it will populate your table correctly.
In PostgreSQL:
CREATE SEQUENCE new_seq;
ALTER TABLE existing_table ADD COLUMN new_serial_column bigint DEFAULT 0;
UPDATE existing_table SET new_serial_column = nextval('new_seq');
ALTER TABLE existing_table ALTER COLUMN new_serial_column SET NOT NULL;
ALTER TABLE existing_table ALTER COLUMN new_serial_column SET DEFAULT nextval('new_seq');
Although, that code is not idempotent, so check you havn't already created the new sequence, something like:
CREATE FUNCTION fixup_existing_table() RETURNS void AS $$
DECLARE
new_id bigint;
seq_column_exists integer;
BEGIN
SELECT Count(column_name) INTO seq_column_exists FROM information_schema.columns WHERE table_name='existing_table' and column_name='new_serial_column';
IF seq_column_exists != 0 THEN RETURN; END IF;
CREATE SEQUENCE new_seq;
ALTER TABLE existing_table ADD COLUMN new_serial_column bigint DEFAULT 0;
UPDATE existing_table SET new_serial_column = nextval('new_seq');
ALTER TABLE existing_table ALTER COLUMN new_serial_column SET NOT NULL;
ALTER TABLE existing_table ALTER COLUMN new_serial_column SET DEFAULT nextval('new_seq');
END;
$$ LANGUAGE plpgsql;
Then, you can safely call: SELECT fixup_existing_table() to alter the schema as many times as you like, i.e. call from some dumb update script.