H2 database: Information about primary key in INFORMATION_SCHEMA - sql

I create the following table in H2:
CREATE TABLE TEST
(ID BIGINT NOT NULL PRIMARY KEY)
Then I look into INFORMATION_SCHEMA.TABLES table:
SELECT SQL
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'TEST'
Result:
CREATE CACHED TABLE TEST(
ID BIGINT NOT NULL
)
Then I look into INFORMATION_SCHEMA.CONSTRAINTS table:
SELECT SQL
FROM INFORMATION_SCHEMA.CONSTRAINTS
WHERE TABLE_NAME = 'TEST'
Result:
ALTER TABLE TEST
ADD CONSTRAINT CONSTRAINT_4C
PRIMARY KEY(ID)
INDEX PRIMARY_KEY_4C
These statements are not the ones which I have stated, therefore, the question is:
Is the information in TABLES and CONSTRAINS reflects how real SQL which was executed in database?
In original CREATE TABLE statement
there was no CACHED word. (not a problem)
I have never executed ALTER TABLE .. ADD CONSTRAINT statement.
The actual reason why I am asking the question is that I am not sure which statement should I execute in order to guarantee that primary key is used in a clustered index.
If you look at my previous question H2 database: clustered index support then you may find in the answer of Thomas Mueller the following statement:
If a primary key is created after the table has been created then the primary key is stored in a new index b-tree.
Therefore, if the statements are executed as such they are shown in INFORMATION_SCHEMA, then primary key is created after the table is created and hence ID is not used in a clustered index (basically as a key in a data b-tree).
Is there a way how one can guarantee that primary key is used in a clustered index in H2?

Is the information in TABLES and CONSTRAINS reflects how real SQL which was executed in database?
Yes. Basically, those are the statements that are run when opening the database.
If you look at my previous question
The answer "If a primary key is created after the table has been created..." was incorrect, I fixed it now to "If a primary key is created after data has been inserted...".
Is there a way how one can guarantee that primary key is used as a clustered index in H2?
This is now better described in the H2 documentation at "How Data is Stored Internally": "If a single column primary key of type BIGINT, INT, SMALLINT, TINYINT is specified when creating the table (or just after creating the table, but before inserting any rows), then this column is used as the key of the data b-tree."

Related

Create a table with a foreign key referencing to a temporary table generated by a query

