Yes/No column with at most one yes - sql

I have a table which contains different versions of objects, e.g. Object A version 1, A version 2, B version 24... etc. One column stores the foreign key to the object, another stores the version number. It it obvious that these in combination should be unique and that is easy to implement with a unique index on both.
However, I want to be able to keep track of which version is the current one with an IsCurrent Yes/No column. The current version is not necessarily the one with the highest number. The problem here is that there is no way to define an index which is unique for yes values but allows many nos.
I find a lot of results when searching for this problem but none of them appear to work in Access. I have tried a "hack" in which I create a calculated column to use in a unique index which is -1 if current is true and the PK otherwise, but Access does not allow you to index calculated columns.
Is there any way to do this?

There is a trick, but you must allow only "yes/null" values for the isCurrent column - "yes" means "this row is current", and "null" otherwise.
This can be done using a validation check [isCurrent]="yes" Or [isCurrent] Is Null
Then create a composite + unique + ignore nulls index on id+isCurrent fields, and allow nulls.
Just click on the "index" button and define it in this way:
This prevents from inserting two rows with the same id + "yes" in the 'isCurrent' column, but allows many rows with the same id + null in the 'isCurrent' column.

Related

What happens when you insert rows in a SERIAL column with existing values?

I had to import a large CSV file into a database, and one column must be a unique ID for a purchase. I set the type of the column to SERIAL (yes, I know it's not actually a type) but since I already had some data in there with their own "random" purchase IDs I'm not sure about what will happen when I insert new rows.
Will the purchase ID take the values that are not already in use? Will it start after the biggest existing ID? Will it start at 1 and not care about if a value is already in use?
The underlying SEQUENCE will not care about the values you inserted (explicitly providing values for the serial column, overruling the default), you have to adapt manually to avoid duplicate key errors:
SELECT setval(pg_get_serial_sequence('tbl', 'id'), max(id)) FROM tbl;
'tbl' and 'id' being the names of table and column respectively.
Related:
How to reset postgres' primary key sequence when it falls out of sync?
How to copy both structure and contents of PostgreSQL table, but duplicate sequences?

Constraint in Oracle that depends on the value of a field

Imagine I have a table in an Oracle database with 3 columns, ID (PK), NAME and ACTIVE. If I wanted, for example, to have NAME and ACTIVE be unique together, I could easily do that. However, what I want is for NAME to be unique only when ACTIVE in that row is set to true (1). When ACTIVE is false (0), I want to be able to have an arbitrary number of rows with the same NAME (but different ID, of course). Is this possible to do?
Yes, in Oracle you can create a unique index with a CASE statement, in your case something like;
CREATE UNIQUE INDEX ix_uq ON test(
name,
CASE WHEN active = 0 THEN id ELSE 0 END
)
Since id is unique, we can have multiple rows with the same name as long as active=0 (since the uniqueness will be based on the (name,id)), while an active entry will check uniqueness on (name,0) which allows only a single active row per name.
An SQLfiddle to test with. Try to add a duplicate active entry, and it will not insert.
Here another possibility.
CREATE UNIQUE INDEX ix_uq ON test(decode(active,1,name,null))
With that, your index is the smallest possible because you don't need to index inactive values. It couls be usefull if your table began to be very big with juste small active values

can I insert a copy of a row from table T into table T without listing its columns and without primary key error?

I want to do something like this:
INSERT INTO T SELECT * FROM T WHERE Column1 = 'MagicValue' -- (multiple rows may be affected)
The problem is that T has a primary key column and so this causes an error as if trying to set the primary key. And frankly, I don't want to set the primary key either. I want to create entirely new rows with new primary keys but the rest of the fields being copied over from the original rows.
This is supposed to be generic code applicable to various tables. Well, so if there is no nice way of doing this, I will just write code to dynamically extract column names, construct the list etc. But maybe there is? Am I the first guy trying to create duplicate rows in a database or something?
I'm assuming by "Primary Key" you mean identity or guid data types that auto-assign or auto-increment.
Without some very fancy dynamic SQL, you can't do what you are after. If you want to insert everything but the identity field, you need to specify fields.
If you want to specify a value for that field, you need to specify all the fields in the SELECT and in the INSERT AND turn on IDENTITY_INSERT.
You don't gain anything from duplicating a row in a database (considering you didn't try to set the Primary Key). It would be wiser and will avoid problem to have another column called "amount" or something.
something like
UPDATE T SET Amount = Amount + 1 WHERE Column1 = 'MagicValue'
or if it can increase by more than 1 like amount of returned fields
Update T SET Amount = Amount * 2 WHERE Column1 = 'MagicValue'
I'm not sure what you're trying to do exactly but if the above doesn't work for what you're doing I think your design requires a new table and insert it there.
EDIT: Also as mentioned under your comments, a generic insert doesn't really make sense. Imagine, for this to work, you need the same number of fields, and they will hold the same values suggesting that they should also have the same names(even if it wouldn't require it to). It would basically be the same table structure twice.

