<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Martin&#039;s MYSQL Blog</title>
	<atom:link href="http://pirringers.com/mysqlblog/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://pirringers.com/mysqlblog</link>
	<description>VFP -&#62; MYSQL migration</description>
	<lastBuildDate>Wed, 07 Apr 2010 11:18:44 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>MYSQL Workbench 5.2.17 Beta</title>
		<link>http://pirringers.com/mysqlblog/?p=65</link>
		<comments>http://pirringers.com/mysqlblog/?p=65#comments</comments>
		<pubDate>Wed, 07 Apr 2010 11:18:44 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://pirringers.com/mysqlblog/?p=65</guid>
		<description><![CDATA[This release of Workbench has shown some major improvements. Especially in looks and feel, so I want to thank the team for those improvements at this time. I would like to take the opportunity though to point out some short commings or inconsistencies &#8211; at least in my oppinion. These are not bugs per say [...]]]></description>
			<content:encoded><![CDATA[<p>This release of Workbench has shown some major improvements. Especially in looks and feel, so I want to thank the team for those improvements at this time. I would like to take the opportunity though to point out some short commings or inconsistencies &#8211; at least in my oppinion. These are not bugs per say &#8211; for those see the bug reporting system &#8211; but just things that make using WB not as much pleasure as it could be.</p>
<p>1.) Triggers: Why can&#8217;t the handling of triggers be consistent with the way MYSQL stores/handles triggers. Namely each trigger as its own entity. Currently triggers just roll together in the sample as shown below in an example that does nothing but is just used to describe the problem</p>
<p>&#8211; Trigger DDL Statements<br />
DELIMITER $$</p>
<p>USE `test`$$CREATE <a href="mailto:DEFINER=`admin`@`%">DEFINER=`admin`@`%</a>` TRIGGER `beforeinsert`<br />
BEFORE INSERT On table1<br />
FOR EACH ROW<br />
BEGIN<br />
    SET new.field1= new.idtable1;<br />
END$$CREATE <a href="mailto:DEFINER=`admin`@`%">DEFINER=`admin`@`%</a>` TRIGGER `beforeunsert`<br />
BEFORE UPDATE On table1<br />
FOR EACH ROW<br />
BEGIN<br />
    SET new.field1= new.idtable1;<br />
END$$</p>
<p>As you can see the triggers just run into each other. At least the coloring helps a bit but it is still difficult to find/handle/change something.</p>
<p>If you execute a SHOW trigger command you get the triggers data  neatly seperated in columns like</p>
<p>TRIGGER EVENT STATEMENT ,,,,, etc</p>
<p>Why not make a seperate tab for each trigger (as it is a row in a table) and then display each field for some data. This should greatly improve the handling of triggers in the system. If also should make the synchronizing easier as each field already has its own value. That way lots of unnecessaty updates could be eliminated.</p>
<p>2.) User defined Data Types: MYSQL currently does not suppor them. It would be nice to have. But currently this feature is unreliable and can be dangerous. It should only be included in the system if it can be implemented in a rock solid way. It would be nice to have User Defined Data Types to help with consistency through the system and allow systemwide changes when for example it is found that a field size &#8211; like for example a customer # or part # fieldd &#8211; must change. Mosty for key/index fields used to help keep up the relations between tables. If a single update from the schema to the model can wipe out the Custom Data Type and replace it with a VARCHAR(whatever) or something like that then the whole concept of having the User Defined Data Types in the model is defeated. Some way to store it at the server side would have to be found &#8211; either as part of the comment for that particular field &#8211; or waiting until MYSQL actually supports user defined Data Types.</p>
<p>3,) Schema Privileges: WB has a whole section devoted to that. One that currently does absolutely nothing of use in my oppinion. Maybe that is why I could not find a help entry in the help system on it. True it lets you create users and define roles. Roles fall in the same category as User Defined Data Types as MYSQL currently does not support User Defined Roles. Currently in WB you can define a role and give that role certain privileges like (INSERT, SELECT&#8230;..) on particular tables and then you cann assign those roles to Users that you create. All very nice and reminiscent to featurs available in other SQL systems &#8211; but currently nothing more thant a notepad as it does not update the server.</p>
<p>Any user created in the model will not create a user on the Server side and any privilege granted to the User will not propagate to the server. In my opinion it is currently at best a notepad on what rights one might want to set up manually at the server &#8230;.</p>
<p>Again I would like to either &#8211; preferably &#8211; have this feature turned into a reliable solid tool &#8211; or removed as it does not do anything really useful at this time and could cause confusion and anger in the user. After all I might have spent hours setting up a Roles/User System in WB and now I got nothing at the server side and got to do it manually there&#8230;..</p>
<p>Lastly it would be nice if they could include a debugger into that system that allows you to debug your Stored Procedures/functions/triggers and step through them &#8211; but I guess I can keep hopeing.</p>
<p>Overall I would like to say that the project is moving in the right direction and I am using WB currently to develop my current project. So keep up the good work</p>
]]></content:encoded>
			<wfw:commentRss>http://pirringers.com/mysqlblog/?feed=rss2&amp;p=65</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Using MYSQL to maintain the VFP frontend.</title>
		<link>http://pirringers.com/mysqlblog/?p=59</link>
		<comments>http://pirringers.com/mysqlblog/?p=59#comments</comments>
		<pubDate>Fri, 19 Mar 2010 20:36:07 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[VFP to MYSQL Migrations]]></category>
		<category><![CDATA[Distribution]]></category>
		<category><![CDATA[MYSQL]]></category>
		<category><![CDATA[VFP]]></category>

		<guid isPermaLink="false">http://pirringers.com/mysqlblog/?p=59</guid>
		<description><![CDATA[The main reason for that VFP to MYSQL conversion project was that the customer is in the process of growth and is establishing multiple locations which all will use the same system to some extend. In addition to that we are going from a system where everyone on the system did pretty much everything to [...]]]></description>
			<content:encoded><![CDATA[<p>The main reason for that VFP to MYSQL conversion project was that the customer is in the process of growth and is establishing multiple locations which all will use the same system to some extend. In addition to that we are going from a system where everyone on the system did pretty much everything to one where there are only going to be a few people who have access to everything and most will just work in their &#8220;special corner&#8221; of the system. Some will just do order entry, some receivables, some sales personel will have only access to their clients etc etc.</p>
<p>At present the VFP system resides on a shared Network drive and if a modification is needed well then one just replaces or adds a Screen or report on that particular shared network drive. Now it would be necessary to push this to multiple locations, in different configurations and that shared network drive wont do anymore &#8211; especially over the internet.</p>
<p>Luckely all VFP Screens, Classes and Reports are stored in tables and each object has its own unique object ID number. So the next phase of this project is to store those tables on the MYSQL server. The clients will just get a Loader program in the Loader program you log in and then the loader program executes a CALL statement to the MYSQL server</p>
<p>CALL sendprogramlist(&lt;userid&gt;)</p>
<p>On the server is a table with the user ids and the particular Start form that they will see and the classes that they will need. so the loader program will get a cursor looking something like</p>
<p>name, type, startup(Y/N to tell which program to start with)</p>
<p>Now the loader program Now all the loader needs to do call the getprogram stored function and save it in the apropriate fiel</p>
<p>SQLEXEC(gn_conn, &#8220;CALL getprogram(&lt;program name&gt;, &lt;userid&gt;), lc_fname)</p>
<p>lc_fname is the filename comprised out of the filename from the proper extension based on the type (scx, frx, vcx) etc. The only special handling is needed for FXP and MPX files. There is a little hit in the initial load time of the application as the initial startup screen and VCX files need to be retrieved from the server but in my oppinion this outweighs the benefits.</p>
<p>Then during program execution where one had a code looking something like</p>
<p>DO FORM &lt;formname&gt; WITH &lt;arglist&gt;</p>
<p>Now we insert a little codeblock before that statement</p>
<p>SQLEXEC(gn_conn, &#8220;CALL getprogram(&lt;program name&gt;, &lt;userid&gt;), &lt;formname&gt;)</p>
<p>DO FORM &lt;formname&gt; WITH &#8230; &amp;&amp; our initial statement</p>
<p>ERASE &lt;formname&gt;  &amp;&amp;delete the form.</p>
<p>On the SQL server now the getprogram procedure can do all kinds of things like checking if the user has the right to access that form if not we can send him a standard &#8220;Access denied&#8221; form and send an email to the developer that the client was able to request a form he was not supposed to see. because why put it on that particular users menu when (s)he is not allowed to use it. The routine can also check all objects on a particular form against a table and remove any objects from a FORM that are off limits for that particular user. So for example if that user is not allowed to delete a record &#8211; then why send him a form with a delete button on it or maybe that particular user is not supposed to see the cost or profit margine of a particular item. So now we can have a system administrator remove objects from a particular users form. In code most things are handled in the events and methods of each object as it is and references to that object from other objects is rare. But this can be handled too quite simply.</p>
<p>Lets assume we want to update the value property of an object that might get removed from another object. All we have to do now is something like</p>
<p>IF TYPE(&lt;objectreference&gt;) = &#8220;O&#8221;</p>
<p>       &lt;objectreference&gt;.value = &lt;expr&gt;</p>
<p>ENDIF</p>
<p>So all that would be needed is to wrap the statement into an IF &#8230; ENDIF block</p>
<p>Othere benefits from this approach are that the code resides only for short periods on the clients machine and is therefor pretty save. Especially as one can blank out the code fields as VFP only needs the objcode (compiled code) to run. With that we can keep the source safe. And mainly updating becomes now a breeze. When we now change a form all we do is replace it once in the table on the SQL server and any client no matter where (s)he is located will get the new form or feature, menu item, report the next time (s)he requests it. No more need of kicking people off to change something or get numerous calls &#8220;I got the update what do I do with it&#8221;.</p>
<p>All the client now has to install is the loader program. Everything else is handled by the forms and objects this loader program loads. And if a user gets terminated and takes the loader program with him/her on the way out the door well then all (s)he hopefully gets the next time (s)he logs in is her own special startup program saying</p>
<p>YOU HAVE BEEN TERMINATED</p>
]]></content:encoded>
			<wfw:commentRss>http://pirringers.com/mysqlblog/?feed=rss2&amp;p=59</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Planet MySQL, Voting and My Blog</title>
		<link>http://pirringers.com/mysqlblog/?p=56</link>
		<comments>http://pirringers.com/mysqlblog/?p=56#comments</comments>
		<pubDate>Fri, 19 Mar 2010 19:56:34 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Planet Mysql]]></category>
		<category><![CDATA[Voting]]></category>

		<guid isPermaLink="false">http://pirringers.com/mysqlblog/?p=56</guid>
		<description><![CDATA[I started with my VFP to MYSQL conversion in earnest at the end of December. To keep the cost down until I know what I am doing I am using the GA version 5.1.43 and now WORKBENCH to do my development. Actually initially I used the 5.0 Administrator and Query which worked fine but did [...]]]></description>
			<content:encoded><![CDATA[<p>I started with my VFP to MYSQL conversion in earnest at the end of December. To keep the cost down until I know what I am doing I am using the GA version 5.1.43 and now WORKBENCH to do my development. Actually initially I used the 5.0 Administrator and Query which worked fine but did not have any supports for triggers. Workbench 5.1.18 frustrated me for many reasons so I started to use the Beta of 5.2 which in my opinion is better but still a Beta. Trying to help to improve I filed about 20 or so bug reports and got into email conversations with the developers.</p>
<p>In course of that I mentioned the process of converting from VFP (Visual Foxpro) to MYSQL and I was asked to Blog about my experiences with that and feed it to Planet MySQL. Starting to read Planet MySQL I must say that I am probably an oddity here as there does not seem to be any VFP people here. For that reason I also fed and anounced my Blog to some VFP boards and feeds. I even got picked up by a PHP blog for reasons I dont know. In the VFP community the response was quite positive an some discussions, comments and email were exchanged</p>
<p>As for Planet MySQL. Well I got some thumbs up and some thumbs down and what bothers me is that I do not know why. Maybe there are some comments somewhere but personally I find a thumbs up or down quite useless if not accompanied by a comment.</p>
<p>Why is a particular blog good? what was the good point(s)?</p>
<p>Whis is it bad. Is it inapropriate here, poorly written? Is the procedure outlined within that particular blog flawed? Is there a better way?</p>
<p>As things keep changing we all are learning so we all can benefit from some advice and some help to stay or get on the right course.</p>
<p>So I apreachiate the votes because it means that someone at least read it. But I would really like to get a note on why its up or especially down. My goal is to become better and to have this blog here to be my contribution and hopefully become a one stop place for anyone facing the task to switch from VFP to MYSQL. So if there is a better way then please by all means drop me a note</p>
<p>Hoping to see some comments from here</p>
<p>Martin Pirringer</p>
]]></content:encoded>
			<wfw:commentRss>http://pirringers.com/mysqlblog/?feed=rss2&amp;p=56</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Security Benefit By Switching to MYSQL</title>
		<link>http://pirringers.com/mysqlblog/?p=52</link>
		<comments>http://pirringers.com/mysqlblog/?p=52#comments</comments>
		<pubDate>Fri, 19 Mar 2010 13:52:21 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Security]]></category>
		<category><![CDATA[VFP to MYSQL Migrations]]></category>

		<guid isPermaLink="false">http://pirringers.com/mysqlblog/?p=52</guid>
		<description><![CDATA[From a security standpoint VFP is a nightmare. You can do pretty much anything on the program end with passwords etc but I never found a way around the fact that you pretty much have to give the client access to the Data directory. VFP being a flat file system (even with the introduction of [...]]]></description>
			<content:encoded><![CDATA[<p>From a security standpoint VFP is a nightmare. You can do pretty much anything on the program end with passwords etc but I never found a way around the fact that you pretty much have to give the client access to the Data directory. VFP being a flat file system (even with the introduction of DBC&#8217;s &#8211; databases) you had to give the client at the very least read access to the tables and if you wanted him/her to change something then write access too. Only way around that might have been to use VFPOLEDB and a web server but then why use VFP.</p>
<p>Now moving to MYSQL I want to point out some instant benefits you get migrating to MYSQL and that is security. One thing probably not immediatly visible to a newbie is the power of the &#8220;DEFINER&#8221; and INVOKER security options on stored procedures. This allows clients to do things under certain circumstances that they do have no right to. I want to use the following example to demonstrate the very usefulness of that feature.</p>
<p>First of course we wont give any of the administrative roles to the user accounts used to access the server. We probably wont even give that account the &#8220;DELETE&#8221; right on the schema. We do not want an errant DELETE&#8230;. to run havoc on our tables &#8211; accidental or malicious as it may be. But there might be a scenario where the client needs to delete something.</p>
<p>We can accomplish this by wrapping the DELETE statemente into a stored procedure We created the stored procedure with a statement something like</p>
<pre>CREATE DEFINER = `user_with <a href="mailto:delete_rights`@`%">delete_rights`@`%</a>` PROCEDURE `deletesomething` (&lt;some parameters&gt;)
BEGIN
-- some checking of the parameters and
DELETE ...... -- our delete statement probable a prepared one</pre>
<p>We do not need to add &#8220;SECURITY DEFINER&#8221; as that is the default option. The above means that the statements within the procedure are executed as if the user was the user_with <a href="mailto:delete_rights`@`%">delete_rights</a> user and not the regular user account that logged in. This way now our client can delete what he needs to delete and we can make sure that he cannot delete anything else accidentally or otherwise.</p>
<p>He needs the &#8220;EXECUTE&#8221; priviledge to run the &#8220;CALL &#8230;. &#8221; statement. But that is all he needs for that. If one is so inclined one could wrap pretty much any statement into a STORED PROCEDURE wrapper and do the same for INSERT, UPDATE and maybe even SELECT statements to enhance the security and integrity of the whole system</p>
]]></content:encoded>
			<wfw:commentRss>http://pirringers.com/mysqlblog/?feed=rss2&amp;p=52</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Optimizing differences</title>
		<link>http://pirringers.com/mysqlblog/?p=45</link>
		<comments>http://pirringers.com/mysqlblog/?p=45#comments</comments>
		<pubDate>Fri, 19 Mar 2010 12:51:33 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[SQL Statements]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[MYSQL]]></category>
		<category><![CDATA[Optimizing]]></category>
		<category><![CDATA[Queries]]></category>
		<category><![CDATA[VFP]]></category>

		<guid isPermaLink="false">http://pirringers.com/mysqlblog/?p=45</guid>
		<description><![CDATA[I would like to use this installment of my blog to show some differences between VFP and MYSQL on how problems need to be solved. On the top it seems to me that the MYSQL optimizer needs a quite different strategy and some more help then the &#8220;rushmore&#8221; one in VFP. There is probably some [...]]]></description>
			<content:encoded><![CDATA[<p>I would like to use this installment of my blog to show some differences between VFP and MYSQL on how problems need to be solved. On the top it seems to me that the MYSQL optimizer needs a quite different strategy and some more help then the &#8220;rushmore&#8221; one in VFP. There is probably some room for improvements left in both approaches but I usually stop optimizing when I get under 2 seconds on getting results.</p>
<p>The task was to roll back the AR aging to a prior date. Basically a standard business problem. Accounting comes, looks at last months aging, makes some adjustments and then wants to rerun the aging, or the bank wants it as of a particular date or things just went nuts and everyone was busy with &#8230; whatevever &#8230;. and did not get to run the aging at the proper time. So to handle this a query was devised to back out all invoices and applications as to a certain date.</p>
<p>In the late 80&#8217;s the customer got and SBT accounting system and that was then modified and modernized over the decades so the  invoices, credits and payments are  stored in the invoice master file. fields if interest: Invamt, Paidamt, Invdte, Balance, custno, salesmn etc. Cash and credit applications are in a file called cc_apply in VFP the problem was solved with a query that ran into a little under 2 seconds and looked like this</p>
<p>SELECT a.custno, ;<br />
a.invno AS invno, ;<br />
a.invdte AS invdte, ;<br />
a.balance + NVL((SELECT SUM(f.apply + f.disc) ;<br />
FROM &#8220;accounts receivables!cc_apply&#8221; f;<br />
WHERE f.invno = a.invno AND TTOD(f.appdate) &gt; ld_date), 0) ;<br />
- NVL((SELECT SUM(f.apply) ;<br />
FROM &#8220;accounts receivables!cc_apply&#8221; f;<br />
WHERE f.ccati = a.invno AND TTOD(f.appdate) &gt; ld_date), 0) AS balance, ;<br />
a.paidamt &#8211; NVL((SELECT SUM(f.apply + f.disc) ;<br />
FROM &#8220;accounts receivables!cc_apply&#8221; f;<br />
WHERE f.invno = a.invno AND TTOD(f.appdate) &gt; ld_date), 0) AS paidamt, ;<br />
a.invamt &#8211; NVL((SELECT SUM(f.apply) ;<br />
FROM &#8220;accounts receivables!cc_apply&#8221; f;<br />
WHERE f.ccati = a.invno AND TTOD(f.appdate) &gt; ld_date), 0) AS invamt ;<br />
FROM &#8220;accounts receivables!invoice master&#8221; a, customer\customer cu, ;<br />
(SELECT DISTINCT e.invno as invno FROM (SELECT d.invno as invno ;<br />
FROM &#8220;accounts receivables!invoice master&#8221; d ;<br />
WHERE d.invdte &lt;= ld_date AND d.balance # 0 AND !d.arstat $ &#8220;Vv&#8221; ;<br />
UNION SELECT d.invno as invno ;<br />
FROM &#8220;accounts receivables!invoice master&#8221; d ,;<br />
&#8220;accounts receivables!cc_apply&#8221; b ;<br />
WHERE d.invno = b.invno AND d.invdte &lt;= ld_date AND d.balance = 0 AND !d.arstat $ &#8220;Vv&#8221; ; AND TTOD(b.appdate) &gt; ld_date ;<br />
AND (b.apply # 0 OR b.disc # 0) ;<br />
UNION SELECT d.invno as invno ;<br />
FROM &#8220;accounts receivables!invoice master&#8221; d ,;<br />
&#8220;accounts receivables!cc_apply&#8221; b ;<br />
WHERE d.invno = b.ccati AND d.invdte &lt;= ld_date AND d.balance = 0 AND !d.arstat $ &#8220;Vv&#8221; ; AND TTOD(b.appdate) &gt; ld_date ;<br />
AND (b.apply # 0 OR b.disc # 0)) e) c ;<br />
WHERE a.invno = c.invno AND a.custno = cu.custno AND !a.arstat $ &#8220;Vv&#8221; AND &amp;lc_filter;<br />
ORDER BY a.custno, a.invdte, a.invno ;<br />
INTO CURSOR c_arorec NOFILTER READWRITE</p>
<p>&amp;lc_filter was used to filter out some other parameters like sales rep, state, customer etc.</p>
<p>The query was ported into MYSQL, NVL() was replaced with COALESCE()  and the statement that took about 2 seconds under VFP took a little over 50 in MYSQL. So a different approach was needed. MYSQL, even with hints seemed to have problems using the right indexes or come up with a reasonable plan as the EXPLAIN statement showed. Even though VFP (Visual foxpro) does not have a feature to show you exactly what it is doing, on a proper written query it will do most the selecting inside the indexes if possible as that is cheaper. The bottle neck in VFP is usually the network. As the processing gets done at the workstation you want to cut he total # of bytes sent to the workstation for processing down.</p>
<p>In  MYSQL its disk reads. I could not convince the optimizer to do things in the sequence I wanted on that query so I had no choice than the break that query and spoon feed it to the optimizer. From past trials it appears to me that the way to go with MYSQL is to cut down rowcount and disk read/writes. So here is the way I got MYSQL to solve the above problem in under 2 seconds.</p>
<p>For starters to cut the row count down as early as possible I decided to employ some level of denormalisation and put the custno, salesman, state,invdte, dtepaid into the cc_apply file. I keep that in sync in the INSERT and update triggers and with foreign keys. This puts a little overhead on the INSERT and UPDATE statement but pays of big time at the time of data SELECTION. Furthermore, as we were going to do this in multiple SQL statement we wrap those statement into a stored procedure to handle our parameters and selections like</p>
<p>CREATE <a href="mailto:DEFINER=`whoever`@`%">DEFINER=`whoever`@`%</a>` PROCEDURE `arorec`(v_date DATE,<br />
v_state varchar(2), v_salesmn varchar(2), v_custno varchar(6), v_sum TINYINT)<br />
READS SQL DATA<br />
DETERMINISTIC<br />
BEGIN<br />
SET @v_date = v_date;<br />
SET @v_custno = v_custno;<br />
SET @v_state = v_state;<br />
SET @v_salesmn = v_salesmn;<br />
SET @v_sum = v_sum; &#8212; summary or not<br />
/*Note need to use tempres and tempcr as you cannot use temp table(tempsel) 2x in a union*/<br />
DROP TABLE IF EXISTS tempsel;<br />
DROP TABLE IF EXISTS tempres;<br />
DROP TABLE IF EXISTS tempcr;</p>
<p>SO for starters we needed all the invoices that were created before the cut of date and that had moneys applied to them after the cut of date. As the resulting set is definitly way below the available ram on the target server we will put this resutl set into a temporary table in memory with</p>
<p>IF @v_custno IS null AND @v_state IS NULL AND @v_salesmn IS NULL THEN<br />
/* GET EVERYONE*/<br />
PREPARE stmt1 FROM &#8221;<br />
CREATE TEMPORARY TABLE tempsel ENGINE = MEMORY SELECT invno, Sum(apply) as apply, SUM(disc) AS disc<br />
FROM cc_apply WHERE invdte &lt;= ? AND appdate &gt; ? GROUP BY invno&#8221; ;<br />
EXECUTE stmt1 USING @v_date, @v_date;<br />
ELSEIF @v_custno IS NOT NULL AND @v_salesmn IS NULL THEN<br />
/* State is irrelevant as each customer has one address customere only*/<br />
&#8230;&#8230;.The selections for other criteria</p>
<p>Next we had to join this with the invoice and CM header records in the invoice master table plus add all the invoices that were created the cutoff date, are still open and did not have anything applied to them. Normally we could do that in a UNION, but MYSQL does not let you use a UNION on a TEMPORARY TABLE so we use another temporary table like this</p>
<p>PREPARE stmt1 FROM &#8221;<br />
CREATE TEMPORARY TABLE tempres ENGINE = MEMORY<br />
SELECT ma.custno, ma.ticket AS refno, ma.dtepaid, ma.artype, ma.ponum AS checkno, 0000000000.00 AS future,<br />
ma.invno, ma.balance + te.apply &#8211; te.disc AS BALANCE, ma.entered, ma.invdte, ma.invamt, ma.paidamt, getpd(@v_date, ma.invdte) AS gp<br />
FROM `invoice master` ma INNER JOIN tempsel te ON ma.invno = te.invno<br />
WHERE ma.invdte &lt;= ? AND artype IN (&#8216; &#8216;, &#8216;I&#8217;, &#8216;C&#8217;, &#8216;W&#8217;)&#8221;;<br />
EXECUTE stmt1 USING @v_date;<br />
DEALLOCATE PREPARE stmt1;<br />
ALTER TABLE tempres ADD INDEX custno(custno);<br />
/* UNION of invoices still open without anything applied and futures*/</p>
<p>IF @v_custno IS null AND @v_state IS NULL AND @v_salesmn IS NULL THEN<br />
Prepare stmt1 FROM &#8221;<br />
INSERT INTO tempres<br />
SELECT ma.custno, ma.ticket AS refno, ma.dtepaid, ma.artype, ma.ponum AS checkno, 0000000000.00 AS future,<br />
ma.invno, ma.balance, ma.entered, ma.invdte, ma.invamt, ma.paidamt, getpd(@v_date, ma.invdte) AS gp<br />
FROM `invoice master` ma LEFt JOIN tempsel t2 ON ma.invno = t2.invno<br />
WHERE t2.invno IS NULL and ma.open = 1 and ma.invdte &lt;= ? AND artype IN (&#8216; &#8216;, &#8216;I&#8217;, &#8216;C&#8217;, &#8216;W&#8217;) UNION SELECT ma.custno, ma.ticket AS refno, ma.dtepaid, ma.artype, ma.ponum AS checkno, ma.invamt AS future, ma.invno, ma.balance + COALESCE(cc.apply, 0) &#8211; COALESCE(cc.disc, 0) AS BALANCE, ma.entered, ma.invdte, ma.invamt, ma.paidamt, getpd(@v_date, ma.invdte) AS gp FROM `invoice master` ma LEFT JOIN cc_apply cc ON ma.invno = cc.invno WHERE ma.invdte &gt; ? AND ma.entered &lt;= ? AND artype IN (&#8216; &#8216;, &#8216;I&#8217;, &#8216;C&#8217;, &#8216;W&#8217;)&#8221;;<br />
EXECUTE stmt1 USING @v_date, @v_date, @v_date;<br />
ELSEIF @v_custno IS NOT NULL AND @v_salesmn IS NULL THEN<br />
&#8230;&#8230;. The other statements for selection by salesmn, state, etc</p>
<p>We then do the same for the payments and end up with a temporary table &#8220;TEMPRES&#8221; that holds all the records needed for our aging.</p>
<p>In VFP we then used &#8220;SET RELATION TO&#8221; to get access to some other relevant fields for our reporting but it is much more efficient to have the MYSQL server send all the data in one result set so we are doing this in the final SELECT statement that looks something like this</p>
<p>SELECT te.*, cu.email, cu.statml, cu.statfmt, cu.company, cu.phone, cu.faxno, cu.contact, cu.address1,<br />
cu.address2, cu.city, cu.state AS state, cu.zip, cu.pterms, cu.lastpay AS lastpay, cu.limit,<br />
cu.lpymt, cu.ldate AS ldate, cu.lsale, cu.inactive AS inactive,<br />
IF(te.gp=&#8217;C',te.invamt &#8211; te.paidamt,0.00) AS current,<br />
IF(te.gp=&#8217;1&#8242;,te.invamt &#8211; te.paidamt,0.00) AS pd1,<br />
IF(te.gp=&#8217;2&#8242;,te.invamt &#8211; te.paidamt,0.00) AS pd2,<br />
IF(te.gp=&#8217;3&#8242;,te.invamt &#8211; te.paidamt,0.00) AS pd3,<br />
IF(te.gp=&#8217;3&#8242;,te.invamt &#8211; te.paidamt,0.00) AS pd4,<br />
IF(te.gp=&#8217;O',te.invamt &#8211; te.paidamt,0.00) AS pdover<br />
FROM tempres te INNER JOIN customer cu ON te.custno = cu.custno<br />
ORDER by te.custno ASC, te.invdte ASC, te.invno ASC;</p>
<p>The getpd() is a stored function that determines based on parameters stored in another table if we want the output in 30/60/90/120, 7/14/28/35. based on the end of the week, end of month or any date in between to allow full flexibility in the aging output.</p>
<p>For those interested.  On a table with about 500,000 invoices records, 300,000 records in the application table, on my development server on my laptop the whole SP takes 1.4 seconds in the worst case scenario. Granted the complete stored procedure with formattin as shown above is over 300 lines long with many statements as compared to one straight forward SQL statement in VFP. But the end result is on par with VFP with the added benefit to provide improved results over slow connections over the internet</p>
]]></content:encoded>
			<wfw:commentRss>http://pirringers.com/mysqlblog/?feed=rss2&amp;p=45</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Keeping save &#8211; Injection attacks etc.</title>
		<link>http://pirringers.com/mysqlblog/?p=39</link>
		<comments>http://pirringers.com/mysqlblog/?p=39#comments</comments>
		<pubDate>Sun, 07 Mar 2010 12:46:44 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[SQL Statements]]></category>
		<category><![CDATA[VFP to MYSQL Migrations]]></category>
		<category><![CDATA[MYSQL]]></category>
		<category><![CDATA[Queries]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[VFP]]></category>

		<guid isPermaLink="false">http://pirringers.com/mysqlblog/?p=39</guid>
		<description><![CDATA[I had someone point out to me that some of the sample code I have used might be vulnerable to an insertion attack. So I feel it is time to address this subject.
First when using examples I try to just focus on the subject of the current blog and often strip a lot of things that [...]]]></description>
			<content:encoded><![CDATA[<p>I had someone point out to me that some of the sample code I have used might be vulnerable to an insertion attack. So I feel it is time to address this subject.</p>
<p>First when using examples I try to just focus on the subject of the current blog and often strip a lot of things that are irrelevant to the point I am trying to make out. But now to the subject of keeping your code save.</p>
<p>If you write and SQL statement looking something like</p>
<p>lc_sql = &#8220;SELECT * from customer WHERE custno = &#8216;&#8221; + lc_custno + &#8220;&#8216;&#8221;</p>
<p>Someone could enter &#8220;&#8216;; TRUNCATE TABLE customer;&#8217;&#8221; resulting in  a SQL statement looking like</p>
<p>SELECT * from customer WHERE custno = &#8221;; TRUNCATE TABLE customer; &#8221;</p>
<p>Which could be quite disasterous. So to avoid this we could use parameterized queries which I cannot as I am using a seperate function to wrap around the SQLEXEC() function to do all kinds of things like connection maintenance, Error handling, Logging and even in some cases sending an email to the IT when certain conditions arise. So the above mentioned problem needs to be addressed when creating the sql statement.</p>
<p>The first line of protection is to properly escape any string I send. The table below is copied from the MYSQL manual showing what needs escaping</p>
<table border="1">
<colgroup span="1">
<col span="1"></col>
<col span="1"></col>
</colgroup>
<tbody>
<tr>
<td><code>\0</code> <a name="id3436214"></a><a name="id3436223"></a></td>
<td>An ASCII NUL (<code>0x00</code>) character.</td>
</tr>
<tr>
<td><code>\'</code> <a name="id3436251"></a><a name="id3436260"></a></td>
<td>A single quote (“<code>'</code>”) character.</td>
</tr>
<tr>
<td><code>\"</code> <a name="id3436289"></a><a name="id3436298"></a></td>
<td>A double quote (“<code>"</code>”) character.</td>
</tr>
<tr>
<td><code>\b</code> <a name="id3436326"></a><a name="id3436335"></a></td>
<td>A backspace character.</td>
</tr>
<tr>
<td><code>\n</code> <a name="id3436358"></a><a name="id3436366"></a><a name="id3436375"></a><a name="id3436384"></a></td>
<td>A newline (linefeed) character.</td>
</tr>
<tr>
<td><code>\r</code> <a name="id3436407"></a><a name="id3436416"></a><a name="id3436424"></a></td>
<td>A carriage return character.</td>
</tr>
<tr>
<td><code>\t</code> <a name="id3436447"></a><a name="id3436456"></a></td>
<td>A tab character.</td>
</tr>
<tr>
<td><code>\Z</code> <a name="id3436478"></a><a name="id3436487"></a></td>
<td>ASCII 26 (Control-Z). See note following the table.</td>
</tr>
<tr>
<td><code>\\</code> <a name="id3436509"></a><a name="id3436518"></a></td>
<td>A backslash (“<code>\</code>”) character.</td>
</tr>
<tr>
<td><code>\%</code> <a name="id3436547"></a><a name="id3436556"></a></td>
<td>A “<code>%</code>” character. See note following the table.</td>
</tr>
<tr>
<td><code>\_</code> <a name="id3436585"></a><a name="id3436594"></a></td>
<td>A “<code>_</code>” character. See note following the table.</td>
</tr>
</tbody>
</table>
<p>So I wrote a vfp function called addslashes()</p>
<p>FUNCTION Addslashes</p>
<p>LPARAMETERS lc_str, ll_notrim</p>
<p>lc_str = STRTRAN(lc_str,&#8221;\&#8221;, &#8220;\\&#8221;)</p>
<p>lc_str = STRTRAN(lc_str,&#8221;&#8216;&#8221;, &#8220;\&#8217;&#8221;)</p>
<p>lc_str = STRTRAN(lc_str,&#8217;&#8221;&#8216;, &#8216;\&#8221;&#8216;)</p>
<p>lc_str = STRTRAN(lc_str,&#8217;%', &#8216;\%&#8217;)</p>
<p>lc_str = STRTRAN(lc_str,&#8217;_', &#8216;\_&#8217;)</p>
<p>lc_str = STRTRAN(lc_str,CHR(0), &#8216;\0&#8242;)</p>
<p>lc_str = STRTRAN(lc_str,CHR(10), &#8216;\n&#8217;)</p>
<p>lc_str = STRTRAN(lc_str,CHR(13), &#8216;\r&#8217;)</p>
<p>lc_str = STRTRAN(lc_str,CHR(26), &#8216;\z&#8217;)</p>
<p>lc_str = STRTRAN(lc_str,CHR(9), &#8216;\t&#8217;)</p>
<p>lc_str = STRTRAN(lc_str,CHR(8), &#8216;\b&#8217;)</p>
<p>IF !ll_notrim THEN</p>
<p>      lc_str = RTRIM(lc_Str)</p>
<p>ENDIF</p>
<p>RETURN lc_str</p>
<p>I could Have used a FOR &#8230; NEXT Loop with a CASE statement and loop through the string but found that that was slower than calling the STRTRAN() function multiple times. So now we run every string through this routine and the above SQL statement becomes</p>
<p>lc_sql = &#8220;SELECT * FROM customer WHERE custno = &#8216;&#8221; + ADDSLASHES(lc_custno) + &#8220;&#8216;&#8221;</p>
<p>And the above attempt to mess with the system will produce an sql now looking like</p>
<p>SELECT * from customer WHERE custno = &#8216;\&#8217;; TRUNCATE TABLE customer; \&#8221;</p>
<p>Which would just return an empty record set.</p>
<p>This will also allow me now to insert and retrieve string like &#8220;John&#8217;s Barber Shop&#8221; or multi line entries. Furthermore I made it a rule to emulate strong typing when it comes to SQL statements. Simply to to prevent a &#8220;Garbage in&#8221;/&#8221;Garbage out&#8221; Scenario. So Each textget has its validation routine and is bound to a local cursor. VFP allows you to change the type of a variable by simple assigning some different type of data to it but fields in a cursor are of a fixed type. This also allows us to use functions to build the SQL statements based on the underlying cursor. So every string and Memofield gets treated with ADDSLASHES() each numeric with STR(), Each date field WITH DTOS() to make a MYSQL valid date string etc.</p>
<p>At this point some might say it looks like a lot of work and some of this is replicating what VFP does through remote views. So why use SQL Pass through and not remote views? The single aswer is performance. This way I can control what to send when and how and avoid data being sent unnecessarily or at inoportune times.</p>
]]></content:encoded>
			<wfw:commentRss>http://pirringers.com/mysqlblog/?feed=rss2&amp;p=39</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Converting and optimizing SQL Statements</title>
		<link>http://pirringers.com/mysqlblog/?p=32</link>
		<comments>http://pirringers.com/mysqlblog/?p=32#comments</comments>
		<pubDate>Sat, 06 Mar 2010 14:57:51 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[SQL Statements]]></category>
		<category><![CDATA[MYSQL]]></category>
		<category><![CDATA[Oprtimizing]]></category>
		<category><![CDATA[Queries]]></category>
		<category><![CDATA[VFP]]></category>

		<guid isPermaLink="false">http://pirringers.com/mysqlblog/?p=32</guid>
		<description><![CDATA[Now it became time to conveert some SQL statements. Even though many SELECT SQL statements will run unchanged in MYSQL it might be wise to check and see if they are performing well. There are some differences in how VFP and MYSQL optimize queries and a query that just performs great in VFP might be very [...]]]></description>
			<content:encoded><![CDATA[<p>Now it became time to conveert some SQL statements. Even though many SELECT SQL statements will run unchanged in MYSQL it might be wise to check and see if they are performing well. There are some differences in how VFP and MYSQL optimize queries and a query that just performs great in VFP might be very slow in MYSQL. And then there are some cases where we have to replace things that are possible in VFP but not supported in MYSQL.</p>
<p>First lets assume that our tables all have lots of records. After all it does not pay to spend time on optimizing on tables with a few dozend records. I also will not go into VFP optimization here as this is a MYSQL blog.</p>
<p>Lets assume for our first case we might have a table with invoice detail records and a table with items that are on sale. So now we want to get maybe 2 queries. One to show a total of all the items that were on sale and the second a total of all the items not on sale. I will leave date selections etc out of this as I just want to focus in on some performance issues</p>
<p>In VFP we might have had something like</p>
<p>SELECT &lt;fieldlist&gt; FROM detail det WHERE INDEXSEEK(det.item, .f., &#8220;promotions&#8221;, &#8220;item&#8221;)</p>
<p>SELECT &lt;fieldlist&gt; FROM detail det WHERE .NOT. INDEXSEEK(det.item, .f., &#8220;promotions&#8221;, &#8220;item&#8221;)</p>
<p>respectively depending which set we want. In VFP it was quite fine to use and INDEXSEEK in the Where clause. I sometimes used it in place of a join as it seemed to work faster and in previous version of VFP was a way to get around limits in the number of JOINS and the complexity of a query vfp supports.</p>
<p>In MYSQL now you could replace the INDEXSEEK with an IN or EXISTS(SELECT&#8230;.) Subquery  so the query rewritten will look something like</p>
<p>SELECT &lt;fieldlist&gt; FROM detail det WHERE EXISTS (SELECT * FROM promotions p WHERE p.item = det.item)</p>
<p>Or the &#8220;NOT EXISTS&#8221; version for the oposite result. You could also use IN but in some cases that might even be more inefficient as IN retrieves the whole matching subset and EXISTS stops at the first occurance.</p>
<p>If we run the above query through &#8220;EXPLAIN&#8221; to see what MYSQL is doing we will see that on ther &#8220;SELECT &lt;fieldlist&gt; FROM detail det&#8221; it will perform a table scan and  then the item  index of the promotions table. So having a large &#8220;details&#8221; table that has also an &#8220;item&#8221; index this is not very efficient. In MYSQL the way to improve on that is to rewrite the query as a join. like</p>
<p>SELECT &lt;field list&gt; FROM detail det JOIN promotions p ON det.item = p.item</p>
<p>In this case MYSQL will use both item indexes and only retrieve the items matching. It gets a little bit more complicated with the case where we want to select items that have no promotions but we can do a join there too with</p>
<p>SELECT &lt;field list&gt; FROM detail det LEFT JOIN promotions p ON det.item = p.item WHER p.item = NULL</p>
<p>This one again will make the selection on the index level and depending on the amount of records you are dealing with result in great performance gains.</p>
<p>Lets now look at another scenario. Lets assume we have a invoice master table with  as &#8220;balance&#8221; field and we want to filter out as part of a query all records that are open so we are looking for &#8220;BALANCE &lt;&gt; 0&#8243; In vfp we created an index on balance  and that improved the statement</p>
<p>SELECT &lt;fieldlist&gt; FROM master WHERE balance &lt;&gt; 0</p>
<p>drastically. Now if we do the same in mysql and we check with</p>
<p>EXPLAIN SELECT &lt;fieldlist&gt; FROM master WHERE balance &lt;&gt; 0</p>
<p>We will find that MYSQL is doing a table scan. MYSQL seems to have a problem with &#8220;&lt;&gt; &lt;keyfield&gt;&#8221;</p>
<p>You can get a slight improvement by</p>
<p>SELECT &lt;fieldlist&gt; FROM master WHERE balance &lt; UNION SELECT &lt;fieldlist&gt; FROM master WHERE balance &gt; 0</p>
<p>As it seems MYSQL has an easier time using the index with just the &gt; and the &lt; and a UNION but you are running into unnecessary overhead there too. What I have found most efficient so far is to add a field to the table</p>
<p>unpaid BIT</p>
<p>And then create an index on this field and set it in the BEFORE UPDATE and BEFORE INSERT triggers with</p>
<p>IF NEW.balance = 0 THEN SET NEW.unpaid = 0; ELSE SET NEW.unpaid = 1; END IF;</p>
<p>THE sql statement now looks like</p>
<p>SELECT &lt;fieldlist&gt; FROM master WHERE unpaid = 1</p>
<p>I used the BIT type field as it nicely translates into a VFP logical field with .f. for 0 and .t. FOR 1. But you probably can achieve the same by using an TINYINT or CHR(1)  or BOOLEAN field.</p>
<p>The performance improvement was drastic.</p>
<p>Beyond this it is always good to remember to</p>
<p>1.) make sure you have indexes on the fields in the &#8220;WHERE&#8221; clause and they are being used. USE &#8220;EXPLAIN &#8230;.&#8221; in the MYSQL query editor to find out.</p>
<p>2.) keep the row count to a minimum especially if you are using subqueries. So it often makes sense to first cut the number of records down with a subquery. I will go into detail on this in a seperate blog</p>
<p>As always comments/suggestions are welcome as this is all work in progress</p>
]]></content:encoded>
			<wfw:commentRss>http://pirringers.com/mysqlblog/?feed=rss2&amp;p=32</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Converting a Simple Form</title>
		<link>http://pirringers.com/mysqlblog/?p=26</link>
		<comments>http://pirringers.com/mysqlblog/?p=26#comments</comments>
		<pubDate>Wed, 17 Feb 2010 00:25:51 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Conferting Forms and Reports]]></category>
		<category><![CDATA[VFP to MYSQL Migrations]]></category>

		<guid isPermaLink="false">http://pirringers.com/mysqlblog/?p=26</guid>
		<description><![CDATA[ 
When modifying VFP Forms to run the MYSQL Backend the first question most likely will be: How do I get the data from MYSQL efficiently to my VFP Form without rewriting the whole thing from scratch. The form previously accessing VFP tables most likely opened them in the Data Environment so it might be tempting [...]]]></description>
			<content:encoded><![CDATA[<p> </p>
<p>When modifying VFP Forms to run the MYSQL Backend the first question most likely will be: How do I get the data from MYSQL efficiently to my VFP Form without rewriting the whole thing from scratch. The form previously accessing VFP tables most likely opened them in the Data Environment so it might be tempting to just replace them with remote cursors or with cursor adapters mimicking the VFP tables and views previously used. Granted the Form might just work with minimal effort but in most cases this approach would be very inefficient. At the same token the VFP controls on the form most likely are bound to some cursor/alias by use of the recordsource or controlsource properties. So unless one wants to do a lot of coding establishing those links later, some fitting cursor needs to exist before the controls are initialized.</p>
<p>Lets assume a simple scenario where we have a customer screen, with a field where you enter the customer code, a little browse control that pops up to give you some choices in case your entry does not exist, and then lots of controls displaying the customer information. The original screen most likely worked by just putting the customer table into the dataenvironment and then bind the controls to it.</p>
<p>So if we replace that with a remote view in the dataenvironment the remote view would have to have “SELECT * FROM customer” as its select statement. If the customer file is very small and not expected to grow much it might be just the easiest to insert a “customer” cursoradaptor into the dataenvironment and use the builder to establish the connection and update method and let VFP take care of the rest. The drawback of this method is that most likely a lot of data would be exchanged between the server and the form that is not really needed. So if the customer table is large and we have a slow connection – maybe over the internet – the performance might be unacceptable. So what can we do to improve performance without getting into too big of a rewrite.</p>
<p>Accessing Data remotly we probably want to keep the traffic between VFP and MYSQL at a minimum. We can accomplish that by using SQL passthrough This gives us full control and the possibility to optimize.</p>
<p>First we are going to have to establish a cursor for the controls before the load. I have found that the forms “LOAD” event is a good place for that. I have a UDF called MYSQLEXEC() that handles all the connection establishing and communication with the MYSQL server. I might write in another blog about it but for now just use it. Its Syntax is MYSQLEXEC(&lt;sqlstatement&gt;, &lt;cursoralias&gt;,&lt;errormessage&gt;, &lt;assync&gt;) It returns .t or .f. based on its success</p>
<p>So in the LOAD event I would put</p>
<p>IF !MYSQLEXEC(“SELECT * FROM customer LIMIT 0”, “customer”, “Cannot access customer File”, .f.)</p>
<p>    THISFORM.RELEASE</p>
<p>    RETURN</p>
<p>ENDIF</p>
<p>This will create a cursor with 0 records – which is enough for the controls to have something to bind to.</p>
<p>Later in the VALID or LOSTFOCUS events of the Customer # textbox I might have had some code like</p>
<p>SELECT customer</p>
<p>IF !SEEK(THIS.value)</p>
<ul>
<li>whatever we did to display the browse screen/control</li>
</ul>
<p>ENDIF</p>
<p>I could now replace this with</p>
<p>IF !MYSQLEXEC(“SELECT * FROM customer WHERE custno =&#8217;” + THIS.value + “&#8217;”, “c_temp”, “Cannot access customer File”, .f.)</p>
<p>    THISFORM.RELEASE</p>
<p>RETURN</p>
<p>ENDIF</p>
<p>SELECT customer</p>
<p>ZAP &amp;&amp; remove previous record from the customer cursor</p>
<p>INSERT INTO customer SELECT * FROM c_temp</p>
<p>You might ask why we do not load the data directly into the customer cursor. The reason is that SQLEXEC which we have to use for passthrough will close the old “customer” cursor and then reestablish a new one which causes grids and other controls to unbind. In grids all columns and controls attached to them would also loose their bindings and we would have to write a routine to establish it again. By ZAPping the customer cursor and then inserting from the c_temp cursor into the customer cursor no unbinding happens and all we have to do is a</p>
<p>THISFORM.REFRESH</p>
<p>So with this method we only transferred the structure an one record over the connection. To further improve on this we could replace the “SELECT *” WITH “SELECT &lt;fieldlist&gt;” to only retrieve the fields we need.</p>
<p>This might especially be benefitial for the Browse screen which we might display in case the entered customer # does not exist. Lets assume that in case the customer selected and incorrect customer code or just entered a portion of it we want to display maybe 20 records close to the customers selection with 10 records before and 10 after it. In VFP we might not have cared and attached the whole “customer” table to the browse screen to allow the user to scroll through the whole database but that again would require to retrieve at the very least a certain number of fields for every record in the table. In foxpro we might have had something like: (assuming lc_custno contains the customer # that was not found)</p>
<p>SELECT customer</p>
<p>SET SOFTSEEK ON</p>
<p>SEEK lc_custno</p>
<p>SKIP -10</p>
<p>IF BOF()</p>
<p>    GO TOP</p>
<p>ENDIF</p>
<ul>
<li>display/refresh the grid. (in the lostfocus event of course)</li>
</ul>
<p>For mysql we first might change the binding from “customer” to something like “seekcust” to not interfere with the “customer” cursor used by the other controls and create a sql string something like this:</p>
<p>lc_sql = “SELECT * FROM (SELECT custno, company from customer where custno &lt; &#8216;&#8221; + ;</p>
<p>lc_custno + “&#8217; ORDER by custno desc limit 10) a “ + ;</p>
<p>“UNION SELECT * FROM( SELECT custno, company from customer where custno &gt;= &#8216;&#8221; + ;</p>
<p>lc_custno + “&#8217; ORDER by custno asc limit 10) a” + ;</p>
<p>“ORDER BY custno ASC”</p>
<p>By Selecting the 10 after and the 10 before the selected value and then sorting them by customer # we just transmit custno and company for 20 records. We then could put a “Next” an “Prev” button onto that little popup control that retrieve the next/prev 20 records. When the customer selects a customer we could stuff that value into the customer # setection textget and execute the “VALID” event</p>
]]></content:encoded>
			<wfw:commentRss>http://pirringers.com/mysqlblog/?feed=rss2&amp;p=26</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Transferring VFP Table Structures to MYSQL</title>
		<link>http://pirringers.com/mysqlblog/?p=17</link>
		<comments>http://pirringers.com/mysqlblog/?p=17#comments</comments>
		<pubDate>Fri, 12 Feb 2010 22:33:12 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Transferring Table Structures]]></category>
		<category><![CDATA[VFP to MYSQL Migrations]]></category>
		<category><![CDATA[MYSQL]]></category>
		<category><![CDATA[Table Structures]]></category>
		<category><![CDATA[VFP]]></category>

		<guid isPermaLink="false">http://pirringers.com/mysqlblog/?p=17</guid>
		<description><![CDATA[Automating Structure Transfer from VFP to MYSQL]]></description>
			<content:encoded><![CDATA[<p>So now that we have established some decent contact between VFP and MYSQL it is time to transfer as much as possible from the VFP Database into the MYSQL Schema. Being a long time believer that making a plan can keep you out of trouble I laid out the following steps for that task</p>
<ul>
<li>Transfer Table Structures</li>
<li>Set up Indexes</li>
<li>Set up foreign keys</li>
</ul>
<p>So lets look at each step more closely</p>
<p><span style="text-decoration: underline;"><strong>Transfer Table Structures</strong></span></p>
<p>Granted we could sit down and take a printout of all the structures and then use Workbench or something like it and start setting up the tables. But being faced with having to do that with close to 100 tables some of which having 40 or so fields this appeared like a daunting task. So I took a look to see how much could be optimized. Tables In MYSQL are created by using the CREATE TABLE ….. syntax which is pretty straight forward. Granted there are some VFP features that MYSQL does not have and some that are available in MYSQL and lacking in VFP but at least for the overlapping part one could quickly write some VFP code as follows:</p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">FUNCTION </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">CreateTable</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">PARAMETERS </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">lc_sourcetable, lc_desttable</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">USE </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">(lc_sourcetable) </span></span><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">AGAIN ALIAS </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">a_conv</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #000000;"><span style="font-family: Courier New, monospace;">ln_c = </span></span><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">AFIELDS</span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">(la_f)</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #000000;"><span style="font-family: Courier New, monospace;">lc_sql = &#8220;DROP TABLE IF EXISTS `&#8221;+lc_desttable + &#8220;`&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">IF </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">!MYSQLEXEC(lc_sql, .f., &#8220;Cannot drop table&#8221;)</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">    RETURN </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">.f.</span></span></span></p>
<p><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;">ENDIF</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #000000;"><span style="font-family: Courier New, monospace;">lc_sql = &#8220;CREATE TABLE `&#8221;+lc_desttable + &#8220;` (&#8220;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">FOR </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">ln = 1 </span></span><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">TO </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">ln_c</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">    IF </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">ln &gt; 1</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #000000;"><span style="font-family: Courier New, monospace;">       lc_sql = lc_sql + &#8220;, &#8220;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #000000;"><span style="font-family: Courier New, monospace;">    </span></span></span><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">ENDIF</span></span></span></p>
<p><span style="color: #0000ff;"> </span></p>
<p><span style="font-size: x-small;"><span style="color: #000000;"><span style="font-family: Courier New, monospace;">    lc_sql = lc_sql + &#8220;`&#8221; + la_f(ln, 1) + &#8220;` &#8221; </span></span></span></p>
<p><span style="color: #000000;"> </span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">    DO CASE</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">    CASE </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">la_f(ln, 2) = &#8220;C&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #000000;"><span style="font-family: Courier New, monospace;">        lc_sql = lc_sql + &#8220;VARCHAR(&#8221; + </span></span><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">ALLTRIM</span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">(</span></span><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">STR</span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">(la_f(ln, 3), 10, 0)) + &#8220;)&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">    CASE </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">la_f(ln, 2) = &#8220;D&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #000000;"><span style="font-family: Courier New, monospace;">        lc_sql = lc_sql + &#8220;DATE&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">    CASE </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">la_f(ln, 2) = &#8220;T&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #000000;"><span style="font-family: Courier New, monospace;">        lc_sql = lc_sql + &#8220;DATETIME&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">     CASE </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">la_f(ln, 2) = &#8220;M&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #000000;"><span style="font-family: Courier New, monospace;">         lc_sql = lc_sql + &#8220;TEXT&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">     CASE </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">la_f(ln, 2) = &#8220;L&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #000000;"><span style="font-family: Courier New, monospace;">         lc_sql = lc_sql + &#8220;BIT&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">     CASE </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">la_f(ln, 2) = &#8220;N&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #000000;"><span style="font-family: Courier New, monospace;">         lc_sql = lc_sql + &#8220;DECIMAL(&#8221; + + </span></span><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">ALLTRIM</span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">(</span></span><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">STR</span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">(la_f(ln, 3), 10, 0)) + ;</span></span></span></p>
<p><span style="color: #000000;"><span style="font-size: x-small;"><span style="font-family: Courier New, monospace;">         &#8221;, &#8220; + </span></span><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">ALLTRIM</span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">(</span></span><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">STR</span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">(la_f(ln, 4), 10, 0)) + &#8220;)&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">     CASE </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">la_f(ln, 2) = &#8220;I&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #000000;"><span style="font-family: Courier New, monospace;">         lc_Sql = lc_sql + &#8220;INTEGER&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">     CASE </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">la_f(ln, 2) = &#8220;G&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #000000;"><span style="font-family: Courier New, monospace;">         lc_sql = lc_sql + &#8220;MEDIUMBLOB&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">    ENDCASE</span></span></span></p>
<p><span style="color: #0000ff;"> </span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">    IF </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">!la_f(ln, 5)</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #000000;"><span style="font-family: Courier New, monospace;">        lc_sql = lc_sql + &#8221; NULL&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">    ELSE</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #000000;"><span style="font-family: Courier New, monospace;">        lc_sql = lc_sql + &#8221; NOT NULL&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">    ENDIF</span></span></span></p>
<p><span style="color: #0000ff;"> </span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">    IF </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">!</span></span><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">EMPTY</span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">(la_f(ln, 9))</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #000000;"><span style="font-family: Courier New, monospace;">        lc_sql = lc_sql + &#8221; DEFAULT &#8216;&#8221; + la_f(ln, 9) + &#8220;&#8216;&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">    ENDIF</span></span></span></p>
<p><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;">NEXT</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #000000;"><span style="font-family: Courier New, monospace;">lc_sql = lc_sql + &#8220;) ENGINE = INNODB&#8221;</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">IF </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">!MYSQLEXEC(lc_sql, .f., &#8220;Cannot create table&#8221;)</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">    RETURN </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">.f.</span></span></span></p>
<p><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;">ENDIF</span></span></span></p>
<p><span style="font-size: x-small;"><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;">RETURN </span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;">.t.</span></span></span></p>
<p>This Function simple creates a CREATE TABLE statement for the VFP table &lt;lc_sourcetable&gt; sent to it an creates a mysql table named &lt;lc_desttable&gt; if it is successful then it returns .t. otherwise .f. The MYSQLEXEC() function is a udf I wrote that handles all the MYSQL traffic including setting up connections and error logging/reporting but could have been accomplished with a combination of SQLCONNECT() and SQLEXEC() functions. We are using the AFIELDS() VFP function to get an array of all the fields in the TABLE and the according properties.</p>
<p>Now all one needs to do is some code like</p>
<p><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;"><span style="color: #0000ff;">OPEN DATABASE </span><span style="color: #000000;">myvfpdb</span></span></span></p>
<p><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;"><span style="color: #000000;">ln_tcnt = </span><span style="color: #0000ff;">ADBOBJECTS</span><span style="color: #000000;">(la_t, &#8220;TABLE&#8221;) &amp;&amp; get a list of all the tables</span></span></span></p>
<p><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;"><span style="color: #0000ff;">FOR </span><span style="color: #000000;">x = 1 </span><span style="color: #0000ff;">TO </span><span style="color: #000000;">ln_tcnt &amp;&amp; loop through all the tables</span></span></span></p>
<p><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;"><span style="color: #0000ff;">    WAIT </span><span style="color: #000000;">&#8220;Creating &#8221; + la_t(x) </span><span style="color: #0000ff;">WINDOW NOWAIT</span></span></span></p>
<p><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;"><span style="color: #0000ff;">    IF !</span><span style="color: #000000;">createtable(&#8220;&lt;pathtodatadir&gt;\&#8221; + la_t(x), la_t(x))</span></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;">        *what ever error handling you want to do</span></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;">    ENDIF</span></span></span></p>
<p><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;">NEXT</span></span></span></p>
<p>So now that we got the structures across we can get to</p>
<p><span style="text-decoration: underline;"><strong>Transfer Indexes and Foreign Keys</strong></span></p>
<p>Unfortunatly in my case I did not come up with a way that was quicker by writing some code than to do this manually. VFP allows too many things in Indexes to drastically reduce the number that could be transferred by a program. Simpe VFP things like using UDF&#8217;s or VFP functions in indexes and also FOR expressions (filters) on indexes. A quick look at the documentation showed that less than 20 % of my indexes could be transferred programatically. This also means that quite some code has to be changed so transferring the same Indexes used in VFP is not a good idea. Indexes are there to improve data retrieval so they need to be optimized to the particular engine.</p>
<p>VFP accomplishes referential integrity with triggers. And there again the two platforms are too different to just write a short procedure to accomplish this. And in this case MYSQL with INNODB has some features that VFP does not have. So it made more sense to do this manually and examine each case and set it up in the way most suitable for MYSQL.</p>
<p>Next: transferring the Data</p>
]]></content:encoded>
			<wfw:commentRss>http://pirringers.com/mysqlblog/?feed=rss2&amp;p=17</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>First Contact &#8211; VFP/MYSQL interaction</title>
		<link>http://pirringers.com/mysqlblog/?p=11</link>
		<comments>http://pirringers.com/mysqlblog/?p=11#comments</comments>
		<pubDate>Fri, 12 Feb 2010 14:05:36 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[VFP MYSQL interactions]]></category>
		<category><![CDATA[VFP to MYSQL Migrations]]></category>

		<guid isPermaLink="false">http://pirringers.com/mysqlblog/?p=11</guid>
		<description><![CDATA[How to connect to MYSQL, the ODBC connector, performance issues]]></description>
			<content:encoded><![CDATA[<p> </p>
<p>So now that the decision was made we had to start to get to work. To show the customer progress and to see what I was up against I downloaded MYSQL Community and set up 3 Servers.</p>
<ul>
<li>One on my Vista Machine, to be accessed by the customer to see the progress. So any part finished and tested will be pushed to that server.</li>
<li>A 2nd one on a WinXP machine in anothter location just to see on how the performance is over the net. After all we don&#8217;t want to find out it takes enough time to have a cup of coffe while waiting for the computer to perform a task</li>
<li>And A 3rd on my Windows 7 laptop.</li>
<li>And another one is planned on a Linux box at a later point.</li>
</ul>
<p>So some of you might ask why 3 different Windows platforms? The simple answere is &#8211; That is what is mostly out there. This is to replace VFP systems run by small to medium sized companies. They have a budget. Mostly if you can bring it in und 10,000 you got the sale. So it benefits if one can use existing equipment.</p>
<p>I am happy to report that all 3 installs went smoothly and w/o any problems.</p>
<p>Next came the task of installing and testing the ODBC driver.</p>
<p>On the Vistat 32 bit and XP machine &#8211; no problem at all. On my Windows 7 I initially was perplexed as after the installation of the 64 bit version I could not find it anywhere but a little digging through the MYSQL bug database produced :</p>
<p><span style="font-family: Courier New, monospace;">Explanation: This is not a bug.<br />
MyODBC 3.51 is 32bit application which, by default, goes into<br />
%SystemRoot%\sysWOW64 folder on 64bit windows. The DataSources(ODBC) link in<br />
control panel searches for 64bit drivers in %SystemRoot%\system32 folder (silly<br />
naming I know&#8230;).<br />
What you have to do is:<br />
  Locate odbcad32.exe in *%SystemRoot%\sysWOW64 folder* (DO NOT use the one in<br />
%SystemRoot%\system32 folder pointed to in control panel!)<br />
  Use this program instead of default 64bit version to access MyODBC<br />
  Start it and add MyODBC driver from the list of available drivers</span></p>
<p>Even though I was installing Version 5.1.6 the above applied to that to and following the above suggestions resolved the issue.</p>
<p>So now that we had a working connection between VFP and MYSQL it was time to do some performance tuning. After all we want things to go nice and snappy. There is awfully little to be found on VFP/MYSQL connections so I spent the better part of a day testing and experimenting. The best settings and procedures I came up with are.</p>
<ul>
<li>On the VFP side use shared connections. It can take up to 1/2 second to set up a connection. Plus setting up individual connections will unecessarily increase the connection count on the server. For large record sets (&gt;100 records) make that async. shared connections.To connect with vfp use:
<p><span style="color: #000000;"><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;">gn_conn = </span></span></span><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;">SQLCONNECT</span></span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;">(&#8220;MYSQLDSN&#8221;, .T.) &amp;&amp; Establish A shareable connection</span></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;">gn_con2 = SQLCONNECT(gn_conn) &amp;&amp; for each subsequent connection to share that connection</span></span></span></p>
<p><span style="font-family: Times New Roman, serif;"><span style="font-size: small;"><span style="color: #000000;">to make the connection async</span></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;">ll_success = </span></span></span><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;">SQLSETPROP</span></span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;">(ln_conn,&#8221;Asynchronous&#8221;,.t.)</span></span></span></p>
<p><span style="font-family: Times New Roman, serif;"><span style="font-size: small;"><span style="color: #000000;">And then to execute a SQL statement</span></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;">ll_success = </span></span></span><span style="color: #0000ff;"><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;">SQLEXEC</span></span></span><span style="color: #000000;"><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;">(gn_conn, lc_sql, lc_alias) </span></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;">* lc_sql holds the MYSQL SQL statement and lc_alias is the alias of the receiving cursor</span></span></span></p>
<p><span style="font-family: Times New Roman, serif;"><span style="font-size: small;"><span style="color: #000000;">Of course the gn_conn could becone gn_con2 etc depending which shared connection you use. The advantage of using a shared connection is that you can run multiple statements asynchronously at the same time. So lets assume we have 3 SQL statements that we need to run on form LOAD then shooting them off on 3 shared asynchronous connections will take about ½ second before returning control back to our program. We now can go on and continue with our code while the results from our SQL statements are loaded in the background. This prevents long wait times – especially in slow connections.</span></span></span></li>
</ul>
<p> </p>
<ul>
<li>On the ODBC connector use the following settings (accessible by clicking the Details button on the ODBC configuration screen):<span style="font-family: Courier New, monospace;">Allow Big Result Sets = on</span>
<p><span style="font-family: Courier New, monospace;">Use Compression = on (will drastically improve availability of data on large result sets</span></p>
<p><span style="font-family: Courier New, monospace;">Disable Driver Provided cursor support = on (performance increase don&#8217;t know why)</span></p>
<p><span style="font-family: Courier New, monospace;">Force use of forward only cursors = on (performance increase)</span></p>
<p><span style="font-family: Courier New, monospace;">Allow multiple statements = on (performance)</span></p>
<p><span style="font-family: Times New Roman, serif;">There is another setting that drastically improves performance but it comes currently at a steep price.</span></p>
<p><span style="font-family: Courier New, monospace;">Don&#8217;t cache results of forward only cursors = on</span></p>
<p><span style="font-family: Times New Roman, serif;">If you use this option you cannot use asynchronous and shared connections as you will receive an error from the ODBC driver that states: “</span><span style="color: #000000;"><span style="font-family: Courier New, monospace;"><span style="font-size: x-small;">&#8220;You cannot execute that command now &#8211; commands out of sync SELECT &#8230;&#8230;&#8221; o</span></span></span><span style="color: #000000;"><span style="font-family: Times New Roman, serif;"><span style="font-size: small;">r something similar. I have seen some bug reports filed on this and am hopeful that this gets resolved at some future time. </span></span></span></li>
</ul>
<p><span style="font-family: Times New Roman; font-size: small;">Next: Transfering the Database</span></p>
]]></content:encoded>
			<wfw:commentRss>http://pirringers.com/mysqlblog/?feed=rss2&amp;p=11</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
