How to correctly setup CHECK constraint with multiple LIKE statements? - tsql

I am trying to enforce a simple rule for inserting values of bank_account column:
- bank account can consist of only digits
- bank account can have one hyphen '-' or zero hyphens
- bank account can have one slash '/' or zero slashes
I have this check constraint:
alter table someTable
add constraint chk_bank check
(
(bank_account not like '%-%-%')
and
(bank_account not like '%/%/%')
and
(bank_account not like '%[^0123456789\-/]%')
)
And I have these bank_account numbers (they are fictional):
12-4414424434/0987
987654321/9999
NULL
41-101010101010/0011
500501502503/7410
NULL
60-6000070000/1234
7987-42516/7845
NULL
12-12121212/2121
When enabling the constraint I get this error:
The ALTER TABLE statement conflicted with the CHECK constraint "chk_bank".
The conflict occurred in database "x", table "someTable", column 'bank_account'.
I tried some select queries but I can't find the wrong numbers.
Is my check constraint written wrong? If so, how should I change it to match my requirements?
Does check constraint ignore NULL values or are these a problem?

According to the documentation, there is no escape character by default. You must use the escape clause to signify that the backslash is the escape character:
...and bank_account not like %[^0123456789\-/]%' escape '\'...

The easy way to check the logic is to select the conditions individually, e.g.:
select bank_account,
case when bank_account not like '%-%-%' then 1 else 0 end as CheckHyphens,
case when bank_account not like '%/%/%' then 1 else 0 end as CheckSlashes,
case when bank_account not like '%[^0123456789\-/]%' then 1 else 0 end as CheckIllegalCharacters,
case when bank_account not like '%[^0123456789\-/]%' escape '\' then 1 else 0 end as CheckIllegalCharactersWithEscape
from YourTable;
It becomes clear that your last condition is failing. Adding an escape clause corrects the pattern.

Related

Postgres CHAR check constraint not evaluating correctly

My goal is to have a column which accepts only values of the type letters and underscores in Postgres.
For some reason though I'm not managing to get Postgres to enforce the regex correctly.
Things I tried:
Produces column which won't accept strings with digits
action_type CHAR(100) check (action_type ~ '^(?:[^[:digit:]]*)$')
Produces column which won't accept anything
action_type CHAR(100) check (action_type ~ '^(?:[^[:digit:] ]*)$')
Produces column which won't accept anything
action_type CHAR(100) check (action_type ~ '^([[:alpha:]_]*)$'),
I have tried using multiple variations of the above as well as using SIMILAR TO instead of '~'. From my experience the column either accepts everything or nothing, depends on the given constraint.
I'm running this on the timescaledb docker image locally which is running PostgreSQL 12.5.
Any help would be greatly appreciated as I am at my wits end.
Try this:
CREATE TABLE your_table(
action_type text
);
ALTER TABLE your_table ADD CONSTRAINT check_letters
CHECK (action_type ~ '^[a-z\_]*$' )
This should only allow a-z characters and underscore _ for action_type column.
Also note that this is case sensitive, if you need case insensitive match, use ~* instead of ~
And also, this allows empty string, if you don't want that, use '^[a-z\_]+$' instead

Select column name containing forward slash in PostgreSQL

I am new to PostgreSQL and trying the below two scenarios:
Select a column name which already has a forward slash(/) in database (Road/Highway)
Using case on same column along with index
Select Road/Highway,
case
when index(upcase(Road/Highway), 'xturn') > 0 then 2
else 0
end as preferred_road
from abc_data;
But I am getting syntax error near index and for slash it is only taking Road.
Generally / means "division", so your column name is non-standard, much like working with keyword column names, column names with special characters must be quoted with double quotes. Use "Road/Highway" when referring to the column.

T-SQL Insert null if ' ' input is empty

My web-application allows for token replacements and therefore my SQL INSERT query looks something like this:
INSERT INTO mytable (Col1, Col2, Col3)
VALUES ('[Col1Value]', '[Col2Value]', '[Col3Value]')
The web app is replacing whats inside the brackets [Col1Value] with the input entered into the form field.
Problem is when an input field is left empty this query is still inserting ' ' just an empty space so the field is not considered null
I'm trying to use SQL's default value/binding section so that all columns that are null have a default value of -- but having my query insert a blank space ' ' is making it so SQL does not trigger the default value action and it still shows blank rather than my desired default value of --
Any ideas as to how I can solve this and make sure ' ' is inserted as a null rather than a space or empty space so it will trigger SQL replacing null with my default value of --
There is no easy going...
How are you inserting the values? If you create these statements literally you are stumbling on the dangerous fields of SQL injection... Use parameters!
One approach might be an insert through a Stored Procedure, another approach is an Instead Of TRIGGER and the third uses the fact, that the string-length does not calculate trailing blanks:
SELECT LEN('') --returns 0
,LEN(' ') --returns 0 too
You can use this in an expression like this:
CASE WHEN LEN(#YourInputValue)=0 THEN NULL ELSE #YourInputValue END

Creating a non-numeric Sequence in Postgres

I've come across a requirement to create a sequence in Postgres for generating a code (in string) which is expected to generate a unique code to increment by one for each new row and it should follow a six digit pattern.
For instance,
AC0001
AC0040
AC0201
AC3421
where the first two letters are chars and the remaining are integers.
I have created a sequence first,
CREATE SEQUENCE code_sequence START WITH 1
INCREMENT BY 1
CACHE 1;
Then, created a table,
CREATE TABLE account
(
code VARCHAR NOT NULL DEFAULT 'AC'||nextval('code_sequence'::regclass)::VARCHAR,
desc VARCHAR
);
This generates the code as AC1, AC2 etc. But, I want to have the code like AC0001, AC0002. Trying to "pad" zero's just after the 'AC'.
I would appreciate, if any one suggest a solution or idea for this problem.
Use to_char() to format the number:
CREATE TABLE account
(
code VARCHAR NOT NULL DEFAULT 'AC'||to_char(nextval('code_sequence'), 'FM0000'),
"desc" VARCHAR
);
Try the LPAD function.
CREATE TABLE account
(
code VARCHAR NOT NULL DEFAULT 'AC' || LPAD(nextval('code_sequence'::regclass), 4, '0')::VARCHAR,
desc VARCHAR
);

Table-wide constraint in Postgres

I'm rather new at Postgres. Is there any way that I can write a constraint for a table that checks ALL characters fields and tests to make sure that there are no leading or trailing characters IF there is any value in the field?
This way I don't have to itemize each and every character field when I write the constraint.
Thanks!
No, you cannot write such a constraint insofar as I am aware.
What you could do is to create a DOMAIN that has the check function and then make all of your table columns of that domain type. Assuming that the characters you refer to are spaces:
CREATE DOMAIN varchar_no_spaces AS varchar
CHECK ( left(VALUE, 1) <> ' ' AND right(VALUE, 1) <> ' ') );
There are many variations on this CHECK expression, including regular expression and using different or multiple characters. See the string functions for more options.
Then:
CREATE TABLE mytable (
f1 varchar_no_spaces,
...
);
Effectively you relay the constraint check to the level of the domain.