The SQLEXEC command in FoxPro allows you to sends SQL statements to a database where they can be executed, returning one or more result sets. You might make a call like this in your application:
SQLEXEC(lnConnHand,'SELECT * FROM Customer')
Of course, using SQLEXEC in this way isn’t a great architectural choice, although serviceable. It is difficult to manage code in this way and difficult to support varying back end databases. I would never code customer applications in this way. Instead, I would load an external SQL file and execute that instead.
SQLEXEC(lnConnHand,FILETOSTR("c:sqlserver2005script1.sql"))
This approach not only decouples the proprietary SQL code from my application, but it also allows me to test and review the scripts in the native application (and likely get syntax coloring and intellisense). For example, script1.sql might contain TSQL code that I can review (and test) in SSMS. Even better, if I have a lone client running SQL Server 2000, I can use a different script if a new command (like INTERSECT, for example) is not available. This approach will prove even more valuable as some clients upgrade to SQL Server 2008. I will be able to take advantage of some of the new, faster features of SQL Server 2008 while still supporting older versions.
Using Metadata
To take this a step further, I have built metadata to manage these scripts. Consider an application that supports 4 different back-end databases: Visual FoxPro, SQL Server 2000, SQL Server 2005, and MySQL. Create the following table and store all SQL for each back-end database in the appropriate memo field:
CREATE TABLE sql_meta FREE (; query_id character(15),; sql_vfp m,; sql_ss2005 m,; sql_ss2000 m,; sql_mysql m)
Include this table in your project. In your beforebuild event in your project hook, import or update the sql scripts from your development folders into this table when you build your application.
Now, when you want to execute a script, your code would look a little like this:
*-- create the query/sql management object and execute the code oMQ = CREATEOBJECT( "manage_query" ) SQLEXEC(lnConnHand, oMQ.get_sql('SQL_ss2005','CUSTOMER')) *-- ok, so not very bullet-proof but you get the idea: DEFINE CLASS manage_query AS Custom PROCEDURE get_sql LPARAMETERS tcBackEnd, tcQueryId SELECT sql_meta LOCATE FOR query_id = tcQueryId RETURN sql_meta.&tcBackEnd ENDPROC ENDDEFINE
I’m a big fan of writing readable code. In fact, it is a priority in my development tasks. I’m also a big fan of making testing easier. This approach allows me to view and test each SQL script outside of my business and data access objects. It also makes for more readable and manageable code.
I’ve left out some examples on how you could use parameters or SQLPREPARE using this approach. I’ll be sure to make this a topic in a future entry.
#1 by Michael Babcock at October 19th, 2007
| Quote
Great concept, Todd. I take the exact same approach–using metadata–in my n-tier designs. I demonstrated this at FoxForward this year and of course the downloads from that–including the source code with the sqldefs.dbf metadata table–are available from Ed Leafe’s ProFox downloads site: http://leafe.com/dls/vfp (see “Designing in N-Tier fashion”).
It’s always best to decouple when you can (without introducing much complexity). Even since the conference I’ve seen other areas in that project where I could decouple even more—specifically for adding temporary indexes.
If I’m not mistaken, Andy Kramek does something extraordinary with decoupling, where virtually most of the code is meta-data! I’m guessing he’s relying heavily on EXECSCRIPT? Neat.
Cool stuff!
–Michael
#2 by Tod McKenna at October 19th, 2007
| Quote
Thanks Michael, there are some great decoupling techniques around and I think that, by far, it is one of the smartest design decisions a developer can make. Especially if there is (or might be) a need to use a different database or UI.
I loved your business object demonstrations and the concept of “passing the buck”. It helped me put some theory around the practices I have been getting used to over the years.
#3 by Ivan Julian at April 7th, 2009
| Quote
Gee I’m so glad I found this blog. Have been a VFP (and FoxBase/FoxPro) programmer for 20 years but only recently have gone back to using VFP with SQL backends.
Question, in your excellent examples above, how does one use metadata if your SQL commands require input and out parameters? This would be quite common I imagine especially when using parameters to update existing records in an SQL backend.
Kind Regards, Ive.
#4 by Tod McKenna at April 19th, 2009
| Quote
Hi Ive,
In short, for variables, you would declare (and set) your variables first and then use the “?Parameter” syntax in the SQL Statement. You’ll need to get a bit creative with how you test these in their native environment (i.e. SSMS) though. Usually, my testing is strictly for performance and data-quality issues and is confined to SQL statements needed specifically for the application.
Hope this helps!
-Tod