Creating Oracle tables with foreign keys that reference primary keys on materialized views - sql

I have several materialized views in Oracle which I can query to get information.
Now I want to create several tables with foreign keys referencing those MVs and to do so, I have already "added" the corresponding primary keys to the MVs (as stated in adding primary key to sql view).
Then, when I execute my SQL create table query, I get an Oracle (ORA-02270) error: no matching unique or primary key for this column-list error at position 0, right at the beginning...
Am I doing something wrong? Is it possible what I am trying to do?
If not, how is it usually done?

When there are materialized views referenced by other tables' foreign keys, you have to take note on your views refresh method and how it affects your foreign keys.
Two things may prevent you from refreshing your materialized views:
1) The data in the tables referencing your views may reference lines that need to be updated or deleted. In that case you have to fix your data.
2) Your views' refresh method is complete. In complete refresh Oracle deletes all data in your mviews tables and repopulates them by rerunning their queries as you can see in Oracle site documentation - Refresh Types, while in fast refresh only the differences are applied to your mviews tables. Fast refresh is an incremental refresh and it won't work only if your foreign keys aren't respected by your data.
Now if there are mviews that can't be created with fast refresh (what Oracle calls them "Complex queries") then you can alter constraints to these mviews to deferrable as you can see here.
That way even complete refresh will work because Oracle only validates deferrable constraints by the end of current transaction. Therefore, as long as your refresh method is atomic, Oracle will issue an DELETE and than INSERT all rows back, all in one transaction.
In other words, in the next command to refresh your mview keep parameter atomic_refresh as true:
dbms_mview.refresh(LIST=>'MVIEW', METHOD =>'C', ATOMIC_REFRESH => TRUE);
By the way, this parameter's default value is TRUE, so just don't mention it and it will work.

The documentation states that:
View Constraints
Oracle does not enforce view constraints. However, operations on
views are subject to the integrity constraints defined on the
underlying base tables. This means that you can enforce constraints on
views through constraints on base tables.
and also:
View constraints are a subset of table constraints and are subject to
the following restrictions:
...
View constraints are supported only in DISABLE NOVALIDATE mode. You cannot specify any other mode. You must specify the keyword DISABLE
when you declare the view constraint. You need not specify NOVALIDATE
explicitly, as it is the default.
...
In practice, the above means that although constrains on views can be created, they are blocked and do not work. So as if they were not at all.
Apart from this, think for a moment what sense it would have foreign key constrainst created on tables, that would refer to a materialized view:
tables are always "online" and have always "fresh" data
materialized views can contain stale data
Imagine this case: You insert a record X into some table. This record is not visible in the materialized view yet, because the view is not refreshed at this moment. Then you try to insert record X into another table that has a foreign key constraint pointing to that materialized view. What the database should do ? Should the database reject the insert statement (since for now X is not visible yet in the view and the foreign key exists) ? If yes, then what about data integrity ? Mayby should it block amd wait until the view is refreshed ? Should it force the view to start refreshing or not in such a case?
As you can see, such a case involves many questions and difficult problems in the implementation, so Oracle simply does not allow for constrains on views.

Related

Create materialised view without primary key column

Is it possible to create materialised view without using one of the primary key.
Use Case:-
I have a table with two primary keys, user_id and app_id, I want to create view to fetch data on the basis of app_id regardless of user_id. I am trying to create materialised view but Cassandra is not allowing me to do so if I keep only one primary key.
I know the fact that, I can use "allow filtering" but this will not give 100% accuracy in data.
In Cassandra, materialized view should always include all existing primary key components, but they could be in the different order. So in this case, you can create MV with primary key of app_id, user_id, but this may lead to big partitions if you have very popular application.
But I suggest just to create a second table with necessary primary key, and populate it from your application - it could be even more performant than having materialized view, because it needs to read data from disk every time you insert/update/delete record in the main table. Plus, take into account that materialized views in Cassandra are experimental feature, and have quite a lot of problems.

Error while dropping column from a table with secondary index (Scylladb)

