It is possible to create table where for example:
CREATE TABLE IF NOT EXISTS router (
id BIGSERIAL PRIMARY KEY
, name_1 BOOLEAN NOT NULL DEFAULT FALSE
, name_2 BOOLEAN NOT NULL DEFAULT FALSE
, name_3 BOOLEAN NOT NULL DEFAULT FALSE
, name_4 BOOLEAN NOT NULL DEFAULT FALSE
, name_5 BOOLEAN NOT NULL DEFAULT FALSE
);
And with this table i would use something like lock to prevent of put more than 1 true value bettwen name_1 - name_5. For example if true value exist in name_1 is impossible to put something in name_2 - name_5. If exists in name_3 is impposible to put name_1,2,4 and 5
You can add a check constraint:
check (name_1 and not name_2 and not ...
or name_2 and not name_1 and not ...
or ...)
(There probably are shorter ways to express the boolean expression, but that's the gist of it.)
Related
In my Postgres database, I'm checking user answers for correctness by checking if two IDs, "user_answered_id" and "expected_answer_id", are equivalent. If the user doesn't provide a "user_answered_id", then we still mark their answer as incorrect.
In Postgres, the following queries
select case when 1 != null then TRUE else FALSE end as test;
select case when 1 = null then TRUE else FALSE end as test;
both result in FALSE. This is true for any number check (e.g., when 2 != null, when 3 != null, ..., etc.
Why doesn't CASE WHEN show TRUE for 1 != null?
Must I put in the check "or is null"? E.g.,
CASE WHEN
user_answered_id != expected_answer_id
OR user_answered_id IS NULL
THEN TRUE
ELSE FALSE
END as user_incorrect_tally
What you are looking for is: IS DISTINCT FROM
select 2 is distinct from null;
?column?
----------
t
select 2 is distinct from 1;
?column?
----------
t
From the docs:
datatype IS DISTINCT FROM datatype → boolean
Not equal, treating null as a comparable value.
1 IS DISTINCT FROM NULL → t (rather than NULL)
NULL IS DISTINCT FROM NULL → f (rather than NULL)
SQL uses three-valued logic: true, false, and null. Null is not false. Null can be thought of as "no value".
Operations on null almost always yield null. So 1 != null is null. 1 = null is null. null = null is null. 5 < null is null. Etc.
To check for null, use is null and is not null.
Back to your query. is not distinct from and is distinct from are like = and != which treat null as a comparable value. So null is distinct from 1 will be true.
select
user_answered_id is distinct from expected_answer_id as user_incorrect
If you need to convert a null into a different value such as 0 or an empty string, use coalesce.
select
coalesce(user_answered_text, 'No Answer')
Your column is named "tally", but a tally means a count. If you intend to count a user's true and false answers use count with a filter.
select
count(user_answered_id) filter (
where user_answered_id = expected_answer_id
) as user_correct_tally,
-- count ignores null, this will only be the questions they tried to answer
count(user_answered_id) as user_answered_tally,
count(user_answered_id) filter (
where user_answered_is is distinct from expected_answer_id
) as user_incorrect_tally
Yes, You should check NULL value with is null, And last query you wrote is correct.
I suggest you to read below documents:
https://www.postgresql.org/docs/current/functions-comparison.html
I need to have a CHECK constraint on a datatype, which has to have a format of 010000 through 129999 with the zero preserved, but I don't know how to achieve this. Basically, as evident, it's a numeric month-year.
I have tried using numeric(6,0) and integer, but I don't know how to use a CHECK that preserves the leading zero.
I also don't know how I could achieve this more easily using character varying(6) and it's not preferred either, as I think it'll be harder to use in the application layer.
Any suggestions? I'm using Postgres.
Three ways (there may be more):
-- (1) use a date type for a date
CREATE TABLE mmyyyy
( id SERIAL NOT NULL PRIMARY KEY
, yyyymm01 DATE NOT NULL CHECK (date_trunc('month', yyyymm01) = yyyymm01)
);
INSERT INTO mmyyyy(yyyymm01) VALUES
('1901-01-01') ,('0001-01-01') ,('2016-02-01') ;
INSERT INTO mmyyyy(yyyymm01) VALUES ('1901-13-01') ; -- should fail
INSERT INTO mmyyyy(yyyymm01) VALUES ('2016-02-13') ; -- should fail
SELECT id, to_char(yyyymm01, 'mmyyyy') AS this FROM mmyyyy ;
-- (2) use a char type and apply the check on the cast_to_int result
CREATE TABLE omg
( id SERIAL NOT NULL PRIMARY KEY
, mmyyyy varchar(6) NOT NULL CHECK (
length(mmyyyy) = 6 AND
left(mmyyyy,2)::integer BETWEEN 1 AND 12)
);
INSERT INTO omg(mmyyyy) VALUES ('011901') ,('010001') ,('022016') ;
INSERT INTO omg(mmyyyy) VALUES ('131901') ; -- should fail
INSERT INTO omg(mmyyyy) VALUES ('002016') ; -- should fail
SELECT id, mmyyyy FROM omg ;
-- (3) use an int type and apply the check to the value/10000
CREATE TABLE wtf
( id SERIAL NOT NULL PRIMARY KEY
, mmyyyy INTEGER NOT NULL CHECK (
mmyyyy/10000 BETWEEN 1 AND 12)
);
INSERT INTO wtf(mmyyyy) VALUES
(11901) ,(10001) ,(22016)
;
INSERT INTO wtf(mmyyyy) VALUES (131901) ; -- should fail
INSERT INTO wtf(mmyyyy) VALUES (2016) ; -- should fail
SELECT id, to_char(mmyyyy, '099999') AS mmyyyy
FROM wtf
;
-- (extra) use an date/char/int type AS the baseclass for a domain(or type):
-- (this can come in handy if the "type" is used in more than one place)
CREATE DOMAIN omgwtf AS
INTEGER CHECK ( value/10000 BETWEEN 1 AND 12)
;
CREATE TABLE tralala
( id SERIAL NOT NULL PRIMARY KEY
, mmyyyy omgwtf NOT NULL
);
INSERT INTO tralala(mmyyyy) VALUES
(11901) ,(10001) ,(22016)
;
INSERT INTO tralala(mmyyyy) VALUES (131901) ; -- should fail
INSERT INTO tralala(mmyyyy) VALUES (2016) ; -- should fail
SELECT id, to_char(mmyyyy, '099999') AS mmyyyy
FROM tralala
;
The output:
CREATE TABLE
INSERT 0 3
ERROR: date/time field value out of range: "1901-13-01"
LINE 1: INSERT INTO mmyyyy(yyyymm01) VALUES ('1901-13-01') ;
^
HINT: Perhaps you need a different "datestyle" setting.
ERROR: new row for relation "mmyyyy" violates check constraint "mmyyyy_yyyymm01_check"
DETAIL: Failing row contains (4, 2016-02-13).
id | this
----+--------
1 | 011901
2 | 010001
3 | 022016
(3 rows)
CREATE TABLE
INSERT 0 3
ERROR: new row for relation "omg" violates check constraint "omg_mmyyyy_check"
DETAIL: Failing row contains (4, 131901).
ERROR: new row for relation "omg" violates check constraint "omg_mmyyyy_check"
DETAIL: Failing row contains (5, 002016).
id | mmyyyy
----+--------
1 | 011901
2 | 010001
3 | 022016
(3 rows)
CREATE TABLE
INSERT 0 3
ERROR: new row for relation "wtf" violates check constraint "wtf_mmyyyy_check"
DETAIL: Failing row contains (4, 131901).
ERROR: new row for relation "wtf" violates check constraint "wtf_mmyyyy_check"
DETAIL: Failing row contains (5, 2016).
id | mmyyyy
----+---------
1 | 011901
2 | 010001
3 | 022016
(3 rows)
CREATE DOMAIN
CREATE TABLE
INSERT 0 3
ERROR: value for domain omgwtf violates check constraint "omgwtf_check"
ERROR: value for domain omgwtf violates check constraint "omgwtf_check"
id | mmyyyy
----+---------
1 | 011901
2 | 010001
3 | 022016
(3 rows)
I ended up using a yyyymm format, as suggested by #lad2025.
I created a table "TEST" and i tried to input some data however i got an error. The error is ERROR: new row for relation "test" violates check constraint "test_status_check" DETAIL: Failing row contains (5 , 2015-07-21, 15:00:00, I7 , 9 , NULL, NULL).
I think it is because of the null of the status. Therefore, i tried put a null in the test table but still not working
Create table test(
clientID CHAR (20),
startDate date,
startTime time,
instructorNO CHAR(20),
centreID CHAR(20),
status CHAR(4) CHECK (status IN ('Fail','Pass')) NULL,
reason VARCHAR(400),
omitted...
)
ERROR: new row for relation "test" violates check constraint "test_status_check" DETAIL: Failing row contains (5 , 2015-07-21, 15:00:00, I7 , 9 , NULL, NULL).
EDIT: I've completely changed my mind. Your existing code is valid (and the NULL part is unnecessary).
According to the documentation,
It should be noted that a check constraint is satisfied if the check expression evaluates to true or the null value. Since most expressions will evaluate to the null value if any operand is null, they will not prevent null values in the constrained columns. To ensure that a column does not contain null values, the not-null constraint described in the next section can be used.
So, something else is messed up. See here for an example of your original code working.
I have a stored procedure which takes a #flag as a parameter. That flag supposes to indicate to select null values or none null values.
for none null values my solution looks like that:
#Flag int
SET #Flag = NULL
WHERE ISNULL(column1,'') = ISNULL(#Flag,'')
Is there a way to accommodate none null values in similar manner ? If no what would be the most compact solution ?
Flag int
SET #Flag = NULL
select * from table
WHERE (#flag is null and column is null) or ((#flag is not null and column is not null) and #flag = column)
I advise to NEVER SET ANSI_NULL OFF, NEVER! That can lead to a lot of unnecessary maintenance pain.
No need for a compact solution like
ISNULL(column1,'') = ISNULL(#Flag,'')
Also that ill return null rows when #flag = '' and also ill return '' rows when flag is null
The only way I can think of is SET ANSI_NULL OFF and then do comparison: column1 = #flag
Today I was surprised by this case behaviour:
select case when null then true else false end;
case
------
f
I would expect it to return null since a null casted to boolean yelds a null not a false:
select null::boolean is null;
?column?
----------
t
Any comments on the rationale of this behaviour? What Am I missing?
Use something like
case when p.parent_id is null then false else true end as has_parent
In case you (also) need something like
if column is null then 'value'
Use:
COALESCE(column_name, 'replacment for null value') as column_name
In case you still need a case statement then use:
case COALESCE(column_name, 'asdf')
when 'asdf' then true
else false
end as desired_column_name
You're thinking of the CASE expression like it was taking the null as input to a function or operator, where null input generally results in null output:
regress=> SELECT 't'::boolean = NULL::boolean;
bool
------
(1 row)
wheras in fact it behaves like a WHERE clause in terms of null handling:
craig=> SELECT 't' WHERE NULL;
?column?
----------
(0 rows)
In WHERE clauses - and in CASE, a NULL result from a test expression is treated as "not true and therefore false". In some ways it's regrettable that the SQL standard didn't make a NULL result in a WHERE expression an error instead of treating it as false, but that's how it is.
This is yet another painful symptom of NULL's split personality, where the SQL spec can't decide if NULL means "unknown/undefined value" or "the absence of a value", much like the horrible mess with NULLs and aggregates.
You can use 'select' to check DATA column for null:
select
ID,
case (select 1 where DATA is null)
when 1 then 'no data'
else data
end
from ...
As the PostgreSQL documentation states:
If no WHEN condition is true then the value of the case expression is
the result in the ELSE clause. If the ELSE clause is omitted and no condition matches, the result is null.
Postgresql does not cast the output, and since you have an else condition, you're getting false. The next query returns a null value (Since there's no else condition)
select case when null then true end;
The CASE statement you wrote has two branches:
The one, when null then true, will never happen (because null is not equal to true)
And else branch, that will happen when there are no matches in regular when branches.
Example:
CASE WHEN val = 1 THEN 5
WHEN val = 2 THEN 10
ELSE 20 /*all other values, including null*/
END
select case when null is null then null else 1 end
returns null
select case null when then null else 1 end
returns 1