Alterations in views - database

Following is the code for sample table I have created:
SQL> create table a (a1 number(2), a2 number(2) not null);
Table created.
Now suppose I create a view on this table as:
SQL> create view aview as select a1 from a;
View created.
I need the opinions on 3 questions: a. Is there a way to insert values in this view. b. If I drop the view using drop view statement. Is there a way to recover like for tables using flashback? c. If I want to add constarint like primary key to this view it is not allowing me. I want to know why?This is what I tried:
SQL> alter view aview add primary key(a1);
alter view aview add primary key(a1)
*
ERROR at line 1:
ORA-00922: missing or invalid option
SQL> alter view aview add primary key a1;
alter view aview add primary key a1
*
ERROR at line 1:
ORA-00906: missing left parenthesis
I am using Oracle 11g.

You can add constraints to a view but the documentation says:
View Constraints
Oracle Database does not enforce view constraints. However, you can enforce constraints on views through constraints on base tables.
You can specify only unique, primary key, and foreign key constraints on views, and they are supported only in DISABLE NOVALIDATE mode. You cannot define view constraints on attributes of an object column.
So to create your primary key you'd need:
SQL> alter view aview add primary key(a1) disable novalidate;
view AVIEW altered.
or with a named constraint:
alter view aview add constraint aview_pk primary key(a1) disable novalidate;
But as it isn't enforced you can still have duplicate values in the view, and you can insert new duplicate values.
You can't insert directly into the view as the base table as a not-null constraint on a2, which you aren't providing. You'd get an ORA-01400. If you have a default value you can use for a2 then you could add that to the table definition:
create table a (a1 number(2), a2 number(2) default 0 not null);
create view aview as select a1 from a;
alter view aview add constraint aview_pk primary key(a1) disable novalidate;
insert into aview (a1) values (42);
select * from a;
A1 A2
---------- ----------
42 0
Or you could create an instead-of trigger, which is what you'd need to do anyway if the view was more complicated (with joins, for example):
create table a (a1 number(2), a2 number(2) not null);
create view aview as select a1 from a;
alter view aview add constraint aview_pk primary key(a1) disable novalidate;
create trigger aview_trig
instead of insert on aview
begin
insert into a values (:new.a1, 0);
end;
/
insert into aview (a1) values (42);
select * from a;
A1 A2
---------- ----------
42 0
But even with the primary key on the view you can do:
insert into aview (a1) values (42);
select * from a;
A1 A2
---------- ----------
42 0
42 0
If you want to the table to not allow duplicates you'd need a primary or unique key on that. If you want the table to have duplicates but the view to not sure them, make it distinct:
create view aview as select distinct a1 from a;
... but then the default value won't be enough to allow you to insert into the view (you'd get ORA-01732) and you would have to use an instead of trigger.
If you drop the view it is not held in the recycle bin:
drop view aview;
drop table a;
select type, original_name, object_name, operation from user_recyclebin;
TYPE ORIGINAL_NAME OBJECT_NAME OPERATION
----- ------------------ ------------------------------ ---------
TABLE A BIN$/KaYRJ66eC3gQwEAAH/46Q==$0 DROP
That only keeps objects that contain data. As the view has no data, it's just a predefined query, there is nothing to store really, and it's simple enough to recreate the view from its DDL - you aren't risking losing data as you would with a table drop.

Related

How to get column with identity specifcation on for view created in SQL Server?

I have created a view where one of my column is primary key.In that field identity specifcation is also set to yes.when i open the view it doesnot contains primary key field.When i set the identity specification to No then only my view contains the primary key field.What is the reason?
Are there any sql queries to set is identity to No?
My view :
create view userAccounts as select user_id ,br_id,user_name,role_id,
,email ,created_dt ,updated_dt ,status from user_accounts
Here user_id doesnot appear in my view.
I can't replicate the behaviour the OP is describing. In the SQL below, both queries against v1 and v2 return both the columns ID and c. If the column is no longer appearing in the view, then this means the definition of the view is being changed.
CREATE TABLE t1 (ID int PRIMARY KEY CLUSTERED, c char(1));
CREATE TABLE t2 (ID int IDENTITY(1,1) PRIMARY KEY CLUSTERED, c char(1));
GO
CREATE VIEW v1 AS
SELECT *
FROM t1;
GO
CREATE VIEW v2 AS
SELECT *
FROM t2;
GO
INSERT INTO t1 (ID,c)
VALUES (1,'a');
SELECT *
FROM v1;
INSERT INTO t2 (c)
VALUES ('a');
SELECT *
FROM v2;
GO
DROP VIEW v1;
DROP VIEW v2;
DROP TABLE t1;
DROP TABLE t2;
Note that you cannot alter an existing column to become an IDENTITY column (or remove it). If you are changing the property within SSMS it is actually creating a new table, copying the data, dropping the old table, and then renaming the new one.