How are these tasks done in SQL?

I have a table, and there is no column which stores a field of when the record/row was added. How can I get the latest entry into this table? There would be two cases in this:
Loop through entire table and get the largest ID, if a numeric ID is being used as the identifier. But this would be very inefficient for a large table.
If a random string is being used as the identifier (which is probably very, very bad practise), then this would require more thinking (I personally have no idea other than my first point above).
If I have one field in each row of my table which is numeric, and I want to add it up to get a total (so row 1 has a field which is 3, row 2 has a field which is 7, I want to add all these up and return the total), how would this be done?
Thanks
1) If the id is incremental, "select max(id) as latest from mytable". If a random string was used, there should still be an incremental numeric primary key in addition. Add it. There is no reason not to have one, and databases are optimized to use such a primary key for relations.
2) "select sum(mynumfield) as total from mytable"
for the last thing use a SUM()
SELECT SUM(OrderPrice) AS OrderTotal FROM Orders
assuming they are all in the same column.
Your first question is a bit unclear, but if you want to know when a row was inserted (or updated), then the only way is to record the time when the insert/update occurs. Typically, you use a DEFAULT constraint for inserts and a trigger for updates.
If you want to know the maximum value (which may not necessarily be the last inserted row) then use MAX, as others have said:
SELECT MAX(SomeColumn) FROM dbo.SomeTable
If the column is indexed, MSSQL does not need to read the whole table to answer this query.
For the second question, just do this:
SELECT SUM(SomeColumn) FROM dbo.SomeTable
You might want to look into some SQL books and tutorials to pick up the basic syntax.

MySql compound keys and null values

I have noticed that if I have a unique compound keys for two columns, column_a and column_b, then my sql ignores this constraint if one column is null.
E.g.
if column_a=1 and column_b = null I can insert column_a=1 and column_b=null as much as I like
if column_a=1 and column_b = 2 I can only insert this value once.
Is there a way to apply this constraint, other than maybe changing the columns to Not Null and setting default values?
http://dev.mysql.com/doc/refman/5.0/en/create-index.html
"A UNIQUE index creates a constraint such that all values in the index must be distinct. An error occurs if you try to add a new row with a key value that matches an existing row. This constraint does not apply to NULL values except for the BDB storage engine. For other engines, a UNIQUE index allows multiple NULL values for columns that can contain NULL."
So, no, you can't get MySQL to treat NULL as a unique value. I guess you have a couple of choices: you could do what you suggested in your question and store a "special value" instead of null, or you could use the BDB engine for the table. I don't think this minor difference in behaviour warrants making an unusual choice of storage engine, though.
I worked around this issue by creating a virtual (stored) column on the same table that was COALESCE(column_b, 0). I then made by unique composite index based upon that column (and the second column) instead. Works very well.
Of course this was probably not possible back in 2010 :)