My webApp works with 2 DataBase servers Informix and DB2 (v9.5 run on localhost), when i work with informix DB i can insert null into a primary key (informix DB handle it and accepts nulls and auto increment the column (Serial8)) but when i switched to use DB2 it doesn't work and this error arrises (DB2 SQL Error: SQLCODE=-407, SQLSTATE=23502, SQLERRMC=TBSPACEID=2, TABLEID=280, COLNO=0, DRIVER=3.50.152 , sqlCode = -407) it looks like DB2 doesn't allow nulls for primary keys (BigInt) ,so how to enforce DB2 to allow nulls for primary key? in a word i want DB2 to allow me insert nulls for this column and auto increment the values in it each time i make an insert
here's the script to create the table:
CREATE TABLE corr.CORRESPONDENCE (
this is the specified col---->CORR_ID BIGINT NOT NULL,
CORR_NAME VARCHAR(255) NOT NULL,
CORR_NO VARCHAR(255),
CREATE_DATE_TIME TIMESTAMP NOT NULL,
DELIVERY_DATE_TIME DATE,
NO_OF_ATTACH INTEGER,
SITE_ID VARCHAR(20),
DELIVERY_ID VARCHAR(20),
CREATE_USER BIGINT NOT NULL,
SECURITY_ID BIGINT,
DELIVERY_BY VARCHAR(20),
WORKFLOW_ID BIGINT
)
DATA CAPTURE NONE ;
ALTER TABLE corr.correspondence ADD CONSTRAINT u143_159 PRIMARY KEY
(corr_id) ;
Which version of Informix? What is the schema of the table? What is the INSERT statement? Which API are you using to access Informix? Which platform is the client code running on? Which platform is the database server running on?
I'm not convinced that you can insert nulls into a SERIAL-like column in Informix. Do you have a primary key constraint on your table, or just a SERIAL8 column that has no NOT NULL and no PRIMARY KEY constraint on it? You cannot insert NULL directly into a SERIAL8 column (nor, by inference, into a SERIAL or BIGSERIAL column).
Demonstration (using a development version of Informix 11.70.FC6 on RHEL 5 Linux x86/64; client is ESQL/C based, and both client and server are on the same machine):
SQL[1910]: begin;
SQL[1911]: create table t1(s8 serial8 not null, v1 char(8) not null);
SQL[1912]: insert into t1(s8, v1) values(null, "works?");
SQL -391: Cannot insert a null into column (t1.s8).
SQLSTATE: 23000 at /dev/stdin:6
SQL[1913]: rollback;
SQL[1914]: begin;
SQL[1915]: create table t1(s8 serial8 primary key, v1 char(8) not null);
SQL[1916]: insert into t1(s8, v1) values(null, "works?");
SQL -703: Primary key on table (t1) has a field with a null key value.
SQLSTATE: IX000 at <<temp>>:2
SQL[1917]: rollback;
SQL[1918]: create table t1(s8 serial8, v1 char(8) not null);
SQL[1919]: insert into t1(s8, v1) values(null, "works?");
SQL -391: Cannot insert a null into column (t1.s8).
SQLSTATE: 23000 at <<temp>>:2
SQL[1920]: drop table t1;
SQL[1921]:
And bother, I forgot to restart the transaction after 1917!
This is behaving exactly as it should; you should not be allowed to insert a NULL into a SERIAL8 (or SERIAL, or BIGSERIAL) column. You can insert zeroes into those columns and a new value will be allocated automatically. But you cannot, and should not be able to, insert a NULL into the column.
DB2 is likewise correct in rejecting attempts to insert NULL into any of the columns in a primary key. It simply is not something you should be allowed to do.
Answering comments
Frank Computer commented:
Strange, I was under the impression that when loading data into a table with a SERIAL column, if I did not supply a value for the SERIAL column, it would convert the NULL to a zero during the insert, as if the loaded data contained a zero?
Also, with ISQL Perform, when I insert a new row into a table containing a SERIAL column, I don't supply a value for the SERIAL column, yet Perform displays a zero (0) and after hitting Escape, it converts it to the next highest INT value!
My immediate response was:
LOAD is done by a complex sub-routine in the client that munges the data, and it could/would deal with NULL for SERIAL columns.
With ISQL, Perform explicitly enforces 0 during data entry and reports back on the inserted value; again, the client-side code is preventing the error.
This is why it is important to know what is in use to demonstrate problems or features. Now I've got to make a LOAD and NULL demo using DB-Access...I don't think my SQLCMD program fixes up NULL for SERIAL columns during LOAD (or, if it does, I made that hack a long, long time ago).
Testing DB-Access (from IDS 11.70.FC2 on Mac OS X 10.7.4, this time), with:
xx.unl
|data for row 1|1|
|data for row 2|2|
xx.sql
BEGIN;
CREATE TABLE load_null(s8 SERIAL8 PRIMARY KEY, v32 VARCHAR(32) NOT NULL, i INTEGER NOT NULL);
LOAD FROM "xx.unl" INSERT INTO load_null;
ROLLBACK;
DB-Access Output
$ dbaccess stores xx
Database selected.
Started transaction.
Table created.
703: Primary key on table (load_null) has a field with a null key value.
847: Error in load file line 1.
Error in line 3
Near character position 41
Transaction rolled back.
Database closed.
$
This does not lend support to the 'DB-Access maps NULL for a SERIAL8 column into a zero' hypothesis. This is SERIAL8 rather than plain SERIAL, but when I changed SERIAL8 into SERIAL, I got the same error; ditto with BIGSERIAL. I don't have ISQL as opposed to DB-Access on the Mac (laziness; I did the port a while ago, but didn't install it as it was not official, and it is not GA), and it is possible that there's a difference between the two LOAD commands, but relatively unlikely.
Testing SQLCMD on the same SQL and data (unload) files, I get the same error message.
I am more than ever unconvinced by the claim that it is possible to insert NULL values into a primary key column with Informix.
More comments and explanations
Although I know LOAD is not an Informix SQL native statement, I assumed it was added to the SE (Standard Engine) and OL (OnLine) engines?
No; the LOAD statement is handled by code in client programs: DB-Access, ISQL, I4GL, DB-Load, DB-Import. In each case, the statement is recognized and parsed by the client, converted into a suitable INSERT statement that is prepared, then the client reads and parses the data file, and sends the data to the server one row at a time (logically; actually, there's an INSERT cursor involved which gives you batch operation on insertions).
Or does LOAD statement actually call the DBLOAD.EXE utility in SE/OL or onload.exe?
No: the LOAD statement does not involve DB-Load, nor does it involve ON-Load.
Is the source for SQLCMD available? If so, can I dump dbaccess and replace it with a stripped down version of SQLCMD?
Yes. It is available from the IIUG (International Informix User Group) Software Archive. The version available there (87.02) is close to current (I'm using 87.06, but I'm not quite ready to release that to the rest of the world, and it'll be 88.00 when it is released). I don't support it on Windows, simply because I find Windows too hostile a development environment. It has, on occasion, been made to work on Windows, though. My last attempt stopped when I found Microsoft promulgating the 'Safe C Library' routines, but the routines they provide are not the same as the ones in the standard TR 27341. I gave up again at that point.
I just confirmed that my ole SE-4.10 clunker accepts NULL values for SERIAL columns when inserting a load file with LOAD.
OK. You couldn't specify PRIMARY KEY constraints in that version (those arrived with 5.00, I'm almost certain), but you could create unique indexes on SERIAL columns, etc. To the extent that it is a bug, it has presumably been fixed. It might or might not be fixed in SE 7.26; I'd expect it to be, but haven't demonstrated that it is. It is fixed in 11.70; my tests above demonstrate that.
You can't insert a null value into a primary key DB2. Instead, you need to modify your insert query to insert the new key, or just not include it in your Insert statement and have the database handle it automatically...
It would help if we knew the insert query (or at least part of it). We could offer better guidance on how to correct it. However, taking a guess at the source of your issue:
Assuming the table looks like this:
ID INTEGER NOT NULL GENERATED DEFAULT (START WITH 1, INCREMENT BY 1)
SomeOtherField VARCHAR(50)
Your statement should just be:
Insert into MyTable (SomeOtherField) Values ('somevalue')
instead of
Insert into MyTable (ID, SomeOtherField) Values (null, 'somevalue')
or
Insert into Values (null, 'somevalue')
A similar question and more info can be found here: http://www.dbforums.com/db2/669352-autoincrement-fields.html
With Informix SERIAL columns, you can insert a zero (0) and it will automatically convert it to the next highest available integer value. You can also insert a specific integer value as long as it hasn't already been used, since SERIAL columns have a unique constraint.
Your question is ambiguous. Primary keys can also be non-SERIAL datatypes and accept a NULL value. If this is the case, I suggest you create a surrogate key (usually an autoincrement column) in order to uniquely identify each row.
What is your primary key used for and what is the reason for inserting NULL into your primary key?.. Is it because at the moment you're inserting the row, the value is unknown, but later on it will be updated with a known value?.. NULL's as primary keys tend to make things not work properly, especially when joining to foreign keys in a child table. If your primary key doesn't have a unique constraint, that means you could have several rows with duplicate NULL values as their primary key.. not a good idea in any DB, DB2 included!
You should try the following SQL statement to solve your problem:
insert into table(primarykey,c1, ...) values(null,v1,...).
Try insert into table(c1,...) values(v1,...)
Related
I am trying to copy data from one database to another using ssis. I created the dtsx package with the SQL Server Import and Export Wizard.
The table I am copying from has a column name "Id", the other table has name "ModuleCategoryId", which I mapped together.
ModuleCategoryId is the identity, and has an auto increment of 1.
In the source database, the Id's are not ordered, and go like this:
32 Name1
14 Name2
7 Name3
After executing the data flow, the destination DB looks like this:
1 Name1
2 Name2
3 Name3
I have enabled identity insert in the wizard during the, but this doesn't do anything.
The destination database was made with Entity Framework, code first.
If I explicitly turn off ValueGeneratedOnAdd, and remake the destination database, the data is being transferred correctly, but I was wondering if there's a way to transfer all the data without turning off the auto increment, and then turning it back on.
If I manually set Identity Insert on for that table, I can insert rows with whatever ModuleCategoryId I want, so it must be something with the dataflow.
Table definitions are table definitions - regardless of the syntactic sugar ORM tools might overlay.
I created a source and destination table and populated the source to match your supplied data. I do define the identity property on the destination table as well. Whether that's what a ValueGeneratedOnAdd is implemented as in the API, I don't know but it almost has to be otherwise the Enable Identity Insert should fail (if the UI even allows it).
The IDENTITY property allows you to seed it with any initial value you want. For the taget table, I seed at the minimum value allowed for a signed integer so that if the identity insert doesn't work, the resulting values will look really "wrong"
DROP TABLE IF EXISTS dbo.SO_67370325_Source;
DROP TABLE IF EXISTS dbo.SO_67370325_Destination;
CREATE TABLE dbo.SO_67370325_Source
(
Id int IDENTITY(1,1) NOT NULL
, Name varchar(50)
);
CREATE TABLE dbo.SO_67370325_Destination
(
ModuleCategoryId int IDENTITY(-2147483648,1) NOT NULL
, Name varchar(50)
);
CREATE TABLE dbo.SO_67370325_Destination_noident
(
ModuleCategoryId int NOT NULL
, Name varchar(50)
);
SET IDENTITY_INSERT dbo.SO_67370325_Source ON;
INSERT INTO DBO.SO_67370325_Source
(
Id
, Name
)
VALUES
(32, 'Name1')
, (14, 'Name2')
, (7, 'Name3');
SET IDENTITY_INSERT dbo.SO_67370325_Source OFF;
INSERT INTO dbo.SO_67370325_Source
(
Name
)
OUTPUT Inserted.*
VALUES
(
'Inserted naturally' -- Name - varchar(50)
);
Beyond your 3 supplied values, I added a fourth and if you run the supplied query, you'll see the generated ID is likely 33. Source table is created with an identity seeded at 1 but the explicit identity inserts on the source table advance the seed value to 32. Assuming no other activity occurs, next value would be 33 since our increment is 1.
All that said, I have 3 scenarios established. In the Import Export wizard, I checked the Identity Insert and mapped Id to ModuleCategoryId and ran the package.
ModuleCategoryId|Name
32|Name1
14|Name2
7|Name3
33|Inserted naturally
The data in the target table is identical to the source - as expected. At this point, the identity seed is sitting at 33 which I could verify with some DBCC check command I don't have handy.
The next case is taking the same package and unchecking the Identity Insert property. This becomes invalid as I'd get an error reporting
Failure inserting into the read-only column "ModuleCategoryId"
The only option is to unmap the Id to ModuleCategoryId. Assuming I loaded to the same table as before, I would see data something like this
ModuleCategoryId|Name
34|Name1
35|Name2
36|Name3
37|Inserted naturally
If I had never put a record into this table, then I'd get results like
ModuleCategoryId|Name
-2147483648|Name1
-2147483647|Name2
-2147483646|Name3
-2147483645|Inserted naturally
WITHOUT AN EXPLICIT ORDER BY ON MY SOURCE, THERE IS NO GUARANTEE OF RESULTS ORDERING. I fight this battle often. The SQL Engine has no obligation to return data in the primary key order or any other such order unless you explicitly ask for it. Had the following results been stored, it would be equally valid.
ModuleCategoryId|Name
34|Inserted naturally
35|Name1
36|Name2
37|Name3
If you have a requirement for data to be inserted into the target table based on the ascending values of Id in the source table, in the Import/Export wizard, you need to go to the screen where it asks whether you want to pick tables or write a query and choose the second option of query. Then you will write SELECT * FROM dbo.SO_67370325_Source ORDER BY Id; or whatever your source table is named.
The final test, loading SO_67370325_Destination_noident, demonstrates a table with no identity property defined. If I do not map Id to ModuleCategoryId, the package will fail as the column is defined as NOT NULL. When I map the Id to ModuleCategoryId, I will see the same results as the first (7,14,32,33) BUT, every subsequent insert to the target table will have to provide their own Id which may or may not align with what your FluentAPI stuff does.
Similar question/answer Error 0xc0202049: Data Flow Task 1: Failure inserting into the read-only column
The query to create the table with its respective field set as IDENTITY is the following:
CREATE TABLE user (
email varchar(100) primary key
name varchar(30),
pwd varchar(10)
)
Alter table to add IDENTITY field:
ALTER TABLE user ADD id int /*NOT NULL*/ IDENTITY;
The email field to be PRIMARY KEY INDEX will fail if a NULL or DUPLICATED value was set, for example supposed that myemail#domain.com already exists, OK the query fails, but I change the email to anotheremail#domain.com SQL Server generate a new one value for the IDENTITY field based on the query(s) that failed before. My question is why does this happen? (Is this ONLY on SQL Server or other database providers also)
Well, this is clearly documented in "CREATE TABLE (Transact-SQL) IDENTITY (Property)":
Reuse of values - For a given identity property with specific seed/increment, the identity values are not reused by the engine. If a particular insert statement fails or if the insert statement is rolled back then the consumed identity values are lost and will not be generated again. This can result in gaps when the subsequent identity values are generated.
Further along the documentation also answers why and suggests what to do if this is not acceptable:
These restrictions are part of the design in order to improve performance, and because they are acceptable in many common situations. If you cannot use identity values because of these restrictions, create a separate table holding a current value and manage access to the table and number assignment with your application.
Is it possible to only alter a table to make an existing column a serial auto generated key, without adding a new column? Sorry if this question is a bit newbie-ish for PostgreSQL, I'm more a SQL Server person but moving to PostgreSQL..
In a nut shell the program will copying an existing SQL Server database into PostgreSQL. With the desire to have a mirrored DB in PostgreSQL as the source from SQL Server with the only caveat one may selectively include/exclude any table or column as desired, or do everything...
Given the process copies all values, thought one should be able create the keys after the copy has finished just as one may do in SQL Server. Thought PostgreSQL would have a comparable methods as SQL Server's SET INSERT_IDENTITY [ON|OFF] so one may override the auto generated key with a desired value. Not seeing an equivalent in PostgreSQL. So my fallback is to create the mirrored records in Postgres without keys any keys and then alter the tables. But it seems to fix up the table as desired one has create a new column, but doing this break or cause a headache fixing up the RI for PK/FK relationships.
Any suggestions? Thanks in advance.
In PostgreSQL, the auto-generated key is always overridden if you insert an explicit value for it. If you don't specify a value (omit the column), or specify the keyword DEFAULT, a generated key is used.
Given table
CREATE TABLE t1 (id serial primary key, dat text);
then both these will get a generated key from sequence t1_id_seq:
INSERT INTO t1 (dat) VALUES ('fred');
INSERT INTO t1 (id, dat) VALUES (DEFAULT, 'bob');
This will instead provide its own value:
INSERT INTO t1 (id, dat) VALUES (42, 'joe');
You are responsible for ensuring that the provided value doesn't conflict with existing data, or with future values the identity sequence will generate. PostgreSQL will not notice that you manually inserted a row with id 42 and skip when its own sequence counter gets to that point.
Usually what you do is load with provided values, then reset the sequence to the max of all keys already in the table, so it keeps counting from there for new local inserts.
In Interbase (I'm using 2007, I don't know if it matters) is there a command to get the identity of a newly-inserted record, similar to SCOPE_IDENTITY() in SQL Server?
No, InterBase doesn't really have an identity feature.
What InterBase has, instead, is a generator feature. Generators are kind of like identity, but they are logically separated from the primary key column. A generator, in other words, will give you a guaranteed unique value, but what you do with that value is up to you.
You could use that value as the primary key values for a single table, or for multiple tables. But actually assigning the primary key value is something you must do yourself.
In addition to not having a feature like SCOPE_IDENTITY, InterBase does not have any kind of feature to return values from an INSERT statement. So not only can you not get a generated primary key value back from an INSERT statement, you also cannot get any other values, such as values set by a trigger.
Workarounds
One possible workaround for this is to generate the primary key value in advance. So you could do something like the following (I'm going to use InterBase stored procedure syntax for this example, since I don't know what programming language you are using, but you can do the same thing in any programming language):
DECLARE VARIABLE ID INTEGER;
BEGIN
ID = SELECT GEN_ID(MY_GENERATOR, 1) FROM RDB$DATABASE;
INSERT INTO MY_TABLE (ID, DESCRIPTION) VALUES (:ID, "Foo");
RDB$DATABASE is a system table which has only one record. Knowing the value of ID, you can return it from the proc.
A second workaround is to SELECT the record using an alternate key and read the generated ID that way.
I want to store a single row in a configuration table for my application. I would like to enforce that this table can contain only one row.
What is the simplest way to enforce the single row constraint ?
You make sure one of the columns can only contain one value, and then make that the primary key (or apply a uniqueness constraint).
CREATE TABLE T1(
Lock char(1) not null,
/* Other columns */,
constraint PK_T1 PRIMARY KEY (Lock),
constraint CK_T1_Locked CHECK (Lock='X')
)
I have a number of these tables in various databases, mostly for storing config. It's a lot nicer knowing that, if the config item should be an int, you'll only ever read an int from the DB.
I usually use Damien's approach, which has always worked great for me, but I also add one thing:
CREATE TABLE T1(
Lock char(1) not null DEFAULT 'X',
/* Other columns */,
constraint PK_T1 PRIMARY KEY (Lock),
constraint CK_T1_Locked CHECK (Lock='X')
)
Adding the "DEFAULT 'X'", you will never have to deal with the Lock column, and won't have to remember which was the lock value when loading the table for the first time.
You may want to rethink this strategy. In similar situations, I've often found it invaluable to leave the old configuration rows lying around for historical information.
To do that, you actually have an extra column creation_date_time (date/time of insertion or update) and an insert or insert/update trigger which will populate it correctly with the current date/time.
Then, in order to get your current configuration, you use something like:
select * from config_table order by creation_date_time desc fetch first row only
(depending on your DBMS flavour).
That way, you still get to maintain the history for recovery purposes (you can institute cleanup procedures if the table gets too big but this is unlikely) and you still get to work with the latest configuration.
You can implement an INSTEAD OF Trigger to enforce this type of business logic within the database.
The trigger can contain logic to check if a record already exists in the table and if so, ROLLBACK the Insert.
Now, taking a step back to look at the bigger picture, I wonder if perhaps there is an alternative and more suitable way for you to store this information, perhaps in a configuration file or environment variable for example?
I know this is very old but instead of thinking BIG sometimes better think small use an identity integer like this:
Create Table TableWhatever
(
keycol int primary key not null identity(1,1)
check(keycol =1),
Col2 varchar(7)
)
This way each time you try to insert another row the check constraint will raise preventing you from inserting any row since the identity p key won't accept any value but 1
Here's a solution I came up with for a lock-type table which can contain only one row, holding a Y or N (an application lock state, for example).
Create the table with one column. I put a check constraint on the one column so that only a Y or N can be put in it. (Or 1 or 0, or whatever)
Insert one row in the table, with the "normal" state (e.g. N means not locked)
Then create an INSERT trigger on the table that only has a SIGNAL (DB2) or RAISERROR (SQL Server) or RAISE_APPLICATION_ERROR (Oracle). This makes it so application code can update the table, but any INSERT fails.
DB2 example:
create table PRICE_LIST_LOCK
(
LOCKED_YN char(1) not null
constraint PRICE_LIST_LOCK_YN_CK check (LOCKED_YN in ('Y', 'N') )
);
--- do this insert when creating the table
insert into PRICE_LIST_LOCK
values ('N');
--- once there is one row in the table, create this trigger
CREATE TRIGGER ONLY_ONE_ROW_IN_PRICE_LIST_LOCK
NO CASCADE
BEFORE INSERT ON PRICE_LIST_LOCK
FOR EACH ROW
SIGNAL SQLSTATE '81000' -- arbitrary user-defined value
SET MESSAGE_TEXT='Only one row is allowed in this table';
Works for me.
I use a bit field for primary key with name IsActive.
So there can be 2 rows at most and and the sql to get the valid row is:
select * from Settings where IsActive = 1
if the table is named Settings.
The easiest way is to define the ID field as a computed column by value 1 (or any number ,....), then consider a unique index for the ID.
CREATE TABLE [dbo].[SingleRowTable](
[ID] AS ((1)),
[Title] [varchar](50) NOT NULL,
CONSTRAINT [IX_SingleRowTable] UNIQUE NONCLUSTERED
(
[ID] ASC
)
) ON [PRIMARY]
You can write a trigger on the insert action on the table. Whenever someone tries to insert a new row in the table, fire away the logic of removing the latest row in the insert trigger code.
Old question but how about using IDENTITY(MAX,1) of a small column type?
CREATE TABLE [dbo].[Config](
[ID] [tinyint] IDENTITY(255,1) NOT NULL,
[Config1] [nvarchar](max) NOT NULL,
[Config2] [nvarchar](max) NOT NULL
IF NOT EXISTS ( select * from table )
BEGIN
///Your insert statement
END
Here we can also make an invisible value which will be the same after first entry in the database.Example:
Student Table:
Id:int
firstname:char
Here in the entry box,we have to specify the same value for id column which will restrict as after first entry other than writing lock bla bla due to primary key constraint thus having only one row forever.
Hope this helps!