I need to create a table having a field, which is a foreign key referencing to another query rather than existing table. E.g. the following statement is correct:
CREATE TABLE T1 (ID1 varchar(255) references Types)
but this one throws a syntax error:
CREATE TABLE T2 (ID2 varchar(255) references SELECT ID FROM BaseTypes UNION SELECT ID FROM Types)
I cannot figure out how I can achieve my goal. In the case it’s needed to introduce a temporary table, how can I force this table being updated each time when tables BaseTypes and Types are changed?
I am using Firebird DB and IBExpert management tool.
A foreign key constraint (references) can only reference a table (or more specifically columns in the primary or unique key of a table). You can't use it to reference a select.
If you want to do that, you need to use a CHECK constraint, but that constraint would only be checked on insert and updates: it wouldn't prevent other changes (eg to the tables in your select) from making the constraint invalid while the data is at rest. This means that at insert time the value could meet the constraint, but the constraint could - unnoticed! - become invalid. You would only notice this when updating the row.
An example of the CHECK-constraint could be:
CREATE TABLE T2 (
ID2 varchar(255) check (exists(
SELECT ID FROM BaseTypes WHERE BaseTypes.ID = ID2
UNION
SELECT ID FROM Types WHERE Types.ID = ID2))
)
For a working example, see this fiddle.
Alternatively, if your goal is to 'unite' two tables, define a 'super'-table that contains the primary keys of both tables, and reference that table from the foreign key constraint. You could populate and update (eg insert and delete) this table using triggers. Or you could use a single table, and replace the existing views with an updatable view (if this is possible depends on the exact data, eg IDs shouldn't overlap).
This is more complex, but would give you the benefit that the foreign key is also enforced 'at rest'.

DB2 error : SQL0204N "SQL160215110205990" is an undefined name

I have following indexes/unique constraints in my table.
INDNAME COLNAMES
SQL160215110206240 +ID
SQL160215110206360 +ENTITY_TYPE+TENANT_ID+ENTITY_LOCAL_USERSTORE+ENTITY_NAME
SQL160215145445420 +ENTITY_TYPE+TENANT_ID+ENTITY_LOCAL_USERSTORE+ENTITY_NAME+PROVISIONING_CONFIG_ID
Now I run following code segment on my darabase.
BEGIN
DECLARE v_rcount VARCHAR(128);
DECLARE STMT VARCHAR(200);
select INDNAME into v_rcount from SYSCAT.INDEXES WHERE TABNAME='IDP_PROVISIONING_ENTITY' AND COLNAMES='+ENTITY_TYPE+TENANT_ID+ENTITY_LOCAL_USERSTORE+ENTITY_NAME';
SET STMT = 'ALTER TABLE IDP_PROVISIONING_ENTITY DROP CONSTRAINT ' || v_rcount;
PREPARE S1 FROM STMT;
EXECUTE S1;
END
When I run it for the 1st time, I am getting following response.
DB20000I The SQL command completed successfully.
But when I list the indexes of the table, I am still seeing all 3 indexes as before.
And also when I run the same code segment again, I am getting following error.
SQL0204N "SQL160215110206360" is an undefined name. SQLSTATE=42704
Not only this index, if I try to drop any other index in the same database, I am getting the same error. What is the reason for this? How can I solve this?
The cause for this issue is, I am getting the name from SYSCAT.INDEXES. So there it is returning the name of INDEX. Even though a INDEX is created to each unique constraint, name of index can be different from name of Constraint. So here I have dropped the constraint and then I have checked the index table if it has removed. That's the reason why I can see the same index name still after removing the constraint.
Also every index name in SYSCAT.INDEXES will not refer to a constraint. So everytime I run DROP CONSTRAINT with a name from SYSCAT.INDEXES will not return success.
IBM DB2 behaves little bit differently compared to other well known databases when comes to constraints and indexes. So let me explain the solution for your problem in detail.
You have the following table.
CREATE TABLE IDP_PROVISIONING_ENTITY (
ID INTEGER NOT NULL,
PROVISIONING_CONFIG_ID INTEGER NOT NULL,
ENTITY_TYPE VARCHAR(255) NOT NULL,
ENTITY_LOCAL_USERSTORE VARCHAR(255) NOT NULL,
ENTITY_NAME VARCHAR(255) NOT NULL,
ENTITY_VALUE VARCHAR(255) NOT NULL,
TENANT_ID INTEGER NOT NULL,
PRIMARY KEY (ID),
UNIQUE (ENTITY_TYPE, TENANT_ID, ENTITY_LOCAL_USERSTORE, ENTITY_NAME),
UNIQUE (PROVISIONING_CONFIG_ID, ENTITY_TYPE, ENTITY_VALUE),
FOREIGN KEY (PROVISIONING_CONFIG_ID) REFERENCES IDP_PROVISIONING_CONFIG(ID) ON DELETE CASCADE)
/
It has a primary key and two unique constraints. The DB2 will automatically create indexes for these three constraints.
The problem is, we have not defined the constraints as named constraints. Therefore we have to take an extra effort to find out the names of the indexes and the constraints which are auto generated by DB2.
These are the two unique constraints that we have in the table.
UNIQUE (ENTITY_TYPE, TENANT_ID, ENTITY_LOCAL_USERSTORE, ENTITY_NAME),
UNIQUE (PROVISIONING_CONFIG_ID, ENTITY_TYPE, ENTITY_VALUE)
You can find the auto generated names for the unique constraints by running the following query. the constraintyp='U' for unique constraints and it is 'P' for primary keys.
db2 "select NAME from sysibm.systabconst where tbname='IDP_PROVISIONING_ENTITY' and constraintyp='U'"
NAME
--------
SQL160219074557840
SQL160219074557920
Above is the result I got. You will get different values in your database as these are random generated.
You can run the following query to see the indexes created for the IDP_PROVISIONING_ENTITY' table.
db2 "SELECT NAME, COLNAMES FROM SYSIBM.SYSINDEXES WHERE TBNAME='IDP_PROVISIONING_ENTITY'"
NAME COLNAMES
SQL160219074557290 +ID
SQL160219074557790 +PROVISIONING_CONFIG_ID+ENTITY_TYPE+ENTITY_VALUE
SQL160219074557860 +ENTITY_TYPE+TENANT_ID+ENTITY_LOCAL_USERSTORE+ENTITY_NAME
Now we know the constraint names for the two unique constraints and the index names for the IDP_PROVISIONING_ENTITY table.
You can find the particular index you need to drop by adding a where clause for the above query as below.
db2 "SELECT NAME FROM SYSIBM.SYSINDEXES WHERE TBNAME='IDP_PROVISIONING_ENTITY' and COLNAMES='+ENTITY_TYPE+TENANT_ID+ENTITY_LOCAL_USERSTORE+ENTITY_NAME'"
NAME
----------
SQL160219074557860
In IBM DB2, you cannot drop an index which is generated for a constraint (primary key or unique) directly. For that, you need to drop the constraint which will drop the index also.
But the problem is we do not know what is the name of the unique constraint that is associated with the index "+ENTITY_TYPE+TENANT_ID+ENTITY_LOCAL_USERSTORE+ENTITY_NAME". We already know that the index name we need to drop is SQL160219074557860.
You can find it out which constraint is associated with which index by running the following query.
db2 "select CONSTNAME, BNAME from SYSCAT.CONSTDEP where TABNAME = 'IDP_PROVISIONING_ENTITY'"
CONSTNAME BNAME
SQL160219074557790 SQL160219074557290
SQL160219074557840 SQL160219074557790
SQL160219074557920 SQL160219074557860
Based on the results, you can get to know the name of the constraint.
So the constraint name associated with the index name "SQL160219074557860" is "SQL160219074557920".
So since we know the constraint name now, we can drop the unique constraint.
db2 "ALTER TABLE IDP_PROVISIONING_ENTITY DROP UNIQUE SQL160219074557920"
This will drop the constraint as well as the index created for the constraint.

Safely rename tables using serial primary key columns

I know that PostgreSQL tables that use a SERIAL primary key end up with an implicit index, sequence and constraint being created by PostgreSQL. The question is how to rename these implicit objects when the table is renamed. Below is my attempt at figuring this out with specific questions at the end.
Given a table such as:
CREATE TABLE foo (
pkey SERIAL PRIMARY KEY,
value INTEGER
);
Postgres outputs:
NOTICE: CREATE TABLE will create implicit sequence "foo_pkey_seq" for serial column "foo.pkey"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo"
Query returned successfully with no result in 52 ms.
pgAdmin III SQL pane shows the following DDL script for the table (decluttered):
CREATE TABLE foo (
pkey serial NOT NULL,
value integer,
CONSTRAINT foo_pkey PRIMARY KEY (pkey )
);
ALTER TABLE foo OWNER TO postgres;
Now rename the table:
ALTER table foo RENAME TO bar;
Query returned successfully with no result in 17 ms.
pgAdmin III:
CREATE TABLE bar (
pkey integer NOT NULL DEFAULT nextval('foo_pkey_seq'::regclass),
value integer,
CONSTRAINT foo_pkey PRIMARY KEY (pkey )
);
ALTER TABLE bar OWNER TO postgres;
Note the extra DEFAULT nextval('foo_pkey_seq'::regclass), this means that renaming the table does not rename the sequence for the primary keys but now we have this explicit nextval().
Now rename the sequence:
I want to keep the database naming consistent so I tried:
ALTER SEQUENCE foo_pkey_seq RENAME TO bar_pkey_seq;
Query returned successfully with no result in 17 ms.
pgAdmin III:
CREATE TABLE bar (
pkey serial NOT NULL,
value integer,
CONSTRAINT foo_pkey PRIMARY KEY (pkey )
);
ALTER TABLE bar OWNER TO postgres;
The DEFAULT nextval('foo_pkey_seq'::regclass), is gone.
QUESTIONS
Why did the DEFAULT nextval('foo_pkey_seq'::regclass) statement appear and disappear?
Is there a way to rename the table and have the primary key sequence renamed at the same time?
Is it safe to rename the table then sequence while clients are connected to the database, are there any concurrency issues?
How does postgres know which sequence to use? Is there a database trigger being used internally? Is there anything else to rename other than the table and the sequence?
What about the implicit index created by a primary key? Should that be renamed? If so, how can that be done?
What about the constraint name above? It is still foo_pkey. How is a constraint renamed?
serial is not an actual data type. The manual states:
The data types smallserial, serial and bigserial are not true types,
but merely a notational convenience for creating unique identifier columns
The pseudo data type is resolved doing all of this:
create a sequence named tablename_colname_seq
create the column with type integer (or int2 / int8 respectively for smallserial / bigserial)
make the column NOT NULL DEFAULT nextval('tablename_colname_seq')
make the column own the sequence, so that it gets dropped with it automatically
The system does not know whether you did all this by hand or by way of the pseudo data type serial. pgAdmin checks on the listed features and if all are met, the reverse engineered DDL script is simplified with the matching serial type. If one of the features is not met, this simplification does not take place. That is something pgAdmin does. For the underlying catalog tables it's all the same. There is no serial type as such.
There is no way to automatically rename owned sequences. You can run:
ALTER SEQUENCE ... RENAME TO ...
like you did. The system itself doesn't care about the name. The column DEFAULT stores an OID ('foo_pkey_seq'::regclass), you can change the name of the sequence without breaking that - the OID stays the same. The same goes for foreign keys and similar references inside the database.
The implicit index for the primary key is bound to the name of the PK constraint, which will not change if you change the name of the table. In Postgres 9.2 or later you can use
ALTER TABLE ... RENAME CONSTRAINT ..
to rectify that, too.
There can also be indexes named in reference to the table name. Similar procedure:
ALTER INDEX .. RENAME TO ..
You can have all kinds of informal references to the table name. The system cannot forcibly rename objects that can be named anything you like. And it doesn't care.
Of course you don't want to invalidate SQL code that references those names. Obviously, you don't want to change names while application logic references them. Normally this wouldn't be a problem for names of indexes, sequences or constraints, since those are not normally referenced by name.
Postgres also acquires a lock on objects before renaming them. So if there are concurrent transaction open that have any kind of lock on objects in question, your RENAME operation is stalled until those transactions commit or roll back.
System catalogs and OIDs
The database schema is stored in tables of the system catalog in the system schema pg_catalog. All details in the manual here. If you don't know exactly what you are doing, you shouldn't be messing with those tables at all. One false move and you can break your database. Use the DDL commands Postgres provides.
For some of the most important tables Postgres provides object identifier types and type casts to get the name for the OID and vice versa quickly. Like:
SELECT 'foo_pkey_seq'::regclass
If the schema name is in the search_path and the table name is unique, that gives you the same as:
SELECT oid FROM pg_class WHERE relname = 'foo_pkey_seq';
The primary key of most catalog tables is oid and internally, most references use OIDs.

Is the PK of a DB2 table a clustered index by default?

This can be a silly question but I want to be sure 100%.
Is the PK of a DB2 table a clustered index by default?
From: DB2 docs - Clustering indexes
Although a table can have several indexes, only one index can be a clustering index. If you do not define a clustering index for a table, DB2 recognizes the first index that is created on the table as the implicit clustering index when it orders data rows.
So no, by default the Primary Key is NOT the clustered index of the table.
The first created index, unique or not, is the "implicit" clustering index and DB2 tries to insert the records as nearly as possible in the order of the values of this index.
If you later create another index and identify it as clustering, then DB2 identifies it as the clustering index but does not rearrange the data that is already in the table. This can be done with the REORG utility.
From the Publib (this assumes DB2 for z/OS, version 9)
When a table has a clustering index, an INSERT statement causes DB2 to
insert the records as nearly as possible in the order of their index
values. The first index that you define on the table serves implicitly
as the clustering index unless you explicitly specify CLUSTER when you
create or alter another index. For example, if you first define a
unique index on the EMPNO column of the EMP table, DB2 inserts rows
into the EMP table in the order of the employee identification number
unless you explicitly define another index to be the clustering index.
You can see which index is the clustering index for a table (in this example, TEST.TABLE1) using the following query, if you're on z/OS:
SELECT NAME
FROM SYSIBM.SYSINDEXES
WHERE TBCREATOR = 'TEST'
AND TBNAME = 'TABLE1'
AND CLUSTERING = 'Y'
And this one for Linux/Unix/Windows (LUW):
SELECT *
FROM SYSCAT.INDEXES
WHERE TABSCHEMA = 'TEST'
AND TABNAME = 'TABLE1'
AND INDEXTYPE = 'CLUS'
DB2 doesn't create clustered index for a PK by default.
Primary keys
A primary key is a special type of unique key and cannot contain null values. For example, the DEPTNO column in the DEPT table is a primary key.
A table can have no more than one primary key. Primary keys are optional and can be defined in CREATE TABLE or ALTER TABLE statements.
The unique index on a primary key is called a primary index. When a primary key is defined in a CREATE TABLE statement or ALTER TABLE statement, DB2 automatically creates the primary index if one of the following conditions is true:
DB2 is operating in new-function mode, and the table space is implicitly created.
DB2 is operating in new-function mode, the table space is explicitly created, and the schema processor is running.
DB2 is operating in conversion mode, and the schema processor is running.
If a unique index already exists on the columns of the primary key when it is defined in the ALTER TABLE statement, this unique index is designated as the primary index when DB2 is operating in new-function mode and implicitly created the table space.
See at: Keys DB2

Can you create a table with indexes at the same time?

I'd like to create a table:
CREATE TABLE sfc.OpenId (
Url VARCHAR(255) PRIMARY KEY,
UserGuid uniqueidentifier NOT NULL references dbo.aspnet_users(userId),
)
...with an index on UserGuid.
Is it possible to create that index in the create table statement?
You can do that if the index on UserGuid is a unique index, via UNIQUE constraint. Otherwise, no.
Is it possible to create that index in the create table statement?
No, only constraints can be created within the CREATE TABLE syntax.
f not defined otherwise, the primary key will automatically be a CLUSTERED index - but that doesn't cover the userguid column. The CREATE INDEX syntax needs to be a separate statement otherwise.
Can you clarify why?
You can use transactions with DDL in SQL server and for most purposes this is equivalent to doing it at the same time.