Avoiding a two step insert in SQL - sql

Let's say I have a table defined as follows:
CREATE TABLE SomeTable
(
P_Id int PRIMARY KEY IDENTITY,
CompoundKey varchar(255) NOT NULL,
)
CompoundKey is a string with the primary key P_Id concatenated to the end, like Foo00000001 which comes from "Foo" + 00000001. At the moment, entries insertions into this table happen in 2 steps.
Insert a dummy record with a place holder string for CompoundKey.
Update the CompoundKey with the column with the generated compound key.
I'm looking for a way to avoid the 2nd update entirely and do it all with one insert statement. Is this possible? I'm using MS SQL Server 2005.
p.s. I agree that this is not the most sensible schema in the world, and this schema will be refactored (and properly normalized) but I'm unable to make changes to the schema for now.

Your could use a computed column; change the schema to read:
CREATE TABLE SomeTable
(
P_Id int PRIMARY KEY IDENTITY,
CompoundKeyPrefix varchar(255) NOT NULL,
CompoundKey AS CompoundKeyPrefix + CAST(P_Id AS VARCHAR(10))
)
This way, SQL Server will automagically give you your compound key in a new column, and will automatically maintain it for you. You may also want to look into the PERSIST keyword for computed columns which will cause SQL Server to materialise the value in the data files rather than having to compute it on the fly. You can also add an index against the column should you so wish.

A trigger would easily accomplish this

This is simply not possible.
The "next ID" doesn't exist and thus cannot be read to fulfill the UPDATE until the row is inserted.
Now, if you were sourcing your autonumbers from somwhere else you could, but I don't think that's a good answer to your question.
Even if you want to use triggers, an UPDATE is still executed even if you don't manually execute it.
You can obscure the population of the CompoundKey, but at the end of the day it's still going to be an UPDATE
I think your safest bet is just to make sure the UPDATE is in the same transaction as the INSERT or use a trigger. But, for the academic argument of it, an UPDATE still occurs.

Two things:
1) if you end up using two inserts, you must use transaction! Otherwise other processes may see the database in inconsistent state (i.e. seeing record without CompoundKey).
2) I would refrain from trying to paste the Id to the end of CompoundKey in transaction, trigger etc. It is much cleaner to do it at the output if you need it, e.g. in queries (select concat(CompoundKey, Id) as CompoundKeyId ...). If you need it as a foreign key in other tables, just use the pair (CompoundKey, Id).

Related

Informix select trigger to update column

Is it possible to increase the value of a number in a column with a trigger every time it gets selected? We have special tables where we store the new id and when we update it in the app, it tends to get conflicts before the update happens, even when it all takes less than a second. So I was wondering if it is not possible to set database to increase value after every select on that column? Do not ask me why we do not use autoincrement for ids because I do not know.
Informix provides the SERIAL and BIGSERIAL types (and also SERIAL8, but don't use that) which provide autoincrement support. It also provides SEQUENCES with more sophisticated autoincrements. You should aim to use one of those.
Trying to use a SELECT trigger to update the table being selected from is, at best, fraught with problems about transactions and the like (problems which both the types and sequences carefully avoid).
If your design team needs help making effective use of these, ask a new question outlining what you want to achieve.
Normally, the correct way to proceed is to make the ID column in each table that defines 'something' (the Orders table, the Customer table, …) into a SERIAL column and either not insert a value into the ID column or insert 0 into it. The generated value can be retrieved and used when creating auxilliary information — order items, etc.
Note that you could think about using:
CREATE TABLE xyz_sequence
(
xyz SERIAL NOT NULL PRIMARY KEY
);
and using:
INSERT INTO xyz_sequence VALUES(0);
and then retrieving the inserted value — in Informix ESQL/C, you'd use sqlca.sqlerrd[1], in other languages, other techniques. You can also delete the newly inserted record, or even all the records in the table. You can afford to ignore errors from the DELETE statement; sooner or later, the rows will be deleted. The next value inserted will continue where the prior ones left off.
In a stored procedure, you'd use DBINFO('sqlca.sqlerrd1') to get the inserted value. You'd use DBINFO('bigserial') to get the value if you use a BIGSERIAL type.
I found out possible answer in this question update with return value instead of doing it with select it seems better to return value directly from update as update use locks it should be more safer even when you use multithreading application. But these are just my assumptions. Hopefully it will help someone.

Update records in database automatically

Let me describe my scenario here.
I am having a table with multiple records, where the name is the same, as it's gonna be records for the same person updated on daily basis.
Right now, I am trying to find out the easiest way to update all the names accordingly.
Name is going to be updated (TestName -> RealName)
I want this change to be applied to all the records with the same name, in this case, "TestName"
I can do a single query, but I am trying to find if there's an automatic way to do this correctly.
Been trying using a triggers, but in most cases, I am ending with an infinite loop, as I am trying to update the table, where a trigger is actually bound to, so it's invoking another update and so on.
I don't need an exact solution, just give me some ropes about how it can be achieved, please.
The problem may be simply resolved by using the function pg_trigger_depth() in the trigger, e.g.:
create trigger before_update_on_my_table
before update on my_table
for each row
when (pg_trigger_depth() = 0) -- this prevents recursion
execute procedure before_update_on_my_table();
However, it seems that the table is poorly designed. It should not contain names. Create a table with names (say user_name) and in the old table store a reference to the new one, e.g.:
create table user_name(id serial primary key, name text);
create table my_table(id serial primary key, user_id int references user_name(id));
You can use event triggers in postgresql https://www.postgresql.org/docs/9.3/sql-createeventtrigger.html

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.

Constrain a table to have only one row

What's the cleanest way to constrain a SQL table to allow it to have no more than one row?
This related question discusses why such a table might exist, but not how the constraint should be implemented.
So far I have only found hacks involving a unique key column that is constrained to have a specific value, e.g. ALWAYS_0 TINYINT NOT NULL PRIMARY KEY DEFAULT (0) CONSTRAINT CHECK_ALWAYS_0 CHECK (ALWAYS_0 = 0). I am guessing there is probably a cleaner way to do it.
The ideal solution would be portable SQL, but a solution specific to MS SQL Server or postgres would also be useful
The cleanest way (I think) would be an ON INSERT trigger that throws an exception (thus preventing the row from being inserted). This also gives the client app a chance to recover gracefully.
I just solved the same problem on SQL Server 2008 by creating a table with a computed column and putting the primary key on that column:
CREATE TABLE MyOneRowTable (
[id] AS (1) PERSISTED NOT NULL CONSTRAINT pk_MyOneRowTable PRIMARY KEY,
-- rest of the columns go here
);
Use Grant to remove permissions for anyone to insert into the table after adding the one row
Your dba will be able to insert but the dba should only be running schema changes which are checked so should not be a problem in practice

SQL: No Identity feature workaround using triggers

I'm a little rusty with my triggers and what not and am trying to figure out this problem for a class:
In a database TEST, tables do not have the option of the IDENTITY feature. In other words, when we insert a row into the table “Users”, we would like the primary key “UserID” to auto-increment. Please suggest a workaround to implement this feature without such a built-in functionality.
(Hint: You may still use functions, stored procedures, sequences, triggers, etc)
Use an Int column for the table Primary Key called ID.
You can then use an instead of Insert Trigger, to populate/calculate the value to be inserted for ID.
The trigger will determine what the maximum existing ID is for the table in question (using select MAX ID from TableA) and then increment it by 1 for each record to be inserted.
If there are no records in the table then the ID value is 1.
You use a sequence, and it's very common with Oracle, which does not (or did not once, it may have changed) have identity columns. Since this is homework I'll let you figure out the rest from here.