Column type to store 0..7 (octal) and bool values (oracle, postgres) - sql

I create a table in Oracle and I need to make a single column with the following values 0, 1, .., 6, 7
CREATE TABLE "LOC_DB"."TI_TS" (
"BIT" CHAR(1) CONSTRAINT cons_ti_ts_bit CHECK (BIT IN
('0','1','2','3','4','5','6','7'))
)
This was the only one best way? Or, maybe, we've got special types(octal number system)
(In case of PostgreSQL question interests too)
Edit 1
I was told that this practice is better:
CREATE TABLE "LOC_DB"."TI_TS" (
"BIT" NUMBER(1,0) NOT NULL CONSTRAINT const_ti_ts_bit CHECK (BIT between 0 and 7),
"ACTIV" NUMBER(1,0) NOT NULL CONSTRAINT const_ti_ts_active CHECK (ACTIV in (0,1)),
)
Thanks to Matt Allwood, a_horse_with_no_name

Related

What data type to use for ratings in PostgreSQL

I am making a table right now, and I'm confused about what to use, because I used to use smallint(6) but it doesn't work in PostgreSQL.
If the column can only have integer values between 1 and 5 you can use a SMALLINT for it with a CHECK constraint.
For example:
create table review (
rating smallint not null check (rating between 1 and 5)
);
The NOT NULL constraint ensures the column always has values.
The CHECK constraint ensures values are always between 1 and 5, and that, for example, a value 6 won't be accepted.

Check if a value appears only once in a column

I want to create a table for managing versions of parameters...
In this table there is a column of type char that lets me know what version I have to use :
create table PARAMETERS_VERSION (
ID number not null,
VERSION number not null,
DATE_START date not null,
DATE_END date check(DATE_START <= DATE_END) not null
ISUSED char(1) check(ISUSED in ('Y','N')) not null,
constraint PARAMETERS_VERSION_VERSION_PK primary key (ID),
constraint PARAMETERS_VERSION_VERSION_UK unique (ISUSED)
);
How to define a unique constraint on the column ISUSED to have only a single row with the value 'Y' (and the others with 'N') ?
By the way, is my check constraint on DATE_END is correct ?
Oracle doesn't quite support partial or filtered indexes. Instead, you can use a functional index with some cleverness:
create unique index idx_parametersversion_isused
on parameters_version(case when is_used = 'Y' then -1 else id end);
That is, when is_used has any value other than Y, then the primary key is used. When it is Y, then a constant is used, so two values will conflict.

PL/SQL - Only one value for a person

I have the following table.
CLASS_HAS_STUDENTS (
PER_SSN INTEGER NOT NULL,
PER_YEAR INTEGER NOT NULL, /*These two are PKs for a student*/
SCHOOL_CODE INTEGER NOT NULL, /*PK for a school*/
CLASS_YEAR INTEGER NOT NULL,
CLASS_NUMBER INTEGER NOT NULL,
CLASS_TEACHTYPE CHAR(3) NOT NULL, /*These three are PKs for a class*/
STUDCLASS_STATUS CHAR(1) NOT NULL
constraint CKC_STUDCLASS_STATUS_CLASS_TI check (StudClass_Status IN ('E', 'Y', 'T', 'P', 'F')),
STUDCLASS_LISTNUMBER INTEGER NOT NULL,
STUDCLASS_ROLLNUMBER INTEGER NOT NULL
);
(This code lacks some minor constraints)
Now, I need a way to check that one PER_SSN/PER_YEAR (a person's PK) can only have one 'E' ("Enrolled") status. I can't do this with a trigger (given I'm selecting from the same table) and I don't know if I can do this with a check constraint (can I use COUNT() here?). Any help is appreciated.
You can create a function-based unique index to enforce this sort of thing. You can't create a constraint as such.
This takes advantage of the fact that Oracle b-tree indexes do not index NULL data so the index will only have entries for the rows where studclass_status is E.
CREATE UNIQUE INDEX idx_one_enrolled
ON class_has_students( CASE WHEN studclass_status = 'E'
THEN per_ssn
ELSE null
END,
CASE WHEN studclass_status = 'E'
THEN per_year
ELSE null
END );
I'm a little confused by your question. I'm guessing you either want to:
1) Prevent insert of more than one status per student (in which case a trigger would be appropriate)
or
2) Use a SELECT statement to find students already in the table, in which case you want to do something like:
SELECT PER_SSN, PER_YEAR, STUDCLASS_STATUS, COUNT(*)
FROM CLASS_HAS_STUDENTS
WHERE STUDCLASS_STATUS = 'E'
HAVING COUNT(*) > 1
GROUP BY PER_SSN, PER_YEAR, STUDCLASS_STATUS;
You should be able to do this with a partial unique index. To make sure you only have one enrolled class for every ssn this should work:
CREATE UNIQUE INDEX ssn_enrollments ON class_has_students(per_ssn)
WHERE studclass_status='E';
Note that this feature is not supported in all SQL implementations, but PostgreSQL has supported since at least version 8.

Create named Column default in CREATE TABLE statement in ANSI SQL