mssql table multi foreign key cascade

I'm confident that this is possible but for the life of me I can't figure it out.
What I have created is a user history MSSQL table to hold the changes made to a user and by whom. This table contains two foreign keys which reference my other table (User) - one fkey for the affected user and the other fkey for the user making the changes.
What I need is for any changes to the (User) table to cascade and update the corresponding entries in this new table.
The fields in the new table (User_History) are as follows (Each user is identified by two fields):
Affected_User_House_Id - int
Affected_User_Id - int
Modified_By_User_House_Id - int
Modified_By_User_Id – int
Modification_Date - datetime
ModificationMade - ntext
Each field is a primary key except for ‘ModificationMade’. The field ‘Modification_Date’ is accurate down to 1 second.
The problem I am having is creating said cascade.
I have tried running the following T-SQL code:
ALTER TABLE [User_History] WITH CHECK
ADD CONSTRAINT [FK_User_History_User] FOREIGN KEY([Affected_User_House_Id], [Affected_User_Id])
REFERENCES [User] ([User_House_Id], [User_ID])
ON UPDATE CASCADE
GO
ALTER TABLE [User_History] CHECK CONSTRAINT [FK_User_History_User]
GO
ALTER TABLE [User_History] WITH CHECK
ADD CONSTRAINT [FK_User_History_User_ModifiedBy] FOREIGN KEY([Modified_By_User_House_Id], [Modified_By_User_Id])
REFERENCES [User] ([User_House_Id], [User_ID])
ON UPDATE CASCADE
GO
ALTER TABLE [User_History] CHECK CONSTRAINT [FK_User_History_User_ModifiedBy]
GO
This T-SQL gave me the following error:
*'User' table saved successfully
'User_History' table
- Unable to create relationship 'FK_User_History_User_ModifiedBy'.
Introducing FOREIGN KEY constraint 'FK_User_History_User_ModifiedBy' on table 'User_History' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint. See previous errors.*
The code works if I remove the second “ON UPDATE CASCADE” the however that will mean the values in the fields “Modified_By_User_House_Id” and “Modified_By_User_Id” will not be updated to match their referenced values in the User table.
I am at a lost as to how to acomplish this goal.
You can only specify a single cascade. Here's an attempt to simulate multiple cascades with two triggers:
create table TabA (
ID1 int not null,
ID2 int not null,
_RowID int IDENTITY(1,1) not null,
constraint PK_TabA PRIMARY KEY (ID1,ID2),
constraint UQ_TabA__RowID UNIQUE (_RowID)
)
go
create table TabB (
ID1a int not null,
ID2a int not null,
ID1b int not null,
ID2b int not null,
constraint PK_TabB PRIMARY KEY (ID1a,ID2a,ID1b,ID2b)
)
They're simpler than your tables, but hopefully close enough. We need an immutable identifier in TabA, and obviously the IDs aren't it, since the whole point is to cascade changes to them. So I've added _RowID.
It would be nice to implement at least a real foreign key and just simulate the cascade behaviour on top of that, but some simple reflection will demonstrate that there's always a point where the FK would be broken. So we simulate it:
create trigger FK_TabB_TabA on TabB
after insert,update
as
set nocount on
if exists (
select
*
from
inserted i
left join
TabA a
on
i.ID1a = a.ID1 and
i.ID2a = a.ID2
left join
TabA b
on
i.ID1b = b.ID1 and
i.ID2b = b.ID2
where
a._RowID is null or
b._RowID is null)
begin
declare #Error varchar(max)
set #Error = 'The INSERT statement conflicted with the Foreign Key constraint "FK_TabB_TabA". The conflict occurred in database "'+DB_NAME()+'", table "dbo.TabB".'
RAISERROR(#Error,16,0)
rollback
end
And then the cascading update:
create trigger FK_TabB_TabA_Cascade on TabA
after update
as
set nocount on
;with Updates as (
select
d.ID1 as OldID1,
d.ID2 as OldID2,
i.ID1 as NewID1,
i.ID2 as NewID2
from
inserted i
inner join
deleted d
on
i._RowID = d._RowID
)
update b
set
ID1a = COALESCE(u1.NewID1,ID1a),
ID2a = COALESCE(u1.NewID2,ID2a),
ID1b = COALESCE(u2.NewID1,ID1b),
ID2b = COALESCE(u2.NewID2,ID2b)
from
TabB b
left join
Updates u1
on
b.ID1a = u1.OldID1 and
b.ID2a = u1.OldID2
left join
Updates u2
on
b.ID1b = u2.OldID1 and
b.ID2b = u2.OldID2
where
u1.OldID1 is not null or
u2.OldID1 is not null
go
Some simple inserts:
insert into TabA (ID1,ID2)
values (1,1),(1,2),(2,1),(2,2)
go
insert into TabB (ID1a,ID2a,ID1b,ID2b)
values (1,1,2,2)
Then the following gets an error. Not quite like a built in FK violation, but close enough:
insert into TabB (ID1a,ID2a,ID1b,ID2b)
values (1,1,2,3)
--Msg 50000, Level 16, State 0, Procedure FK_TabB_TabA, Line 28
--The INSERT statement conflicted with the Foreign Key constraint "FK_TabB_TabA". The conflict occurred in database "Flange", table "dbo.TabB".
--Msg 3609, Level 16, State 1, Line 1
--The transaction ended in the trigger. The batch has been aborted.
This is the update that we wanted to be able to perform:
update TabA set ID2 = ID2 + 1
And we query the FK table:
select * from TabB
Result:
ID1a ID2a ID1b ID2b
----------- ----------- ----------- -----------
1 2 2 3
So the update cascaded.
Why you can't use real FKs:
You want to have cascading updates. That means that the ID values in TabA are going to change to a new value that doesn't currently exist (caveat - we're excluding situations where 2n rows swap their identity values) - otherwise, the primary key constraint will be broken by this update.
As such, we know that the new key value will not yet exist. If we were to attempt cascading updates using an INSTEAD OF trigger (to update the child table before the parent) then the new values we attempt to update to in TabB do not yet exist. Alternately, if we attempt to do cascading updates using an AFTER trigger - well, we're too late. The FK constraint has already prevented the update.
I suppose you could implement an INSTEAD OF trigger that inserts the new rows as "duplicates", updates the children, then deletes the old rows. In such a circumstance, I think you could have real FKs. But I don't want to try writing that trigger to be right in all circumstances (e.g where you have three rows being updated. Two swap their ID values and the other creates a new ID)
According to this knowledge base article, this error message occurs when "a table cannot appear more than one time in a list of all the cascading referential actions that are started by either a DELETE or an UPDATE statement."
Since you have two paths coming from the same table, a possible workaround may involve creating a new key on the parent table and creating a single foreign key on the child ([Affected_User_House_Id], [Affected_User_Id], [Modified_By_User_House_Id], [Modified_By_User_Id]). This however, will likely create a lot of overhead. As a last resort, you can use triggers to enforce the relational integrity.

