1

I do not have sufficient privileges to use a CREATE statement, I can only SELECT. I have a script that has three distinct parts and calculations that need to run and each one references the same complicated WITH statement selection that redundantly clutters the code and is a pain to maintain in three separate locations.

I have tried creating temp tables and views, but again, privileges do not support. Is there a way using either SQL or PL/SQL syntax to define my WITH statement ONCE without using CREATE, and then reference it like I would any other table? Example:

--Define the temp table
WITH tempview AS (SELECT .... FROM ...);

--First query
SELECT ... FROM tempview;
/

--Second query
SELECT ... FROM tempview;
/

--Third query
SELECT ... FROM tempview;
/
6
  • 5
    I would find a way to present my case to the DBA for either giving me create view privilege or creating the view for me. How much time have you wasted trying to do something trivial given the proper privileges? Commented Apr 28, 2017 at 18:02
  • 1
    I'm with DCookie, but if the view would only be used in this one place that might be a hard sell. You can't do this in SQL, but how are you running the script - will it always be run through SQL*Plus, for instance? Commented Apr 28, 2017 at 18:08
  • 1
    work with DBA's to get the structure created and simply re-use it each time you need to populate it thus you're inserting records not creating? Commented Apr 28, 2017 at 18:40
  • Unfortunately, I am saddled with bureaucracy and poor management, otherwise I would not bother trying to seek a workaround. Commented Apr 28, 2017 at 21:11
  • 1
    @SandPiper Sometimes you have to spend more time solving sociological problems than technical problems. You might want to try asking why its a rule (and keep asking until you get either a name or a specific line in a security guideline - if they're using the DoD STIG they are misinterpreting it, a common problem), or bribery, or build a quick AWS demo and scare them with the idea of outsourcing their job. Commented Apr 30, 2017 at 2:24

2 Answers 2

3

Getting the correct permissions and creating permanent objects is the best approach. It sounds like this view would only be used in a single script, which doesn't necessarily make it any less valid to create it, but you might find it harder to justify depending on your DBA and policies. It's certainly worth trying that approach, as @DCookie suggested.

If that fails then there may be hacky workarounds, depending on the client you will run this script in.

For instance, in SQL*Plus it's possible to abuse substitution variables to get something close to what you describe. This uses the define command to create a substitution variable that contains the 'view' query, and then uses that variable inside a WITH clause. (You can't replace the entire with like this, but it's maybe clearer like this anyway). I'm used a trivial dummy query:

define tempview_query = 'SELECT * -
FROM dual -
UNION ALL -
SELECT * -
FROM dual'

WITH tempview AS (&tempview_query)
SELECT * FROM tempview;

WITH tempview AS (&tempview_query)
SELECT * FROM tempview;

When the script is run the output produced is:

D
-
X
X

2 rows selected.


D
-
X
X

2 rows selected.

I've also executed set verify off to hide the substitutions, but turning it on might be instructive to see what's happening.

Notice the dashes at the end of each line of the query; that's the continuation character, and as the define docs mention:

If the value of a defined variable extends over multiple lines (using the SQL*Plus command continuation character), SQL*Plus replaces each continuation character and carriage return with a space.

so the 'new' query shown by set verify on will have your entire view query on a single line (if you display it). It's feasible that with a long enough query you'd hit some line length limit but hopefully you won't reach that point (except you did; see below).

You can do the same thing in SQL Developer, but there the continuation needs to use two dashes, so:

define tempview_query = 'SELECT * --
FROM dual --
UNION ALL --
SELECT * --
FROM dual'

except it isn't quite the same as the continuation in SQL*Plus; here the define has to end with a dash, but it is not replaced in the way the SQL*Plus docs describe - so with a single dash the define works but the query ends up invalid. (At least in 4.2.0; possibly a bug...) By using two dashes the multi-line define still works, the dashes remain part of the query, but they're treated as comment markers; so they make the substituted query look odd (again, if you display it) but don't stop it working. You won't notice with set verify off unless someone looks in v$sql.


If your query exceeds 240 characters - which is rather likely unless it's trivial enough to repeat anyway - you'll hit something like:

string beginning "'SELECT * ..." is too long. maximum size is 240 characters.

Both SQL*Plus and SQL Developer allow you to set a substitution variable from a query, using the column ... new_value command:

column tempalias new_value tempview_query
set termout off

select q'[SELECT *
FROM dual
UNION ALL
SELECT *
FROM dual]'
FROM dual;

set termout on

The query selects the text of your view query as a string; I've used the alternative quoting mechanism, with [] as the delimiters, so you don't have to escape any single quotes in the view query. (You need to pick a delimiter that can't appear in the query too, of course). Also note that you don't need the line continuation character any more.

The text literal that query generates is aliased as tempalias. The column command sets the tempview_query substitution variable to whatever that aliased column expression contains. Using the substitution variable is then the same as in the previous examples.

WITH tempview AS (&tempview_query)
SELECT * FROM tempview;

The set termout lines just hide that generating query; you can temporarily omit the off line to see what the query produces, and that it does exactly match the view query you expected.


Other clients might have similar mechanisms, but those are the only two I'm really familiar with. I should probably also reiterate that this is a bit of a hack, and not something I'd necessarily recommend...

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you, this is much cleaner. I have some difficult DBAs to work with who pretty much don't approve anything. It makes my life hard having to find workarounds all the time. I love this technique, it will be implemented in a couple of places...
This was working for me, but upon expansion I got beyond 240 characters so it told me my string was too long. Do you have any ideas for expanding the character limit?
@SandPiper - that limit is lower than I remembered... I've added an alternative way to set the substitution variable.
2

Another trick with SQL*Plus is to import code from a second SQL script. You can do this with the @ command (making sure to put the @ at the very start of a line), e.g.:

tempview.sql

WITH tempview AS (SELECT .... FROM ...)

(notice there is no ending semicolon ; here, and make sure you either don't have a blank line at the end of the file or set sqlblanklines on)

main.sql

--First query
@tempview.sql
SELECT ... FROM tempview
/

--Second query
@tempview.sql
SELECT ... FROM tempview
/

--Third query
@tempview.sql
SELECT ... FROM tempview
/

3 Comments

This looks like it should work, but I can't seem to make it. Every time I try running it, it comes back with "table or view does not exist". The error is coming from the select statement in main.sql.
@SandPiper - this works as shown and is a good approach. But, if you have a blank line at the end of tempview.sql it will give that error; either make sure you remove that blank line, or do set sqlblanklines on in main.sql first. (It would, however, run each query twice because it has both a semicolon and slash after each query in main... I've removed the semicolons but you could do it with only semicolons if you prefer.)
I got it working. I had to modify my @ directory. Simple error. Thank you, that's a great work around!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.