I want to create a named default value in an ANSI compliant fashion, if possible, in a CREATE TABLE statement
If I try to add the CONSTRAINT as I would normally write it in an ALTER TABLE statement, it fails (at least in SQL SERVER, though I emphasise I am hoping to find an ANSI complaint statement as I would prefer it to work over a variety of Ado.NET DbConnections).
Example:
CREATE TABLE [dbo].[MyExample]
(
Id int NOT NULL IDENTITY (1, 1),
Name varchar(512) NOT NULL,
IsActive bit NOT NULL,
CONSTRAINT PK_MyExample PRIMARY KEY CLUSTERED (Id),
CONSTRAINT DF_MyExample_IsActive DEFAULT (1) FOR [IsActive]
)
Error:
Incorrect syntax near 'for'.
In terms of the SQL-92 Standard -- which is both ISO (I = International) and ANSI (A + American), by the way -- DEFAULT is not a constraint that may be given a name. In SQL-92 the DEFAULT can only be defined inline with the column definition and must be between the data type and the NOT NULL (if used) e.g.
CREATE TABLE T (c INTEGER DEFAULT 1 NOT NULL UNIQUE);
Note you have much non-Standard syntax in your small example:
square brackets as quoted identifiers (should be double quotes)
non-compliant data type (e.g. incorrect bit null behaviour)
abbreviated data types (e.g. int rather than INTEGER)
IDENTITY
CLUSTERED
Is it not ANSI compliant?
CREATE TABLE [dbo].[MyExample]
(
Id int NOT NULL IDENTITY (1, 1),
Name varchar(512) NOT NULL,
IsActive bit NOT NULL CONSTRAINT DF_MyExample_IsActive DEFAULT (1),
CONSTRAINT PK_MyExample PRIMARY KEY CLUSTERED (Id)
)

Constraints in SQL Database

I need to have a table in T-SQL which will have the following structure
KEY Various_Columns Flag
1 row 1 F
2 row_2 F
3 row_3 T
4 row_4 F
Either no rows, or at most one row can have the Flag column with the value T. My developer claims that this can be achieved with a check constraint placed on the table.
Questions:
Can such a constraint be placed on the database itself (ie an inter-row constraint) at the database level, rather than in business rules for updating or inserting rows
Is such a table in normal form?
Or would normal form require removing the Flag column, and instead (say) had another simple table or variable containing the value of row which had Flag=T, ie in the above case row=3.
1 No. A check constraint is per row. No other constraint will do this either.
You need one of:
a trigger (all versions)
indexed view with filter Flag = T, and unique index on Flag (SQL Server 2000+)
filtered index (SQL Server 2008)
2 Good enough
3 Overkill really. You're splitting the same data up to avoid one the solutions above. But using a one row table, FK for the ID columns, and a unique constraint on Flag
My developer claims that this can be
achieved with a check constraint
placed on the table.
SQL Server does not directly** support subqueries in CHECK constraints (a requirement for Full SQL-92; SQL Server is only compliant with Entry Level SQL-92, broadly speaking).
While there are almost certainly better ways of enforcing this constraint in SQL Server, purely out of interest it can indeed be achieved using a row-level CHECK constraint and a UNIQUE constraint e.g. here's one way:
CREATE TABLE YourStuff
(
key_col INTEGER NOT NULL UNIQUE,
Various_Columns VARCHAR(8) NOT NULL,
Flag CHAR(1) DEFAULT 'F' NOT NULL
CHECK (Flag IN ('F', 'T')),
Flag_key INTEGER UNIQUE,
CHECK (
(Flag = 'F' AND Flag_key = key_col)
OR
(Flag = 'T' AND Flag_key = NULL)
)
);
The issue here is that you will need to maintain the Flag_key column's values 'manually'. Replacing the column + CHECK with a calculated column would mean the values are maintained automatically:
CREATE TABLE YourStuff
(
key_col INTEGER NOT NULL UNIQUE,
Various_Columns VARCHAR(8) NOT NULL,
Flag CHAR(1) DEFAULT 'F' NOT NULL
CHECK (Flag IN ('F', 'T')),
Flag_key AS (
CASE WHEN Flag = 'F' THEN key_col
ELSE NULL END
),
UNIQUE (Flag_key)
);
** While SQL Server does not directly support subqueries in CHECK constraints, there is a workaround in some cases using a user defined function (UDF) e.g.
CREATE FUNCTION dbo.CountTFlags ()
RETURNS INTEGER
AS
BEGIN
DECLARE #return INTEGER;
SET #return = (
SELECT COUNT(*)
FROM YourStuff
WHERE Flag = 'T'
);
RETURN #return;
END;
CREATE TABLE YourStuff
(
key_col INTEGER NOT NULL UNIQUE,
Various_Columns VARCHAR(8) NOT NULL,
Flag CHAR(1) DEFAULT 'F' NOT NULL
CHECK (Flag IN ('F', 'T')),
CHECK (1 >= dbo.CountTFlags())
);
Note that the UDF approach won't work in every case and that caution is required. The important point is that UDF will be evaluated for each row affected (rather than at the SQL statement or transaction level, as you may expect). In this case, the constraint needs to be true for every row affected and therefore -- I think! -- it is safe. For more details, see Trouble with CHECK Constraints by David Portas.
Personally, I would simply use a second table to model Flag, which would only involve keys and a foreign key e.g.
CREATE TABLE YourStuff
(
key_col INTEGER NOT NULL UNIQUE,
Various_Columns VARCHAR(8) NOT NULL
);
CREATE TABLE YourStuffFlag
(
key_col INTEGER NOT NULL UNIQUE
REFERENCES YourStuff (key_col)
);
Is [my] table in normal form?
You should by aiming for Fifth normal form (5NF). Whether you have achieved this depends upon the design of Various_Columns. I do not believe that your Flag falls fowl of the requirements for 5NF and I do not see any update, delete or insert anomalies (which is the point of normalization but a 5NF design can still exhibit anomalies). That said, to switch the row that gets the flag attibute, my two-table design requires a single UPDATE statement while your single-table design requires two ;)