How write Alter Table and add new columns?

I have a table that has 3 columns A,B,C which has rows also. Column A is the primary key.
Now as per new requirement I need to add new column D, E and F.
Also i need to remove the previous primary key from column A and add a new primary key for column D.
Column E and F is NULL.
Please help me to create alter table statement.
What you require is a multi-step process. Adding the columns, dropping the existing primary key constraint and finally adding a new one.
The most difficult thing here is adding column D. Because you want it to be the new primary key it will have to be NOT NULL. If your table has existing data you will need to handle this error:
SQL> alter table your_table
2 add ( d number not null
3 , e date
4 , f number )
5 /
alter table your_table
*
ERROR at line 1:
ORA-01758: table must be empty to add mandatory (NOT NULL) column
SQL>
So, step 1 is add the new columns with D optional; then populate it with whatever key values:
SQL> alter table your_table
2 add ( d number
3 , e date
4 , f number )
5 /
Table altered.
SQL> update your_table
2 set d = rownum
3 /
1 row updated.
SQL>
Now we can make column D mandatory:
SQL> alter table your_table
2 modify d not null
3 /
Table altered.
SQL>
Finally, we can change the primary key column from A to D:
SQL> alter table your_table
2 drop primary key
3 /
Table altered.
SQL> alter table your_table
2 add constraint yt_pk primary key (d)
3 /
Table altered.
SQL>
For some alterations we want to add a column with a default value. In this scenario it is possible to do so in one step:
alter table your_table
add new_col varchar2(1) default 'N' not null;
In later versions of Oracle this is actually an extremely efficient of populating the new column with the same value, considerably faster than the multi-step approach outlined above.
In case it's not clear the above syntax is Oracle. I expect SQL Server will be something similar.

