Skip to Main Content

APEX

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

Oracle Apex - virtual column error raised

KatReedAug 27 2018 — edited Aug 28 2018

I am currently working in Oracle Application Express 18.1.0.00.45 and I am getting an error that I do not understand.

I created an interactive grid using the following query:

select periodic_topics_id, filter, topic, CASE WHEN LINK1 like '%116%' then LINK1||:APP_SESSION ELSE LINK1 END AS LINK1 From periodic_topics where meeting like :P31_MEETING_DESC and (nvl(:P31_FILTER,'0') = '0' or instr(:P31_FILTER||':',filter||':') > 0) 

In the table in the database, the periodic_topics_id column is the primary key and it is automatically populated when a new row is added to the table using the following trigger:

create or replace TRIGGER periodic_topics_trigger BEFORE INSERT ON periodic_topics FOR EACH ROW BEGIN :new.periodic_topics_id := periodic_topics_seq.nextval; END;

In the APEX application, link1 is a textfield and in the "Link" section of this column's properties, the "Target" is of type URL and the URL is &LINK1. I also indicated in the APEX application that periodic_topics_id is the primary key. These are the properties of the link column that I am referring to:

pic.png

The problem: when I manually insert a value into a cell in the "LINK1" column of the interactive grid, an error is raised that says:

"•Ajax call returned server error ORA-20987: APEX - Process 'Periodic Topics - Save Interactive Grid Data' raised 'ORA-01733: virtual column not allowed here' while executing a DML command. This error can occur if a column is based on an aggregation or SQL expression. Set column attribute 'Query Only' to Yes to exclude the column from the INSERT and UPDATE statement. - Contact your application administrator. for ."

However, if I create the interactive grid using the same query but without the case statement, then I have no problem adding a link in the interactive grid. No error occurs. In other words, no error occurs when I try to add a value to the "Link1" column in the interactive grid if I create the interactive grid using the following query:

select periodic_topics_id, filter, topic, link1 From periodic_topics where meeting like :P31_MEETING_DESC and (nvl(:P31_FILTER,'0') = '0' or instr(:P31_FILTER||':',filter||':') > 0)

Just FYI, I need the query to have the case statement because some of the links will direct the user to external websites and others will direct the user to another page in the application. Without the case statement concatenating :APP_SESSION to the link, the user is forced to log back in to the application whenever they click on a link that directs them to another page in the application.

Does anyone know why the error would occur when the case statement is in the query but not when the case statement isn't in the query?

Thank you in advance.

This post has been answered by fac586 on Aug 28 2018
Jump to Answer

Comments

tsuji

Ok for the question. But you need to be more exact in the data so that people can get a clear picture of what you need,

.

[1] why the table shown (P3, AA)=80, but your xml suggests it being 50?

If your expected output shows 80_80 as ns1:titleRange for P3, the xml at (P3, AA) should be wrong.

[2] why the table shown (P4, AB)=50, but your xml suggests it being 80?

If your expected output shows 50 in ns1:RangeAtClass for AB, the xml at (P4, AB) should be wrong there.

And then if it is really 80 as shown, why then the expected output should show up 50 rather than 80 at ns1:RangeAtClass?

At least some consistency???

RAMMII

Thanks for response. sorry for bad data, i have updated the correct values.

Let me know the procedure for the output.

tsuji

The revised xml makes more sense and is then overall consistent in conception, as far as I can abstract out of the question.

Any pointers how to achieve that would be helpful, if not through x query is there any other approach to do this?

If you insist on using xquery, that is more clumsy as it would be more procedural oriented... I would suggest using XMLTransform to do it. Needless to say that XMLQuery and XMLTransform are of the same class of citizens as far as the querying oracle xmltype column or db, each has its range of ease in a query, In this case, I think, XMLTransform would be a more appropriate choice. The general idea would be that if you output practically the original xml except changes happened here and there at some focus points, I would tend to opt for XMLTransform, whereas if you output mostly those changes or original data at some focus points, not as wide-spread to the amplitude of practical the whole original, I would then tend to opt for XMLQuery instead.

Having said, even using XMLTransform here is not very straight-forward as xslt 1.0 is still the norm in that space and xslt 2.0+ is still something to desire. With xslt 2.0, it would look much easier to understand the underlying reasoning. The support of distinct-values(), string-join() and sequence make its coding much simpler than xslt 1.0 for this kind of functionality asked. I have no guarantee that you would understand it right-away.

Here is how it can be done in xslt 1.0. It is not the most efficient version which may use xsl:key construction. But for a reasonably shorter xmltype column or db, it is even preferrable due to its easier in reading directly from the script.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

    xmlns:ns="urn:unknown"

>

<xsl:output method="xml" omit-xml-declaration="yes" encoding="utf-8" indent="yes" />

<xsl:strip-space elements="*" />

<xsl:template match="*|@*|text()">

    <xsl:copy>

        <xsl:apply-templates select="*|@*|text()" />

    </xsl:copy>

</xsl:template>

