Skip to Main Content
  • Questions
  • Storing SQL in a table for inline execution in front-end application

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question.

Asked: October 29, 2006 - 6:02 pm UTC

Last updated: October 31, 2006 - 4:03 pm UTC

Version: 10g R2

Viewed 1000+ times

You Asked

Hello Tom,

I have a question for you regarding storing SQL in a table to be executed in a client application to either display reports or process business rules that differ between customers using our system. I currently do this in a reporting app (using MS Access), and it seems to work quite well. However, I have been told that this is a bad practice, and would like your opinion.

Basically, we are a “software as a service” provider and have a number of customers using our system (each with their own instance/schema), and although their business processes and practices are similar, they differ in terms of their reporting requirements. So, what I do is store the SQL for a report in a report table (treport) in their schema, and the parameters of the report in a child table (treport_param).

CREATE TABLE TREPORT
(
REPORT_ID NUMBER(22) NOT NULL,
REPORT_GROUP_ID NUMBER(22) NOT NULL,
REPORT_NAME VARCHAR2(50 BYTE) NOT NULL --Report SQL Stored here,
DESCRIPTION VARCHAR2(4000 BYTE),
REPORT_SQL VARCHAR2(4000 BYTE),
ACTIVE_FLAG VARCHAR2(1 BYTE),
Etc.

CREATE TABLE TREPORT_PARAM
(
REPORT_PARAM_ID NUMBER(22) NOT NULL,
REPORT_ID NUMBER(22) NOT NULL,
DISPLAY_NAME VARCHAR2(50 BYTE),
TOOL_TIP_TEXT VARCHAR2(50 BYTE),
CONTROL_TYP VARCHAR2(50 BYTE),
TEXT_TYP VARCHAR2(50 BYTE),
CONTROL_WIDTH NUMBER(22),
CONTROL_INPUT_MASK VARCHAR2(50 BYTE),
DEFAULT_VAL VARCHAR2(50 BYTE),
TEXT_REQUIRED_FLAG VARCHAR2(1 BYTE),
COMBO_SQL VARCHAR2(4000 BYTE),
COMBO_COLUMN_COUNT NUMBER(22),
COMBO_COLUMN_WIDTHS VARCHAR2(50 BYTE),
COMBO_LIST_WIDTH NUMBER(22),
COMBO_COLUMN_HEADS VARCHAR2(1 BYTE),
SORT_ORDER NUMBER(5),
CONTROL_HEIGHT NUMBER(22),
Etc.

When a user selects a report to run, my front-end application builds a parameter input form based on the parameters in treport_Param with drop downs etc., and then takes the SQL for the report stored in treport, does a find and replace on the parameters, creates an MS Access pass-through query, and then dynamically builds the report. Once the report is displayed, users can use the built in MS Access functions for sorting, filtering it etc.

Although I know there are commercial tools that do this (like Cognos), this little reporting app. was very quick to build, and users love the flexibility of what they can do with the output and how quickly reports can be added or changed. Plus I didn’t want a thick client application that had all kinds of logic for presenting data. Basically, I wanted a very thin app that simply presented the results of an SQL statement to the user, which 99% of the time they copy and paste to MS Excel to format/manipulate on their own anyway.

I like this for a couple of reasons. First, I can very quickly change and deploy reports by simply changing the SQL for the report in treport to do things like adding/removing columns, adding calculated fields, sorting differently etc., and the front-end will dynamically display the changes the next time the user runs the report. So, from a business perspective, I can be very responsive to customer requests.

Second, source code control and release management is much less time consuming. Currently, all clients are at the same version of our system, and whenever we make changes to a database object, we try to upgrade all customer schemas as quickly as possible so that we don’t have the nightmare of supporting multiple versions of our system. So, if I were to have the report logic in packages or views, then every time a customer wanted to make a change to a report, I would have to do a release for all customers to ensure they are all on the same version of the database, and then have a bunch of condition statements that basically say “If customer X, run report with these columns, but if customer Y, do something different”, or something like that. Then, someone else has the reporting package already checked out to make changes for another customer, so I have to wait, and on and on it goes. So, since changing a report is effectively configuration, it is very easy to simply make the changes to the SQL in the report table and be done with it.

However, here is what I think is wrong with doing this:
1. I am not using bind variables. Instead, I build an MS Access pass-thru query using inline SQL in VBA. This obviously is not as efficient as using bind variables, but the majority of reports run in seconds anyway as they do not return reams of data.
2. I am not using a source code control tool for managing the report SQL. Instead, the SQL is stored in a table, and one would have to turn on auditing or something to track changes to reports over time (if it was important to do so). Or, we could extract the table as insert statements and store that in a source code control tool before any changes were made.

As you can probably tell, I am from the business side of our organization and not the development side, and know just enough to be dangerous. However, fast deployment = happy customers, and happy customers = $$ and growth. Furthermore, less time doing system administration (version control/release management) and more time actually building new functionality into the system to attract even more customers seems to be a better use of resources.

Anyway, since I don't want to be a complete "coding cowboy" as I have been described in the past, my question: Is it a good or bad practice to store SQL in a table as I am doing in this little app?

Thank you in advance for your time.


and Tom said...

1) it matters not how "fast" they run (in fact, the faster they run - the potentially WORSE not using binds is!!!)

It matters

a) how many of these unique sql statements you execute
b) in what period of time


It takes exactly ONE program not binding ONE sql statement - that is executed frequently enough to entire KILL a system. ONE, that is all.

So, if you are doing dozens of these per minute, suggest you really really really want to look at an approach that would permit binding, or at the very very very least - uses cursor_sharing.


