Deny specific chars - sql

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_-]%'
))

Related

Alter column type deleting those that can't be converted

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;

Translate SQL Server Query to Postgres

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;
$$
;

PostgreSQL truncate existing field and altering to add character limit

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

PostgreSQL Varchar UID to Int UID while preserving uniqueness

Say I have a unique column of VarChar(32).
ex. 13bfa574e23848b68f1b7b5ff6d794e1.
I want to preserve the uniqueness of this while converting the column to int. I figure I can convert all of the letters to their ascii equivalent, while retaining the numbers and character position. To do this, I will use the translate function.
psuedo code: select translate(uid, '[^0-9]', ascii('[^0-9]'))
My issue is finding all of the letters in the VarChar column originally.
I've tried
select uid, substring(uid from '[^0-9]') from test_table;
But it only returns the first letter it encounters. Using the above example, I would be looking for bfaebfbbffde
Any help is appreciated!
First off, I agree with the two commenters who said you should use a UID datatype.
That aside...
Your UID looks like a traditional one, in that it's not alphanumeric, it's hex. If this is the case, you can convert the hex to the numeric value using this solution:
PostgreSQL: convert hex string of a very large number to a NUMERIC
Notice the accepted solution (mine, shame) is not as good as the other solution listed, as mine will not work for hex values this large.
That said, yikes, what a huge number. Holy smokes.
Depending on how many records are in your table and the frequency of insert/update, I would consider a radically different approach. In a nutshell, I would create another column to store your numeric ID whose value would be determined by a sequence.
If you really want to make it bulletproof, you can also create a cross-reference table to store the relationships that would
Reuse an ID if it ever repeated (I know UIDs don't, but this would cover cases where a record is deleted by mistake, re-appears, and you want to retain the original id)
If UIDs repeat (like this is a child table with multiple records per UID), it would cover that case as well
If neither of these apply, you could dumb it down quite a bit.
The solution would look something like this:
Add an ID column that will be your numeric equivalent to the UID:
alter table test_table
add column id bigint
Create a sequence:
CREATE SEQUENCE test_id
create a cross-reference table (again, not necessary for the dumbed down version):
create table test_id_xref (
uid varchar(32) not null,
id bigint not null,
constraint test_id_xref_pk primary key (uid)
)
Then do a one-time update to assign a surrogate ID to each UID for both the cross-reference and actual tables:
insert into test_id_xref
with uids as (
select distinct uid
from test_table
)
select uid, nextval ('test_id')
from uids;
update test_table tt
set id = x.id
from test_id_xref x
where tt.uid = x.uid;
And finally, for all future inserts, create a trigger to assign the next value:
CREATE OR REPLACE FUNCTION test_table_insert_trigger()
RETURNS trigger AS
$BODY$
BEGIN
select t.id
from test_id_xref t
into NEW.id
where t.uid = NEW.uid;
if NEW.id is null then
NEW.id := nextval('test_id');
insert into test_id_xref values (NEW.uid, NEW.id);
end if;
return NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
CREATE TRIGGER insert_test_table_trigger
BEFORE INSERT
ON test_table
FOR EACH ROW
EXECUTE PROCEDURE test_table_insert_trigger();
create one function which replace charter with blank which you not need in string,
CREATE FUNCTION replace_char(v_string VARCHAR(32) CHARSET utf8) RETURNS VARCHAR(32)
DETERMINISTIC
BEGIN
DECLARE v_return_string VARCHAR(32) DEFAULT '';
DECLARE v_remove_char VARCHAR(200) DEFAULT '1,2,3,4,5,6,7,8,9,0';
DECLARE v_length, j INT(3) DEFAULT 0;
SET v_length = LENGTH(v_string);
WHILE(j < v_length) DO
IF ( FIND_IN_SET( SUBSTR(v_string, (j+1), 1), v_remove_char ) = 0) THEN
SET v_return_string = CONCAT(v_return_string, SUBSTR(v_string, (j+1), 1) );
END IF;
SET j = j+1;
END WHILE;
RETURN v_return_string;
END$$
DELIMITER ;
Now you just nee to call this function in query
select uid, replace_char(uid) from test_table;
It will give you string what you need (bfaebfbbffde)
If you want to int number only i.e 13574238486817567941 then change value of variable, and also column datatype in decimal(50,0), decimal can stored large number and there is 0 decimal point so it will store int value as decimal.
v_remove_char = 'a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z';

PostgreSQL create table if not exists

In a MySQL script you can write:
CREATE TABLE IF NOT EXISTS foo ...;
... other stuff ...
and then you can run the script many times without re-creating the table.
How do you do this in PostgreSQL?
This feature has been implemented in Postgres 9.1:
CREATE TABLE IF NOT EXISTS myschema.mytable (i integer);
For older versions, here is a function to work around it:
CREATE OR REPLACE FUNCTION create_mytable()
RETURNS void
LANGUAGE plpgsql AS
$func$
BEGIN
IF EXISTS (SELECT FROM pg_catalog.pg_tables
WHERE schemaname = 'myschema'
AND tablename = 'mytable') THEN
RAISE NOTICE 'Table myschema.mytable already exists.';
ELSE
CREATE TABLE myschema.mytable (i integer);
END IF;
END
$func$;
Call:
SELECT create_mytable(); -- call as many times as you want.
Notes
The columns schemaname and tablename in pg_tables are case-sensitive. If you double-quote identifiers in the CREATE TABLE statement, you need to use the exact same spelling. If you don't, you need to use lower-case strings. See:
Are PostgreSQL column names case-sensitive?
pg_tables only contains actual tables. The identifier may still be occupied by related objects. See:
How to check if a table exists in a given schema
If the role executing this function does not have the necessary privileges to create the table you might want to use SECURITY DEFINER for the function and make it owned by another role with the necessary privileges. This version is safe enough.
Try this:
CREATE TABLE IF NOT EXISTS app_user (
username varchar(45) NOT NULL,
password varchar(450) NOT NULL,
enabled integer NOT NULL DEFAULT '1',
PRIMARY KEY (username)
)
I created a generic solution out of the existing answers which can be reused for any table:
CREATE OR REPLACE FUNCTION create_if_not_exists (table_name text, create_stmt text)
RETURNS text AS
$_$
BEGIN
IF EXISTS (
SELECT *
FROM pg_catalog.pg_tables
WHERE tablename = table_name
) THEN
RETURN 'TABLE ' || '''' || table_name || '''' || ' ALREADY EXISTS';
ELSE
EXECUTE create_stmt;
RETURN 'CREATED';
END IF;
END;
$_$ LANGUAGE plpgsql;
Usage:
select create_if_not_exists('my_table', 'CREATE TABLE my_table (id integer NOT NULL);');
It could be simplified further to take just one parameter if one would extract the table name out of the query parameter. Also I left out the schemas.
This solution is somewhat similar to the answer by Erwin Brandstetter, but uses only the sql language.
Not all PostgreSQL installations has the plpqsql language by default, this means you may have to call CREATE LANGUAGE plpgsql before creating the function, and afterwards have to remove the language again, to leave the database in the same state as it was before (but only if the database did not have the plpgsql language to begin with). See how the complexity grows?
Adding the plpgsql may not be issue if you are running your script locally, however, if the script is used to set up schema at a customer it may not be desirable to leave changes like this in the customers database.
This solution is inspired by a post by Andreas Scherbaum.
-- Function which creates table
CREATE OR REPLACE FUNCTION create_table () RETURNS TEXT AS $$
CREATE TABLE table_name (
i int
);
SELECT 'extended_recycle_bin created'::TEXT;
$$
LANGUAGE 'sql';
-- Test if table exists, and if not create it
SELECT CASE WHEN (SELECT true::BOOLEAN
FROM pg_catalog.pg_tables
WHERE schemaname = 'public'
AND tablename = 'table_name'
) THEN (SELECT 'success'::TEXT)
ELSE (SELECT create_table())
END;
-- Drop function
DROP FUNCTION create_table();
There is no CREATE TABLE IF NOT EXISTS... but you can write a simple procedure for that, something like:
CREATE OR REPLACE FUNCTION execute(TEXT) RETURNS VOID AS $$
BEGIN
EXECUTE $1;
END; $$ LANGUAGE plpgsql;
SELECT
execute($$
CREATE TABLE sch.foo
(
i integer
)
$$)
WHERE
NOT exists
(
SELECT *
FROM information_schema.tables
WHERE table_name = 'foo'
AND table_schema = 'sch'
);
There is no CREATE TABLE IF NOT EXISTS... but you can write a simple procedure for that, something like:
CREATE OR REPLACE FUNCTION prc_create_sch_foo_table() RETURNS VOID AS $$
BEGIN
EXECUTE 'CREATE TABLE /* IF NOT EXISTS add for PostgreSQL 9.1+ */ sch.foo (
id serial NOT NULL,
demo_column varchar NOT NULL,
demo_column2 varchar NOT NULL,
CONSTRAINT pk_sch_foo PRIMARY KEY (id));
CREATE INDEX /* IF NOT EXISTS add for PostgreSQL 9.5+ */ idx_sch_foo_demo_column ON sch.foo(demo_column);
CREATE INDEX /* IF NOT EXISTS add for PostgreSQL 9.5+ */ idx_sch_foo_demo_column2 ON sch.foo(demo_column2);'
WHERE NOT EXISTS(SELECT * FROM information_schema.tables
WHERE table_schema = 'sch'
AND table_name = 'foo');
EXCEPTION WHEN null_value_not_allowed THEN
WHEN duplicate_table THEN
WHEN others THEN RAISE EXCEPTION '% %', SQLSTATE, SQLERRM;
END; $$ LANGUAGE plpgsql;