<xsl:template match="ns:titleRange">

    <xsl:variable name="klass" select="normalize-space(ancestor::ns:Network/ns:classType/ns:class)" />

    <xsl:variable name="titleCode" select="normalize-space(preceding-sibling::ns:titleCode)" />

    <xsl:variable name="sequence"

        select="/root/ns:Network/ns:classType[normalize-space(ns:class)=$klass]/ns:title[normalize-space(ns:titleCode)=$titleCode]/ns:titleRange" />

    <xsl:variable name="string-join">

        <xsl:for-each select="$sequence">

            <xsl:sort select="." data-type="number" order="descending" />

            <xsl:choose>

                <xsl:when test="position()=last()">

                    <xsl:value-of select="normalize-space()" />

                </xsl:when>

                <xsl:otherwise>

                    <xsl:value-of select="concat(normalize-space(),'_')" />

                </xsl:otherwise>

            </xsl:choose>

        </xsl:for-each>

    </xsl:variable>

    <xsl:copy>

        <xsl:apply-templates select="@*" />

        <xsl:value-of select="$string-join" />

    </xsl:copy>

</xsl:template>

<xsl:template match="ns:RangeAtClass">

    <xsl:variable name="all-sequence"

        select="ancestor::ns:Network/ns:classType/ns:title/ns:titleRange[number()!=number(current()/ancestor::ns:Network/following::ns:titleRange)]" />

    <xsl:variable name="max">

        <xsl:for-each select="$all-sequence">

            <xsl:sort select="count(current()/ancestor::ns:Network/ns:classType/ns:title/ns:titleRange[.=current()])" order="descending" />

            <xsl:choose>

                <xsl:when test="position()=1">

                    <xsl:value-of select="." />

                </xsl:when>

                <xsl:otherwise />

            </xsl:choose>

        </xsl:for-each>

    </xsl:variable>

    <xsl:copy>

        <xsl:apply-templates select="@*" />

        <xsl:value-of select="$max" />

    </xsl:copy>

</xsl:template>

</xsl:stylesheet>

Here I suppose the namespace of ns1 in the original xml document be urn:unknown. Replace it with your proper namespace uri.

With it as an external file, you load it to perform the query... like this.

create or replace directory xdir as 'c:\yourDirectoryToXsl';

-- suppose your db be xmltype db or a db with column testdoc being of xmltype storing the xml input doc,

-- with another column id being 1 to query upon...

-- it would look something like this...

SELECT x.testdoc.transform(XMLType(bfilename('XDIR', 'theXsltFile.xsl'), nls_charset_id('AL32UTF8'))).getStringVal() as output

FROM yourDB x where id='1';

That's about it.

odie_63

You're not saying whether you expect a solution compatible with Oracle Database XQuery implementation.

Anyhow...

Using XQuery only, it's relatively easy with XQuery Update features :

declare namespace ns1 = "dummy";

declare function local:getMaxOccurring($items as element()*) as xs:string

{

  ( for $tr in fn:distinct-values($items)

    order by fn:count($items[.=$tr]) descending

    return $tr )[1]

};

copy $d := $inputDoc

modify (

  for $i in $d/root/ns1:Network[1]/ns1:classType/ns1:title

    , $j in $d/root/ns1:Network[2]/ns1:classType/ns1:title

  let $key := fn:concat($i/ns1:titleRange, "_", $j/ns1:titleRange)

  where $i/ns1:titleCode = $j/ns1:titleCode          

  return (

    replace value of node $i/ns1:titleRange with $key

  , replace value of node $j/ns1:titleRange with $key

  )

, for $i in $d/root/ns1:Network/ns1:classType

  return replace value of node $i/ns1:RangeAtClass

            with local:getMaxOccurring($i/ns1:title/ns1:titleRange)

)

return $d

I find the solution much more intuitive and less convoluted than an equivalent XSLT 1.0 approach.

Using the above from within Oracle would look like this :