that is, after you have submitted your code for review to at least 5 people that don't like you and are tasked with pointing out all of your SQL INJECTION vunerabilities:

</code> https://www.oracle.com/technetwork/issue-archive/2005/05-jan/o15asktom-084959.html <code>

Dead serious on that - at least 5 competent people that don't trust your coding abilities. Have they rip your code apart.




Happy customers hate security holes, and I know you have a big big big one.



storing sql in a table - is that a bad practice?

No, we in fact do the logical equivalent in PLSQL, we store plsql compiled, but the SQL is just stored as strings - STRINGS THAT USE BIND VARIABLES of course, but as strings.

Same with pro*c
Same with VB
Same with whatever language you use.

It is all in how you interact with these strings - and I believe you want binds for

a) SECURITY
b) secondarily - for performance and scalability.


Rating

  (6 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Is it fair to compare rock solid unix to rock solid Oracle?

Kurt Look, October 30, 2006 - 8:52 pm UTC

> It takes exactly ONE program not binding ONE sql
> statement - that is executed frequently enough to
> entire KILL a system. ONE, that is all.

Due in no small part to your efforts, this is now well known. Oracle goes out of their way to make the database as solid as it can possibly be. One would think it would be a priority at Oracle to not let ONE program KILL a system. Isn't this equivalent to allowing a single unix process to bring down the entire box?


Tom Kyte
October 31, 2006 - 8:33 am UTC

one process CAN bring down the entire unix box.

My favorite example of this was this actual program that someone compiled and ran years ago:


void main()
{
while(1) fork();
}





Equivalence between Unix and Oracle processes

S, October 30, 2006 - 10:20 pm UTC

I don't think that the analogy is good. The better one would be having a single Unix program being compiled 10 times per second. That would definitely bring down the entire box. What can be done about that? Not much as long as the account has the privileges to run the compilation and no limitation or resource consumption. Same thing with Oracle.

Thanks for pointing out the importance of bind variables

A reader, October 30, 2006 - 11:38 pm UTC

Yikes! Your response reminds me of the time when I was 12 and I asked my Mom if it was ok if I stored my dirty magazines under my mattress. Turns out where I stored them was not so much the problem...

Thanks for pointing out the importance of using bind variables. In my app, I control what the user can enter for parameter values since I use an input form with input masks and drop down controls. Additionally, since this is a reporting app, there are not that many SQL statements executed at any given time. Nevertheless, from your link I see how important it is to use bind variables, and since security and scalability are very important to us, I will look for a way to incorporate them. And, finding 5 people who don’t like me to review my code shouldn’t be too hard!

Also, just wanted to take the opportunity to thank you for the great service you are providing here. This site and your books are the first place we look when we have an Oracle related problem, and we have incorporated a number of your ideas already. In fact, we are currently using your DIY parallelism ideas in some new functionality we are about to deploy.


It matters: how many & how often

Duke Ganote, October 31, 2006 - 9:11 am UTC

> It matters
> a) how many of these unique sql statements you execute
> b) in what period of time

Someone wise wrote:
</code> http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:24269876789087

(2)There Are Exceptions to Every Rule
... as a general rule, you should use bind variables...All it takes is one bad query in your system that fails to use a bind variable, but is executed frequently differing only in the literal values, to bring it to its knees.  

Does that mean that you should always use bind variables in every circumstance? 

No, ... On a system where you are measuring seconds per query (a data warehouse, for example), rather than per second, bind variables are something you may actually want to avoid using.  Here, the overhead of the parse time is a tiny fraction of the overall execution time. Even if you have thousands of users, they are not waiting behind each other to parse queries, but rather are waiting for the queries to finish getting the answer.

***************
I may have to side with the person posing the question, who wrote:  
> fast deployment = happy customers, and 
> happy customers = $$ and growth. 
> Furthermore, less time doing system administration 
> more time actually building new functionality to attract even more customers 

Each user has "their own instance/schema".  I assume that means separate databases, potentially on separate servers.  I don't know how standardized their administrative, etc processes are.

I didn't see any mention of security requirements that limit what certain groups of users within each schema/instance can see, so SQL injection may be not a concern.

However, if the users are using identical data objects, then a consolidated database with row-level-security and bind variables would be the more cost-effective approach
http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:897828464193 <code>
It is a business issue of cost/benefit trade-offs.

Tom Kyte
October 31, 2006 - 10:40 am UTC

ahh, but did you see the response by the person that asked the original question?

sometimes security trumps all.

I believe the sql injection risks must be

a) understood
b) evaluated

here before anything can be said.

Would this not be an ideal place for HTMLDB?

Alan Howlett, October 31, 2006 - 9:31 am UTC

Not that you asked, but based on what I've read this application might be an ideal starting point for learning the Oracle version of the Ginsu knife - the HTMLDB components - SQL Workshop, Data Workshop, and Application Builder. Its lightweight, easy, cut & paste to load/extract data...

Tom Kyte
October 31, 2006 - 10:43 am UTC

it sure could be, yes. They can play with it at:

</code> http://apex.oracle.com/ <code>

to get a feel for it.

RE: security trumps all

Duke Ganote, October 31, 2006 - 1:37 pm UTC

Sorry, it wasn't obvious at quick glance that
A reader from Calgary, AB, Canada
was the the person that asked the original question.

Makes me wonder where the security is controlled, at the DB or application? For example:

"How do i prevent end users from connecting to the database other than my application?"
</code> http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:561622956788
"Why "application server" ?" 
http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:376767427785 <code>



Tom Kyte
October 31, 2006 - 4:03 pm UTC

security should be controlled in the database, for the day when your database is accessed not via the application and the day when new application comes along.

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library