Enforce unique constraint only on some queries - sql

Suppose I've got an accounts table, and in that table I've got a profile_id column. I'd like to be able to have multiple rows in the column with the same profile_id, but I'd also like to be able to say "insert iff no other row has this profile_id value." Normally, I'd use a unique constraint for this, but that offers me no way to override it when I expect the profile_id to exist.
Is there a way to (without race conditions) have an insert error or fail if a value already exists, or alternatively, have an insert ignore a constraint and enter values that don't match the constraint?

You could build a partial index; it is at least a partial solution to your problem. That is, define a column that says something like:
only_uniques_allowed
And then build the index:
create unique index unq_t_profile_id_uniques on (profile_id) where only_uniques_allowed;
Then, any row that has only_uniques_allowed set to true would have a unique profile_id among the rows with this setting.
However, you don't really want to enforce a constraint, so perhaps what you want to do is better done at the application level. Or perhaps it suggests that you need a different data model.

Related

How are UNIQUE constraints implemented when an INSERT command is called in Postgresql?

If I, for example, had a table with a single (integer) attribute a with a UNIQUE constraint placed on the attribute, and I tried inserting a single value 2 into the table, what would query planner do in this case? For example, would the the UNIQUE constraint be checked by doing a sequential scan through the table, or something else?
I tried understanding what happens by running EXPLAIN ANALYZE in postgres, but unfortunately it did not reveal anything of use. Any help would be much appreciated.

Is it possible that setting a column as a primary key could turn some values in a column NULL?

I needed to modify by a datatable by setting the id column as the PRIMARY KEY in order to work with it on a client I am developing. However, I forgot to copy/screen-cap the already existing records and now I get the feeling data is missing. Was it possible that setting a column as the primary key could've affected data in other columns?
FYI, I set the primary key by going into the design, right clicking the column, and clicking on "Set as primary key"
What 'design' tool were you using? The only thing that would make sense would be if there were duplicate ids, but that should give an error instead of picking random rows. The only other thing I can think of would be if you had a foreign key set to cascade deletes and deleted rows in the child table.
You only say you have an 'idea' that rows were deleted. That makes me think that you might be just doing some simple 'select top(100) *' query or something and don't see the same data as you had before, i.e. you used to see ids like 2093939 and now you only see 1, 2, 3, etc.
When creating a primary key or a clustered index, that can alter the order that rows are returned by default. Creating a clustered primary key would most likely then return the rows in ascending order in that case by default. Could this be the case?

Create autoserial column in informix

is it possible to create a autoserial index in order 1,2,3,4... in Informix and what would be the syntax. I have a query and some of my timestamps are identical so I was unable to query using a timestamp variable. Thanks!
These are the commands that I ran to add an id field to an existing table. While logged in to the dbaccess console.
alter table my_table add id integer before some_field;
create sequence myseq;
update my_table set id = myseq.nextval;
drop sequence myseq;
alter table my_table modify (id serial not null);
Thanks to #ricardo-henriques for pointing me in the right direction. These commands will allow you to run the instructions explained in his answer on your database.
That would be the SERIAL data type.
You can use, as #RET mention the SERIAL data type.
Next you will struggle with the fact that you can't add a SERIAL column to an existing table. Ways to work around:
Add an INTEGER column, populate with sequential numbers and then alter the column to SERIAL.
Unload the data to a file, drop the table and recreate it with the new column.
Create a new table with the new column, populate the new table with the data from the old, drop the old and rename the new.
...
Bear in mind that they may not be unique. Hence you have to create an unique index or a primary key or an unique constraint in the column to prevent duplicates.
Another notes you should be aware:
- Primary key don't allow NULLS, unique index and unique constraints allow (as long there is only one record), so you should specify NOT NULL on the column definition.
- If you use a primary key or a unique constraint you can create a foreign key to it.
- In primary key and unique constraint the validation of the uniqueness of the record is done in the end of the DML, for the unique index it is done row a row.
Seems you're getting your first touch with informix, welcome. Yes it can be a little bit hard on the beginning just remember:
Always search before asking, really search.
When in doubt or reached a dead end then ask away.
Try to trim down your case scenario, built your own case the simple away you can, these will not only help us to help us but you will practice and in some cases find the solution by yourself.
When error is involve always give the error code, in informix it is given at least one error code and sometimes an ISAM error too.
Keen regards.

