13

Given a table like:

CREATE TABLE "MyTable" 
(
  "MyColumn" NUMBER NOT NULL
);

I want to create a view like:

CREATE VIEW "MyView" AS
SELECT
    CAST("MyColumn" AS BINARY_DOUBLE) AS "MyColumn"
FROM "MyTable";

Only where the column "MyColumn" is "NOT NULL".

In SQL Server this is pretty straight forward:

CREATE VIEW [MyView] AS
SELECT
    ISNULL(CAST([MyColumn] AS Float), 0.0) AS [MyColumn]
FROM [MyTable];

However the Oracle equivalent results in a "NULL" column:

CREATE VIEW "MyView" AS
SELECT
    NVL(CAST("MyColumn" AS BINARY_DOUBLE), 0.0) AS "MyColumn"
FROM "MyTable";

Is there anyway to force Oracle to mark the view's column as "NOT NULL" in the metadata?

Ben
  • 51,770
  • 36
  • 127
  • 149
Danny Varod
  • 17,324
  • 5
  • 69
  • 111
  • 1
    Cross posted in DBA.SE: http://dba.stackexchange.com/questions/19484/oracle-how-to-create-a-not-null-column-in-a-view – Danny Varod Jun 19 '12 at 09:09
  • In this case the column in the view can't be null if the underlying table column has a not-null constraint. Do you want `desc "MyView"` (hope you aren't really using mixed-case object names!) to show it as not-null too? – Alex Poole Jun 19 '12 at 09:24
  • Yes, I want to metadata to show it as not-null. (And no, those are not the real naming conventions.) – Danny Varod Jun 19 '12 at 09:50
  • This has to work on Oracle 10g and 11g (have databases of both versions). – Danny Varod Jun 19 '12 at 11:24

2 Answers2

8

You can't add a not null or check constraint to a view; see this and on the same page 'Restrictions on NOT NULL Constraints' and 'Restrictions on Check Constraints'. You can add a with check option (against a redundant where clause) to the view but that won't be marked as not null in the data dictionary.

The only way I can think to get this effect is, if you're on 11g, to add the cast value as a virtual column on the table, and (if it's still needed) create the view against that:

ALTER TABLE "MyTable" ADD "MyBDColumn" AS
    (CAST("MyColumn" AS BINARY_DOUBLE)) NOT NULL;

CREATE OR REPLACE VIEW "MyView" AS
SELECT
    "MyBDColumn" AS "MyColumn"
FROM "MyTable";

desc "MyView"

 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 MyColumn                                  NOT NULL BINARY_DOUBLE

Since you said in a comment on dba.se that this is for mocking something up, you could use a normal column and a trigger to simulate the virtual column:

CREATE TABLE "MyTable" 
(
  "MyColumn" NUMBER NOT NULL,
  "MyBDColumn" BINARY_DOUBLE NOT NULL
);

CREATE TRIGGER "MyTrigger" before update or insert on "MyTable"
FOR EACH ROW
BEGIN
    :new."MyBDColumn" := :new."MyColumn";
END;
/

CREATE VIEW "MyView" AS
SELECT
    "MyBDColumn" AS "MyColumn"
FROM "MyTable";

INSERT INTO "MyTable" ("MyColumn") values (2);

SELECT * FROM "MyView";

  MyColumn
----------
  2.0E+000

And desc "MyView" still gives:

 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 MyColumn                                  NOT NULL BINARY_DOUBLE

As Leigh mentioned (also on dba.se), if you did want to insert/update the view you could use an instead of trigger, with the VC or fake version.

Mat
  • 202,337
  • 40
  • 393
  • 406
Alex Poole
  • 183,384
  • 11
  • 179
  • 318
  • So you added a calculated column to the table with the casting in it and a not null constraint, that way the view doesn't need a cast. - I'll try this ASAP. Is the new column writable? (Can it update the original column?) – Danny Varod Jun 19 '12 at 10:07
  • No, you can't insert into the VC on the table (since it's generated); and you can't insert into the view because it's against the VC. You'd get ORA-54013 on `insert`, or ORA-54017 on `update`. (But I get the same error against your original view, with the `cast`, if the VC exists; and ORA-01733 if it doesn't). – Alex Poole Jun 19 '12 at 10:12
  • Good enough for now :-) (At least until Oracle supports mapping "number" to "double" in their EF provider on until we migrate away from Oracle.) – Danny Varod Jun 19 '12 at 10:19
  • I am getting "ERROR ORA-00902: invalid datatype" while running the "alter table" command. Apparently I am on 10g (10.2.0.5.0). – Danny Varod Jun 19 '12 at 10:40
  • Ah, virtual columns weren't introduced until 11g, so this won't work for you. I've added a version using a psuedo-virtual column that I think will work in 10g or 11g, though I don't have a 10g DB handy to test on. – Alex Poole Jun 19 '12 at 16:46
1

If you could have a NOT NULL constraint on a view column I believe that a SELECT from the view would then fail if the column in question was NULL. If this is the intent then the following might give you what you're looking for:

CREATE OR REPLACE VIEW some_view AS
  SELECT some_field,
         some_other_field,
         CASE
           WHEN field_of_interest IS NOT NULL
             THEN CAST(field_of_interest AS BINARY_DOUBLE)
             ELSE 1 / 0
         END AS field_of_interest_not_null
     FROM some_table;

Not very attractive, and you get an ugly "ORA-01476: division is equal to zero" message if the ELSE branch of the CASE is taken, but perhaps it's a step on the road to "better".

Share and enjoy.


EDIT: If the objective is to only pick up rows where your target column is not null perhaps you could add a WHERE clause to your view, as in:

CREATE OR REPLACE VIEW some_view AS
  SELECT some_field,
         some_other_field,
         CAST(field_of_interest AS BINARY_DOUBLE) AS field_of_interest
    FROM some_table
    WHERE field_of_interest IS NOT NULL;

YMMV.


EDIT2: Looking at the SQL Server example, it appears that the ISNULL function is being used to ensure that the column is never NULL. If this is acceptable you could do the following:

CREATE OR REPLACE VIEW some_view AS
  SELECT some_field,
         some_other_field,
         CAST(NVL(field_of_interest, 0.0) AS BINARY_DOUBLE) AS field_of_interest
    FROM some_table
    WHERE field_of_interest IS NOT NULL;

To quote Bullwinkle, "This time fer sure!!!" :-)

  • The value can not be null so the select can not fail, I just want the metadata to reflect that. – Danny Varod Jun 19 '12 at 10:59
  • I understand how you feel. I guess that the underlying table column is your best option for the NOT NULL constraint. – Bob Jarvis - Слава Україні Jun 19 '12 at 11:10
  • @DannyVarod: added third option to use NVL along with the CAST, which is roughly equivalent to the ISNULL used in the SQL Server example in the original post. – Bob Jarvis - Слава Україні Jun 19 '12 at 11:14
  • 1
    The MSSQL ISNULL() function indicates to MSSQL that if the cast fails the value is not null. MSSQL reflects this in the metadata. The Oracle NVL() function is meant to do the same, however, the metadata doesn't reflect it. I am not sure that there is a good option regarding Oracle. – Danny Varod Jun 19 '12 at 11:18
  • 1
    Ah, now I understand your point. I agree that Oracle can't be persuaded to mark the column as being non-NULLABLE in DBA_TAB_COLUMNS. Also, I find that I can't even SELECT from the above view definition due to the presence of BINARY_DOUBLE - I get an "ORA-03115 - unsupported network datatype or representation". I learn something new every day. :-) – Bob Jarvis - Слава Україні Jun 19 '12 at 11:26