views:

775

answers:

3

I am trying to refer to a column name to order a query in an application communicating with an Oracle database. I want to use a bind variable so that I can dynamically change what to order the query by.

The problem that I am having is that the database seems to be ignoring the order by column.

Does anyone know if there is a particular way to refer to a database column via a bind variable or if it is even possible?

e.g my query is SELECT * FROM PERSON ORDER BY :1 (where :1 will be bound to PERSON.NAME) The query is not returning results in alphabetical order, I am worried that the database is interpreting this as:- SELECT * FROM PERSON ORDER BY 'PERSON.NAME' which will obviously not work.

Any suggestions are much appreciated.

+5  A: 

No. You cannot use bind variables for table or column names.

This information is needed to create the execution plan. Without knowing what you want to order by, it would be impossible to figure out what index to use, for example.

Instead of bind variables, you have to directly interpolate the column name into the SQL statement when your program creates it. Assuming that you take precautions against SQL injection, there is no downside to that.

Update: If you really wanted to jump through hoops, you could probably do something like

order by decode(?, 'colA', colA, 'colB', colB)

but that is just silly. And slow. Don't.

Thilo
+1 btw I don't know if that's true for Oracle, but there is definitely a downside in other databases (like DB2) - it makes much harder for the database to cache queries if you modify the query command text all the time (and not just the values of the parameters)
DrJokepu
That also happens in oracle. But you are not changing the command text all the time. There are only a few choices for the column name, so there will be only five cache entries or so.
Thilo
Thanks a lot, that makes sense to me. I will try to make the code understand when its looking at the order by clause and if it contains a bind variable there I'll replace it with its value.
Scottm
Hmmm... You cannot just not bind it in the first place? It can still be a parameter within your program. Trying to fix the SQL statement after it has been composed sounds tricky.
Thilo
Not really - the tool where the queries are built up parses the sql string for placeholders, then populates the placeholders with their values. This means at some point I'm definately going to have to go back and mess around with the statement to put the column names rather than trying to bind them.
Scottm
+2  A: 

As you are using JDBC. You can rewrite your code, to something without bind variables. This way you can also dynamically change the order-by e.g.:

    String query = "SELECT * FROM PERS ";
    if (condition1){
      query = query+ " order by name ";
    // insert more if/else or case statements
    } else {
      query = query+ " order by other_column ";
    }
    Statement select = conn.createStatement();
    ResultSet result = select.executeQuery(query);

Or even

    String columnName = getColumnName(input);
    Statement select = conn.createStatement();
    ResultSet result = select.executeQuery("SELECT * FROM PERS ORDER BY "+columnName);
Edwin
SQL is best left in the database.
I disagree - this is a good suggestion Edwin, thanks. It looks like I might have to do something like this. I will also need to do some removal of the placeholders from the query string when I add the order by column.
Scottm
A: 

ResultSet result = select.executeQuery("SELECT * FROM PERS ORDER BY "+columnName);

will always be a NEW statement to the database. That means it is, like Thilo already explained, impossible to "reorder" an already bound, calculated, prepared, parsed statement. When using this result set over and over in your application and the only thing, which changes over time is the order of the presentation, try to order the set in your client code. Otherwise, dynamic SQL is fine, but comes with a huge footprint.

jrEving
It will only be a new statement if columnName is *always* different. I'd expect columnName to only take one of a small set of values, which means the detriment to the shared pool will be minimal.
Jeffrey Kemp