SQL Server: How to allow duplicate records on small table

I have a small table "ImgViews" that only contains two columns, an ID column called "imgID" + a count column called "viewed", both set up as int.
The idea is to use this table only as a counter so that I can track how often an image with a certain ID is viewed / clicked.
The table has no primary or foreign keys and no relationships.
However, when I enter some data for testing and try entering the same imgID multiple times it always appears greyed out and with a red error icon.
Usually this makes sense as you don't want duplicate records but as the purpose is different here it does make sense for me.
Can someone tell me how I can achieve this or work around it ? What would be a common way to do this ?
Many thanks in advance, Tim.
To address your requirement to store non-unique values, simply remove primary keys, unique constraints, and unique indexes. I expect you may still want a non-unique clustered index on ImgID to improve performance of aggregate queries that would otherwise require a scan the entire table and sort. I suggest you store an insert timestamp, not to provide uniqueness, but to facilitate purging data by date, should the need arise in the future.
You must have some unique index on that table. Make sure there is no unique index and no unique or primary key constraint.
Or, SSMS simply doesn't know how to identify the row that was just inserted because it has no key.
It is generally not best practice to have a table without a (logical) primary key. In your case, I'd make the image id the primary key and increment the counter. The MERGE statement is well-suited for performing and insert or update at the same time. Alternatives exist.
If you don't like that, create a surrogate primary key (an identity column set as the primary key).
At the moment you have no way of addressing a specific row. That makes the table a little unwieldy.
If you allow multiple rows being absolutely identical, how would you update/delete one of those rows?
How would you expect the database being able to "know" what row you referred to??
At the very least add a separate identity column (preferred being the clustered index, too).
As a side note: It's weird that you "like to avoid unneeded data" but at the same time insert duplicates over and over again instead of simply add up the click count per single image...
Use SQL statements, not GUI, if the table has not primary key or unique constraint.

implementing complex check constraints

I have the following tables:
CREATE TABLE group_systems
(
group_name,
system_name,
section_name,
created_date,
decom_date,
status (Active, Deactivate)
)
CREATE TABLE systems
(
system_name,
section_name
)
A system is identified by the key (system_name, section_name). There can be dup system names but no dup section name.
In the groups table, I want to enforce the constraint that only one system in a section in a group can be active. However, because the groups table is also a history table, I can't just use the unique constraint (group_name, section_name, system_name). I have to use a check constraint that runs a subquery. There's also some additional constraints that are subqueries.
The problem is that inserting a benchmark of 100k records takes a long time (due to the subqueries).
Is it better to build another table active_systems_for_groups that references back to the group_systems table? That way, I can add the unique constraint to active_systems_for_groups that enforces only one active system per section per group and keep building complex constraints by adding more tables.
Is there a better way to handle complex check constraints?
You can enforce the "single active record" pattern in two ways:
The solution you suggest, which is to create a table that holds only the primary key values of the active records from the multiple-records-allowed table. Those values also serve as a primary key in the active records table.
Adding a column to another table that represents the objects that can have only a single active record each. In this case that would mean adding a column active_group_name to systems. This column would be a foreign key to the multiple-records-allowed table.
Which is preferable depends, in part, on whether every section is required to have an active group, whether it's common (but not required) for a section to have an active group, or whether it's only occasionally true that a section has an active group.
In the first case (required), you would use option (2) and the column could be declared NOT NULL, preserving complete normalization. In the second case (common) you would need to make the column NULLable but I'd probably still use that technique for convenience of JOINs. In the third case (occasional), I'd probably use option (1) since it might well improve performance when JOINing to get the active records.
Since you never answered which RDBMS you're using I'll throw this out there for others who might be interested in another way to easily handle this constraint in SQL Server (2008 or later).
You can use a filtered unique index to effectively put a constraint on the number of "active" rows for a given type. As an example:
CREATE UNIQUE INDEX My_Table_active_IDX ON My_Table (some_pk) WHERE active = 1
This approach has several advantages:
It's declarative
It's self-contained within the single table (no
FKs, no other objects that you need to keep updated, etc.)