Oracle. Constrain one row to have a certain value - sql

I want to add a constraint to a table where only one row can exist at any one time with a value in a certain column.
For example
create table MyTable
....
status varchar(1);
)
A check on status column would be 'O', 'C', 'P' for open , closed or pending.
There can be multiple Closed and Pending but only one (or none) row in the table can be Open.
Any pointers appreciated.

Create an unique function index:
CREATE UNIQUE INDEX myindex ON mytable( CASE status WHEN 'O' THEN 'O' END );
It will prevent from inserting two 'O' values, but will allow for other duplicate values.

Related

How to update a row with a unique constraint? (PostgreSQL)

In my database, I have a row with these values:
prop_1
prop_2
prop_3
4444
'aaaa'
'bbbb'
This row has a unique constraint so that duplicate rows are not posted.
I want to update the value of the row to be:
prop_1
prop_2
prop_3
4444
'aaaa'
'cccc'
But when I use the 'UPDATE':
UPDATE table
SET prop_3 = 'cccc'
WHERE prop_1 = 4444
I get this error:
error: duplicate key value violates unique constraint "constraint_name"
...
detail: 'Key (prop_1, prop_3)=(4444, cccc) already exists.',
But I know for sure that that row does not exist with the value of 'cccc' for prop_3.
Also, I'm not inserting a new row, I just want to update an existing row.
How would I update an existing row in Postgresql with a unique constraint?
EDIT:
When I try this statement:
SELECT * FROM table WHERE prop_3 = 'cccc'
0 rows / no rows are returned.
Additional Edit:
When I created this table originally, I think the way I set up the constraints was that I did not want another row to have the same exact matching values. I wanted to allow there to be duplicate values in rows, just not an exact duplicate of a row with the same 3 values.
I'm not sure how I set that up, it was so long ago.
I was thinking a potential solution would be to remove the unique constraint and create a new one the right way if I can't figure out how to update the current row.
I have no idea why, but when I changed my SQL UPDATE to:
UPDATE table
SET prop_3 = 'cccc'
WHERE prop_4 = 'another column row value'
It updated without any error.
Literally, all I changed was the WHERE value to another column in the row and the update worked without error. I don't know why that fixed it.

UNIQUE inside CHECK constraint

This question is new twist of An IF inside a check constraint SQL. I want to do something similar to the following check (which throws an ORA-00936: missing expression exception):
ALTER TABLE t_table
ADD CONSTRAINT chk_unique_active CHECK
(
( tb_active = 0 ) OR
( tb_active = -1 AND UNIQUE(tb_active, tb_img, tb_objid))
);
The objetive is to be sure (at DBMS level) that only one row with the same objid is active although inactive rows can be duplicated (an historical view of the rows).
It can be done in a trigger but it seems to be better using the check as explained in UNIQUE constraint vs checking before INSERT question.
is this possible?
Do this with a unique index:
create unique index unq_table_active
on ( (case when tb_active = -1 then tb_img end),
(case when tb_active = -1 then tb_objid end)
)
Oracle allows multiple rows with NULL values in a unique index.

How best to sum 2 columns and update 3rd column with sum?

