views:

523

answers:

12

I am working on a project currently where there are SQL strings in the code that are around 3000 lines.

The project is a java project, but this question could probably apply for any language.

Anyway, this is the first time I have ever seen something this bad. The code base is legacy, so we can suddenly migrate to Hibernate or something like that.

How do you handle very large SQL strings like that?

I know its bad, but I don't know exactly what is the best thing to suggest for a solution.

+2  A: 

The best thing I could come up with so far is to put the query into several stored procedures, the same way I would handle a method thats too long in Java.

John Sonmez
Several stored procedures? Is this one big query or what is this exactly?
BobbyShaftoe
Most definitely there is. The biggest problem being its difficult to understand.
John Sonmez
There's nothing inherently evil about big long sql statements. Understanding them isn't easier if you decompose them, and take the sum of the parts. And it's probably more efficient this way. Any way to know if it was profiled/optimized?
le dorfier
Decompositon is a non-transferable concept from procedural code (where it's almost always a win) to declarative code (where it's almost always a lose). That's why temp tables are an SQL antipattern.
le dorfier
Decomposition absolutely works in SQL. You can take apart a complicated query that has multiple joins and make it simple to understand using views or stored procedures.
John Sonmez
I bet you can't find any reputable best-practices list to support that. (Putting it into one SP doesn't count - it just puts it elsewhere. Multiple SP's deoptimizes it. Using views obfuscates it in most cases.)
le dorfier
@le dorfier: I'm going to go ahead and say that a 3000 line SQL statement is inherently evil. Or at best, it's a "solution" to an even bigger mess.
MusiGenesis
I wasn't even going to suggest the second option, it's so obvious. :)
le dorfier
If nothing else, somebody must have known what they were doing at least a little bit, to get it to work in the first place.
le dorfier
+10  A: 

It seems to me that making those hard-coded values into stored procedures and referencing the sprocs from code instead might be high yield and low effort.

Gabriel Isenberg
I can't see any reason NOT to make them stored procedures. High yield and low effort is right!
AJ
You sound pretty positive for not having seen the query. :) Precisely what hard-coded values are you referring to?
le dorfier
A: 

One easy way would be to break them out into some kind of constants. That would at least make the code more readable.

peteDog
+1  A: 

What I do in php is this:

$query = "SELECT * FROM table WHERE "; $query .= "condition<5 AND "; $query .= "condition2>10 AND ";

. . .

and then, once you've finished layering on $query:

mysql_query($query);

stalepretzel
+4  A: 

I guess the first question is, what are you supposed to do with it? If it's not broken, quietly close it up and pretend you've never seen it. Otherwise, refactor like mad - hopefully there's some exit condition somewhere that looks something like a contract.

le dorfier
+1 close the door, walk away, nothing to see here folks!
Steven A. Lowe
Unfortunately it is broken. Or rather it was changed and now doesn't meet code quality standards.
John Sonmez
What code quality standards doesn't it meet? I hope there are separate best practices for procedural code and declarative code. Otherwise you're in deep shit.
le dorfier
If its in the Java, is now Java code.
John Sonmez
Then take it out of java and put it into one SP. That's not decomposing it, it's moving it from an inappropriate place to an appropriate place, as is, It won't fix what's broken, but may change the code standards rules. (It should be moved in any case.)
le dorfier
Then if you want to post it, I'll help see if we can patch it up.
le dorfier
+4  A: 

Does the SQL has a lot of string concatenations for the variables?

If it doesn't you can extract them a put them in resources files. But you'll have to remove the string conatentation in the line breaks.

The stored procedure approach you used is very good, but sometimes when there's need to understand what the SQL is doing, you have to switch from workspace to your favorite SQL IDE. That's the only bad thing.

For my suggestion it would be like this:

String query = "select ......."+
3000 lines.

To

ResourceBundle bundle = ResourceBundle.getBundle("queries");
String query = bundle.getString( "customerQuery" );

Well that's the idea.

OscarRyz
A: 

I store them in files (or resources), and then read and cache them on app start (or on change if it's a service or something).

Or, just put them into a big old SqlQueries class as consts or readonlys.

Mark Brackett
+2  A: 

I'm in the same spot you are... My plan was to pull the SQL into separate.sql files within the project and create a utility method to read the file in when I need the query.

string sql = "select asd,asdf,ads,asdf,asdf," 
           + "from asdfj asfj as fasdkfjl asf"
           + "..........................."
           + "where user = @user and ........";

The query gets dumped into a file called usageReportByUser.sql
and the becomes something like this.

string sql = util.queries("usageReportByUser");

Make sure it's done in a way that the files are not publicly accessible.

jms
You can use the ResourceBundle to loead the sql very easily. It's pretty straight forward. And then have the variables replaced using something like Velocity.
OscarRyz
Excellent. Thanks.
jms
+1  A: 

Use the framework iBatis

+1  A: 

I wrote a toolkit for this a while back and have used it in several projects. It allows you to put queries in mostly text files and generate bindings and documentation for them.

Check out this example and an example use (it's pretty much just a prepared statement compiled to a java class with early bound/typesafe query bindings).

It generates some nice javadocs, too, but I don't have any of those online at the moment.

Dustin
+1  A: 

I second the iBatis recommendation. At the least, you can pull the SQL out from Java code that most likely uses StringBuffer and appending or String concat into XML where it is just easier to maintain.

I did this for a legacy web app and I turned on debugging and ran unit tests for the DAOs and just copied the generated sql for each statement into the iBatis xml. Worked pretty smooth.

tmeisenh
A: 

I have had success converting large dynamic queries to linq queries. (1K lines+) This works very well for reporting scenarios where you have a lot of dynamic filtering and dynamic grouping on a relatively small number of tables. Create an edmx for those tables and you can write excellent strong typed composable queries.

I found that performance actually improved and the resulting sql was a lot simpler. I'm sure you would get similar mileage with Hibernate - other than being able to use linq ofcourse. But generaly speaking if the generated sql needs to be highly dynamic, then it is not a good candidate for a stored proc. writing dynamic sql inside a stored proc is the worst of both worlds. sql generation frameworks would be my preferred way to go here. If you dig Hibernate I think it would be a good solution.

That being said, if the queries are just simple strings with parameters, then just throw them in a stored proc and be done with it. -but then you miss out on the results being nice to work with objects.

Tion