While dropping a column from a table that contains secondary index I get the following error. I am using ScyllaDB version 3.0.4.
[Invalid query] message="Cannot drop column name on base table warehouse.myuser with materialized views"
Below are the example commands
create table myuser (id int primary key, name text, email text);
create index on myuser(email);
alter table myuser drop name;
I can successfully run the above statements in Apache Cassandra.
Default secondary indexes in Scylla are global and implemented on top of materialized views (as opposed to Apache Cassandra's local indexing implementation), which gives them new possibilities, but also adds certain restrictions. Dropping a column from a table with materialized views is a complex operation, especially if the target column is selected by one of the views or its liveness can affect view row liveness. In order to avoid these problems, dropping a column is unconditionally not possible when there are materialized views attached to a table. The error you see is a combination of that and the fact that Scylla's index uses a materialized view underneath to store corresponding base keys for each row.
The obvious workaround is to drop the index first, then drop the column and recreate the index, but that of course takes time and resources.
However, in some cases columns can be allowed to be dropped from the base table even if it has materialized views, especially if the column is not selected in the view and its liveness does not have any impact on view rows. For reference, I created an issue that requests implementing it in our bug tracker: https://github.com/scylladb/scylla/issues/4448

SQL Server - Updating a table that has foreign keys, using DELETE/INSERT instead of UPDATE

I have a main table with many associated tables linked to it using an "id" foreign key.
I need to update a row in this main table.
Instead of updating all the fields of the row, one by one, it would be easier for me to simply deleting the whole row and recreating it with the new values (by keeping the original primary key!).
Is there a way, inside a transaction, to delete such row that has foreign key constraints if the row is recreated, with the same primary key, before the transaction is actually commited?
I tried it, and it doesn't seem to work...
Is there something I can do to achieve that other than dropping the constraints before my DELETE operation? Some kind of lock?
No.
Without dropping/disabling the constraint, SQL Server will enforce the relationship and prevent you from the deleting the referenced row.
It is possible to disable the constraint, but you'll incur the overhead when enabling it that SQL Server must verify EVERY REFERENCE to that key before it will consider the relationships trusted again.
You are much better off taking the time to develop a separate update/upsert function than to incur that additional processing overhead every time you need to change a record.
You could alter the foreign key to use a CASCADE DELETE, but that has its own overhead and baggage.

RELY Constraint in View

While learning inline_constraint and out_of_line_constraint for view. I get this example in oracle documentation.http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_8004.htm#i2126714
CREATE VIEW emp_sal (emp_id, last_name,
email UNIQUE RELY DISABLE NOVALIDATE,
CONSTRAINT id_pk PRIMARY KEY (emp_id) RELY DISABLE NOVALIDATE)
AS SELECT employee_id, last_name, email FROM employees;
I have so many doubts in this statement:
1) what is rely constaint - This constraint is useful when queries typically access views instead of base tables, and the DBA thus needs to define the data relationships between views rather than tables.
But in this example it is not materialized views so data will be accessed through base table only. Could you please explain function and when to use rely constraint.
2) what is the use of unique key if we have disabled it like in this statement.
1) what is rely constraint
View constraints, because they are unenforced, are usually specified with the RELY parameter to make them more useful.
http://docs.oracle.com/cd/B19306_01/server.102/b14200/clauses002.htm#i1002565
Specify RELY to activate an existing constraint in NOVALIDATE mode for query rewrite in an unenforced query rewrite integrity mode.
http://docs.oracle.com/cd/B19306_01/server.102/b14200/clauses002.htm#i1002915
This means if a materialized view is created based on the view, it may rely on the constraint in allowing certain queries to be rewritten to use the materialized view instead of querying your base tables.
2) what is the use of unique key if we have disabled it like in this statement
All unique constraints on a view must be disabled. Basically these can be useful in order to allow query rewrite.

Setting the right foreign key on insert

Morning all,
I'm doing a lot of work to drag a database (SQL Server 2005, in 2000 compatibility mode) kicking and screaming towards having a sane design.
At the moment, all the tables' primary keys are nvarchar(32), and are set using uniqId() (oddly, this gets run through a special hashing function, no idea why)
So in several phases, I'm making some fundamental changes:
Introducing ID_int columns to each table, auto increment and primary key
Adding some extra indexing, removing unused indexes, dropping unused columns
This phase has worked well so far, test db seems a bit faster, total index sizes for each table are MUCH smaller.
My problem is with the next phase: foreign keys. I need to be able to set these INT foreign keys on insert in the other tables.
There are several applications pointing at this DB, only one of which I have much control over. It also contains many stored procs and triggers.
I can't physically make all the changes needed in one go.
So what I'd like to be able to do is add the integer FKs to each table and have them automatically set to the right thing on insert.
To illustrate this with an example:
Two tables, Call and POD, linked pod.Call_ID -> Call.Call_ID. This is an nvarchar(32) field.
I've altered call such that Call_ID_int is identity, auto increment, primary key. I need to add POD.Call_ID_int such that, on insert, it gets the right value from Call.Call_ID_int.
I'm sure I could do this with a BEFORE trigger, but I'd rather avoid this for maintenance and speed reasons.
I thought I could do this with a constraint, but after much research found I can't. I tried this:
alter table POD
add constraint
pf_callIdInt
default([dbo].[map_Call_ID_int](Call_ID))
for Call_ID_int
Where the map_Call_ID_int function takes the Call_ID and returns the right Call_ID_int, but I get this error:
The name "Call_ID" is not permitted in this context. Valid expressions
are constants, constant expressions, and (in some contexts) variables.
Column names are not permitted.
Any ideas how I can achieve this?
Thanks very much in advance!
-Oli
Triggers are the easiest way.
You'll have odd concurrency issues with defaults based on UDFs too (like you would for CHECK constraints).
Another trick is to use views to hide schema changes but still with triggers to intercept DML. So your "old" table no longer exists only as a view on "new" table. A write to the "old" table/view actually happens on the new table.