I am looking for the best way to add 2 or more columns in a SQL Server table and update another column with there sum.
Yes, I know this is a dumb thing to do and calculations should be done at time of transaction but I am modifying an existing table where the data in a column now needs to be more detailed but numerous processes will still use the column value.
For example, a column name is TotalDailyMiles and numerous processes access and use that field. Now more detail is needed. 2 columns need to be added to the table TotalAMMiles and TotalPMMiles. These 2 columns will sum to the existing column. Changing all the processes that access the TotalDailyMiles column to use the 2 new columns instead is not an option. The data for the new columns in old records does not exist so the value for columns holding the sum of the 2 new columns cannot be based on the 2 new columns in old records because in old records the new column values will be 0, or maybe null but I'm leaning toward 0 so I can set the new columns as Not Null.
I'm thinking of using a trigger to update the column holding the sum based on the new columns changing but I'm hoping one of you smart people have a better option.
How about treating the existing column as its own value (which will be 0 in future rows), adding the two new columns, and then creating a calculated column with the same name as the old Total? Something like this (I'm assuming a data type of decimal(7, 2) but of course use what you have, though I hope it's not float):
EXEC sp_rename 'dbo.Miles.TotalDailyMiles', 'DailyMiles';
ALTER TABLE dbo.Miles ADD COLUMN AMMiles decimal(7, 2) NOT NULL
CONSTRAINT DF_Miles_AMMiles DEFAULT (0);
ALTER TABLE dbo.Miles ADD COLUMN PMMiles decimal(7, 2) NOT NULL
CONSTRAINT DF_Miles_PMMiles DEFAULT (0);
ALTER TABLE dbo.Miles ADD COLUMN TotalDailyMiles
AS (DailyMiles + AMMiles + PMMiles) PERSISTED;
Some possible housekeeping that might be needed on the DailyMiles column, too:
-- if not already NOT NULL
ALTER TABLE dbo.Miles ALTER COLUMN AMMiles decimal(7, 2) NOT NULL;
-- if not already defaulting to 0
ALTER TABLE dbo.Miles ADD
CONSTRAINT DF_Miles_DailyMiles DEFAULT (0) FOR DailyMiles;
You could additionally add a constraint that either DailyMiles must be 0, or AMMiles and PMMiles must both be 0:
ALTER TABLE dbo.Miles ADD CONSTRAINT CK_Miles_DailyOrAMPM
CHECK (DailyMiles = 0 OR (AMMiles = 0 AND PMMiles = 0));
As long as consumers of the data don't try to update the TotalDailyMiles column, you've solved your problem handily.

Oracle: selective uniqueness index issue with update

I have created selective unique index
CREATE UNIQUE INDEX fn_unique_idx
ON table1 (CASE WHEN is_deleted='N' THEN id ELSE null END,
CASE WHEN is_deleted='N' THEN name ELSE null END,
CASE WHEN is_deleted='N' THEN type ELSE null END);
So at any point of time I want only one entry with is_deleted 'N' for (id, name, type).
Insertion works fine i.e. it allows to enter mulitple is_deleted 'Y' and thows unique constriant exception when I try to insert with is_deleted = 'N' which is expected.
But when I try to update it is thowing
oracle error:
ORA-00600: internal error code, arguments: [qctVCO : bfc], [1], [0], [1], [871], [1], [2], [875], [], [], [], []
SQL : UPDATE table1 set is_deleted = 'Y' where id = 1, name = 'foo' and type =bar';
I want to set this current entry as deleted and insert a new entry with updated data and is_deleted = 'N'. This is basically for maintaining the history.
Can someone help me fix this issue.
Thanks.
that type of error is an oracle internal error - aka a bug...
what patch version are you on? perhaps go the the current one just in case.
If I understand what you are trying to accomplish, you want to be able to have several rows with the same (id, name, type). For one of these rows is_deleted = 'N' and for the rest of them is_deleted = 'Y'.
Is that correct?
If so, let me offer some ideas:
Remove the is_deleted field. Instead, have a version field and whatever the latest version is, this is the row which is not deleted. The unique constraint/index then naturally covers (id, name, type, version). This can complicate querying though.
Introduce 3 new fields: archive_id, archive_name, archive_type. The unique constraint still covers the original (id, name, type). The row is "deleted" by moving values to archive_* fields and NULL-ifying the original fields. This should work because tuples that contain all NULLs are not included in the (unique) index.
Have a separate table for archival data, without the unique constraint.
Maybe use a CONSTRAINT UNIQUE instead of the UNIQUE INDEX?
Also, let us know if there are any referential integrity constraints in play?
Agree with Branko on point #3. You could also add START & END dates to this separate table for archival data.

SQL Server 2008: Check constraints that guarantees that only one value in all rows is set to 1 and others are 0

There is a need to build constraint on the column that guarantees that only one value in all rows is 1 and all the others are 0.
Solution with triggers exists but I would like to have something built in.
Is such thing possible at all?
Edit
Actually I just noticed you are on SQL Server 2008 you could use a filtered index for this
CREATE UNIQUE NONCLUSTERED INDEX UIX ON YourTable (col) where col = 1
Original Answer
The easiest way would probably be to store this one special pk in a separate one row table. The no more than one row aspect can be enforced with check constraints.
CREATE TABLE OneRowTable
(
lock CHAR(1) DEFAULT 'X' NOT NULL PRIMARY KEY CHECK (lock = 'X'),
OtherTablePK int
);
Otherwise assuming you might have an id field comprised of positive integers you could add a computed column with the following definition
case when col=1 then -1 else id end
and add a unique constraint to that.