SQL> with tmp as (

  2    select xmltype('<root xmlns:ns1="dummy">

  3  <ns1:Network>

  4      <ns1:description>AA</ns1:description>

  5      <ns1:classType>

  6          <ns1:class>23</ns1:class>

  7          <ns1:RangeAtClass>100</ns1:RangeAtClass>

  8          <ns1:title>

  9              <ns1:titleCode>P1</ns1:titleCode>

10              <ns1:titleRange>100</ns1:titleRange>

11          </ns1:title>

12          <ns1:title>

13              <ns1:titleCode>P2</ns1:titleCode>

14              <ns1:titleRange>80</ns1:titleRange>

15          </ns1:title>

16          <ns1:title>

17              <ns1:titleCode>P3</ns1:titleCode>

18              <ns1:titleRange>80</ns1:titleRange>

19          </ns1:title>

20          <ns1:title>

21              <ns1:titleCode>P4</ns1:titleCode>

22              <ns1:titleRange>80</ns1:titleRange>

23          </ns1:title>

24      </ns1:classType>

25  </ns1:Network>

26  <ns1:Network>

27      <ns1:description>AB</ns1:description>

28      <ns1:classType>

29          <ns1:class>23</ns1:class>

30          <ns1:RangeAtClass>100%</ns1:RangeAtClass>

31          <ns1:title>

32              <ns1:titleCode>P1</ns1:titleCode>

33              <ns1:titleRange>100</ns1:titleRange>

34          </ns1:title>

35          <ns1:title>

36              <ns1:titleCode>P2</ns1:titleCode>

37              <ns1:titleRange>50</ns1:titleRange>

38          </ns1:title>

39          <ns1:title>

40              <ns1:titleCode>P3</ns1:titleCode>

41              <ns1:titleRange>80</ns1:titleRange>

42          </ns1:title>

43          <ns1:title>

44              <ns1:titleCode>P4</ns1:titleCode>

45              <ns1:titleRange>50</ns1:titleRange>

46          </ns1:title>

47       </ns1:classType>

48  </ns1:Network>

49  </root>') doc

50    from dual

51  )

52  select xmlserialize(document

53           xmlquery(

54             'declare namespace ns1 = "dummy"; (::)

55              declare function local:getMaxOccurring($items as element()*) as xs:string

56              {

57                ( for $tr in fn:distinct-values($items)

58                  order by fn:count($items[.=$tr]) descending

59                  return $tr )[1]

60              }; (::)

61              copy $d := .

62              modify (

63                for $i in $d/root/ns1:Network[1]/ns1:classType/ns1:title

64                  , $j in $d/root/ns1:Network[2]/ns1:classType/ns1:title

65                let $key := fn:concat($i/ns1:titleRange,"_",$j/ns1:titleRange)

66                where $i/ns1:titleCode = $j/ns1:titleCode

67                return (

68                  replace value of node $i/ns1:titleRange with $key

69                , replace value of node $j/ns1:titleRange with $key

70                )

71              , for $i in $d/root/ns1:Network/ns1:classType

72                return replace value of node $i/ns1:RangeAtClass

73                          with local:getMaxOccurring($i/ns1:title/ns1:titleRange)

74              )

75              return $d'

76             passing doc

77             returning content

78           )

79           indent

80         )

81  from tmp;

XMLSERIALIZE(DOCUMENTXMLQUERY(

--------------------------------------------------------------------------------

<root xmlns:ns1="dummy">

  <ns1:Network>

    <ns1:description>AA</ns1:description>

    <ns1:classType>

      <ns1:class>23</ns1:class>

      <ns1:RangeAtClass>80</ns1:RangeAtClass>

      <ns1:title>

        <ns1:titleCode>P1</ns1:titleCode>

        <ns1:titleRange>100_100</ns1:titleRange>

      </ns1:title>

      <ns1:title>

        <ns1:titleCode>P2</ns1:titleCode>

        <ns1:titleRange>80_50</ns1:titleRange>

      </ns1:title>

      <ns1:title>

        <ns1:titleCode>P3</ns1:titleCode>

        <ns1:titleRange>80_80</ns1:titleRange>

      </ns1:title>

      <ns1:title>

        <ns1:titleCode>P4</ns1:titleCode>

        <ns1:titleRange>80_50</ns1:titleRange>

      </ns1:title>

    </ns1:classType>

  </ns1:Network>

  <ns1:Network>

    <ns1:description>AB</ns1:description>

    <ns1:classType>

      <ns1:class>23</ns1:class>

      <ns1:RangeAtClass>50</ns1:RangeAtClass>

      <ns1:title>

        <ns1:titleCode>P1</ns1:titleCode>

        <ns1:titleRange>100_100</ns1:titleRange>

      </ns1:title>

      <ns1:title>

        <ns1:titleCode>P2</ns1:titleCode>

        <ns1:titleRange>80_50</ns1:titleRange>

      </ns1:title>

      <ns1:title>

        <ns1:titleCode>P3</ns1:titleCode>

        <ns1:titleRange>80_80</ns1:titleRange>

      </ns1:title>

      <ns1:title>

        <ns1:titleCode>P4</ns1:titleCode>

        <ns1:titleRange>80_50</ns1:titleRange>

      </ns1:title>

    </ns1:classType>

  </ns1:Network>

</root>

tsuji

It is indeed a good and needy, the update functionality, xquery technology ought to have and since then is supported. There is no question about it.

Otherwise, I want to make a little note, just to make things value to what they are worth, is that the xslt approach for this (specific - to be more focus and not diverting attention) problem is that the cardinality of ns:Network is in no way restricted to 2. It can take on any number, in the language of w3c schema being @maxOccurs="unbounded" as will. Furthermore, in the xslt2, the xslt script would be substantially more concise, However, xslt1 version is, to me the least, always of interest in any circumstances as we encounter that many opinions out there and that it would be a big misunderstanding to put up an impression that xslt1 is somehow unable to handle many situations that it actually can.,,

1 - 5
Locked Post
New comments cannot be posted to this locked post.

Post Details

Locked on Sep 25 2018
Added on Aug 27 2018
7 comments
10,729 views