SQL Server, How to set auto increment after creating a table without data loss?

I have a table table1 in SQL server 2008 and it has records in it.
I want the primary key table1_Sno column to be an auto-incrementing column. Can this be done without any data transfer or cloning of table?
I know that I can use ALTER TABLE to add an auto-increment column, but can I simply add the AUTO_INCREMENT option to an existing column that is the primary key?
Changing the IDENTITY property is really a metadata only change. But to update the metadata directly requires starting the instance in single user mode and messing around with some columns in sys.syscolpars and is undocumented/unsupported and not something I would recommend or will give any additional details about.
For people coming across this answer on SQL Server 2012+ by far the easiest way of achieving this result of an auto incrementing column would be to create a SEQUENCE object and set the next value for seq as the column default.
Alternatively, or for previous versions (from 2005 onwards), the workaround posted on this connect item shows a completely supported way of doing this without any need for size of data operations using ALTER TABLE...SWITCH. Also blogged about on MSDN here. Though the code to achieve this is not very simple and there are restrictions - such as the table being changed can't be the target of a foreign key constraint.
Example code.
Set up test table with no identity column.
CREATE TABLE dbo.tblFoo
(
bar INT PRIMARY KEY,
filler CHAR(8000),
filler2 CHAR(49)
)
INSERT INTO dbo.tblFoo (bar)
SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY (SELECT 0))
FROM master..spt_values v1, master..spt_values v2
Alter it to have an identity column (more or less instant).
BEGIN TRY;
BEGIN TRANSACTION;
/*Using DBCC CHECKIDENT('dbo.tblFoo') is slow so use dynamic SQL to
set the correct seed in the table definition instead*/
DECLARE #TableScript nvarchar(max)
SELECT #TableScript =
'
CREATE TABLE dbo.Destination(
bar INT IDENTITY(' +
CAST(ISNULL(MAX(bar),0)+1 AS VARCHAR) + ',1) PRIMARY KEY,
filler CHAR(8000),
filler2 CHAR(49)
)
ALTER TABLE dbo.tblFoo SWITCH TO dbo.Destination;
'
FROM dbo.tblFoo
WITH (TABLOCKX,HOLDLOCK)
EXEC(#TableScript)
DROP TABLE dbo.tblFoo;
EXECUTE sp_rename N'dbo.Destination', N'tblFoo', 'OBJECT';
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0 ROLLBACK TRANSACTION;
PRINT ERROR_MESSAGE();
END CATCH;
Test the result.
INSERT INTO dbo.tblFoo (filler,filler2)
OUTPUT inserted.*
VALUES ('foo','bar')
Gives
bar filler filler2
----------- --------- ---------
10001 foo bar
Clean up
DROP TABLE dbo.tblFoo
SQL Server: How to set auto-increment on a table with rows in it:
This strategy physically copies the rows around twice which can take a much longer time if the table you are copying is very large.
You could save out your data, drop and rebuild the table with the auto-increment and primary key, then load the data back in.
I'll walk you through with an example:
Step 1, create table foobar (without primary key or auto-increment):
CREATE TABLE foobar(
id int NOT NULL,
name nchar(100) NOT NULL,
)
Step 2, insert some rows
insert into foobar values(1, 'one');
insert into foobar values(2, 'two');
insert into foobar values(3, 'three');
Step 3, copy out foobar data into a temp table:
select * into temp_foobar from foobar
Step 4, drop table foobar:
drop table foobar;
Step 5, recreate your table with the primary key and auto-increment properties:
CREATE TABLE foobar(
id int primary key IDENTITY(1, 1) NOT NULL,
name nchar(100) NOT NULL,
)
Step 6, insert your data from temp table back into foobar
SET IDENTITY_INSERT temp_foobar ON
INSERT into foobar (id, name) select id, name from temp_foobar;
Step 7, drop your temp table, and check to see if it worked:
drop table temp_foobar;
select * from foobar;
You should get this, and when you inspect the foobar table, the id column is auto-increment of 1 and id is a primary key:
1 one
2 two
3 three
If you want to do this via the designer you can do it by following the instructions here "Save changes is not permitted" when changing an existing column to be nullable
Yes, you can. Go to Tools > Designers > Table and Designers and uncheck "Prevent Saving Changes That Prevent Table Recreation".
No, you can not add an auto increment option to an existing column with data, I think the option which you mentioned is the best.
Have a look here.
If you don't want to add a new column, and you can guarantee that your current int column is unique, you could select all of the data out into a temporary table, drop the table and recreate with the IDENTITY column specified. Then using SET IDENTITY INSERT ON you can insert all of your data in the temporary table into the new table.
Below script can be a good solution.Worked in large data as well.
ALTER DATABASE WMlive SET RECOVERY SIMPLE WITH NO_WAIT
ALTER TABLE WMBOMTABLE DROP CONSTRAINT PK_WMBomTable
ALTER TABLE WMBOMTABLE drop column BOMID
ALTER TABLE WMBOMTABLE ADD BomID int IDENTITY(1, 1) NOT NULL;
ALTER TABLE WMBOMTABLE ADD CONSTRAINT PK_WMBomTable PRIMARY KEY CLUSTERED (BomID);
ALTER DATABASE WMlive SET RECOVERY FULL WITH NO_WAIT

In which cases will Oracle create indexes automatically?

As far as I know (this page) Oracle automatically creates an index for each UNIQUE or PRIMARY KEY declaration. Is this a complete list of cases when indexes are created automatically in Oracle?
I'll try to consolidate given answers and make it community wiki.
So indexes are automatically created by Oracle for such cases:
APC: For primary key and unique key unless such indexes already exist.
APC: For LOB storage and XMLType.
Gary: For table with a nested table.
Jim Hudson: For materialized view.
Firstly, Oracle does not always create an index when we create a primary or unique key. If there is already an index on that column it will use it instead...
SQL> create table t23 (id number not null)
2 /
Table created.
SQL> create index my_manual_idx on t23 ( id )
2 /
Index created.
SQL> select index_name from user_indexes
2 where table_name = 'T23'
3 /
INDEX_NAME
------------------------------
MY_MANUAL_IDX
SQL>
... note that MY_MANUAL_IDX is not a unique index; it doesn't matter ...
SQL> alter table t23
2 add constraint t23_pk primary key (id) using index
3 /
Table altered.
SQL> select index_name from user_indexes
2 where table_name = 'T23'
3 /
INDEX_NAME
------------------------------
MY_MANUAL_IDX
SQL> drop index my_manual_idx
2 /
drop index my_manual_idx
*
ERROR at line 1:
ORA-02429: cannot drop index used for enforcement of unique/primary key
SQL>
There is another case when Oracle will automatically create an index: LOB storage....
SQL> alter table t23
2 add txt clob
3 lob (txt) store as basicfile t23_txt (tablespace users)
4 /
Table altered.
SQL> select index_name from user_indexes
2 where table_name = 'T23'
3 /
INDEX_NAME
------------------------------
MY_MANUAL_IDX
SYS_IL0000556081C00002$$
SQL>
edit
The database treats XMLType same as other LOBs...
SQL> alter table t23
2 add xmldoc xmltype
3 /
Table altered.
SQL> select index_name from user_indexes
2 where table_name = 'T23'
3 /
INDEX_NAME
------------------------------
MY_MANUAL_IDX
SYS_IL0000556081C00002$$
SYS_IL0000556081C00004$$
SQL>
No, we're getting closer but that's not quite a complete list yet.
There will also be an index automatically created when you create materialized view since Oracle needs to be able to quickly identify the rows when doing a fast refresh. For rowid based materialized views, it uses I_SNAP$_tablename. For primary key materialized views, it uses the original PK name, modified as necessary to make it unique.
create materialized view testmv
refresh force with rowid
as select * from dual;
select index_name from user_indexes where table_name = 'TESTMV';
Index Name
--------------
I_SNAP$_TESTMV
And another one, if you create a table with a nested table you get an index created automatically. Object based storage in general can do this as there can be hidden tables created.
I think schema-based XMLTypes will also do it.
Yes, that's the complete list. Oracle automatically creates an index for each UNIQUE or PRIMARY KEY declaration.

Resources