Archive for the ‘Uncategorized’ Category

MYSQL Workbench 5.2.17 Beta

Wednesday, April 7th, 2010

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 – at least in my oppinion. These are not bugs per say – for those see the bug reporting system – but just things that make using WB not as much pleasure as it could be.

1.) Triggers: Why can’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

– Trigger DDL Statements
DELIMITER $$

USE `test`$$CREATE DEFINER=`admin`@`%` TRIGGER `beforeinsert`
BEFORE INSERT On table1
FOR EACH ROW
BEGIN
    SET new.field1= new.idtable1;
END$$CREATE DEFINER=`admin`@`%` TRIGGER `beforeunsert`
BEFORE UPDATE On table1
FOR EACH ROW
BEGIN
    SET new.field1= new.idtable1;
END$$

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.

If you execute a SHOW trigger command you get the triggers data  neatly seperated in columns like

TRIGGER EVENT STATEMENT ,,,,, etc

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.

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 – like for example a customer # or part # fieldd – 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 – either as part of the comment for that particular field – or waiting until MYSQL actually supports user defined Data Types.

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…..) 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 – but currently nothing more thant a notepad as it does not update the server.

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 ….

Again I would like to either – preferably – have this feature turned into a reliable solid tool – 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…..

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 – but I guess I can keep hopeing.

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

Planet MySQL, Voting and My Blog

Friday, March 19th, 2010

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.

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

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.

Why is a particular blog good? what was the good point(s)?

Whis is it bad. Is it inapropriate here, poorly written? Is the procedure outlined within that particular blog flawed? Is there a better way?

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.

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

Hoping to see some comments from here

Martin Pirringer

Optimizing differences

Friday, March 19th, 2010

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 “rushmore” 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.

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 … whatevever …. 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.

In the late 80’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

SELECT a.custno, ;
a.invno AS invno, ;
a.invdte AS invdte, ;
a.balance + NVL((SELECT SUM(f.apply + f.disc) ;
FROM “accounts receivables!cc_apply” f;
WHERE f.invno = a.invno AND TTOD(f.appdate) > ld_date), 0) ;
- NVL((SELECT SUM(f.apply) ;
FROM “accounts receivables!cc_apply” f;
WHERE f.ccati = a.invno AND TTOD(f.appdate) > ld_date), 0) AS balance, ;
a.paidamt – NVL((SELECT SUM(f.apply + f.disc) ;
FROM “accounts receivables!cc_apply” f;
WHERE f.invno = a.invno AND TTOD(f.appdate) > ld_date), 0) AS paidamt, ;
a.invamt – NVL((SELECT SUM(f.apply) ;
FROM “accounts receivables!cc_apply” f;
WHERE f.ccati = a.invno AND TTOD(f.appdate) > ld_date), 0) AS invamt ;
FROM “accounts receivables!invoice master” a, customer\customer cu, ;
(SELECT DISTINCT e.invno as invno FROM (SELECT d.invno as invno ;
FROM “accounts receivables!invoice master” d ;
WHERE d.invdte <= ld_date AND d.balance # 0 AND !d.arstat $ “Vv” ;
UNION SELECT d.invno as invno ;
FROM “accounts receivables!invoice master” d ,;
“accounts receivables!cc_apply” b ;
WHERE d.invno = b.invno AND d.invdte <= ld_date AND d.balance = 0 AND !d.arstat $ “Vv” ; AND TTOD(b.appdate) > ld_date ;
AND (b.apply # 0 OR b.disc # 0) ;
UNION SELECT d.invno as invno ;
FROM “accounts receivables!invoice master” d ,;
“accounts receivables!cc_apply” b ;
WHERE d.invno = b.ccati AND d.invdte <= ld_date AND d.balance = 0 AND !d.arstat $ “Vv” ; AND TTOD(b.appdate) > ld_date ;
AND (b.apply # 0 OR b.disc # 0)) e) c ;
WHERE a.invno = c.invno AND a.custno = cu.custno AND !a.arstat $ “Vv” AND &lc_filter;
ORDER BY a.custno, a.invdte, a.invno ;
INTO CURSOR c_arorec NOFILTER READWRITE

&lc_filter was used to filter out some other parameters like sales rep, state, customer etc.

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.

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.

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

CREATE DEFINER=`whoever`@`%` PROCEDURE `arorec`(v_date DATE,
v_state varchar(2), v_salesmn varchar(2), v_custno varchar(6), v_sum TINYINT)
READS SQL DATA
DETERMINISTIC
BEGIN
SET @v_date = v_date;
SET @v_custno = v_custno;
SET @v_state = v_state;
SET @v_salesmn = v_salesmn;
SET @v_sum = v_sum; — summary or not
/*Note need to use tempres and tempcr as you cannot use temp table(tempsel) 2x in a union*/
DROP TABLE IF EXISTS tempsel;
DROP TABLE IF EXISTS tempres;
DROP TABLE IF EXISTS tempcr;

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

IF @v_custno IS null AND @v_state IS NULL AND @v_salesmn IS NULL THEN
/* GET EVERYONE*/
PREPARE stmt1 FROM ”
CREATE TEMPORARY TABLE tempsel ENGINE = MEMORY SELECT invno, Sum(apply) as apply, SUM(disc) AS disc
FROM cc_apply WHERE invdte <= ? AND appdate > ? GROUP BY invno” ;
EXECUTE stmt1 USING @v_date, @v_date;
ELSEIF @v_custno IS NOT NULL AND @v_salesmn IS NULL THEN
/* State is irrelevant as each customer has one address customere only*/
…….The selections for other criteria

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

PREPARE stmt1 FROM ”
CREATE TEMPORARY TABLE tempres ENGINE = MEMORY
SELECT ma.custno, ma.ticket AS refno, ma.dtepaid, ma.artype, ma.ponum AS checkno, 0000000000.00 AS future,
ma.invno, ma.balance + te.apply – te.disc AS BALANCE, ma.entered, ma.invdte, ma.invamt, ma.paidamt, getpd(@v_date, ma.invdte) AS gp
FROM `invoice master` ma INNER JOIN tempsel te ON ma.invno = te.invno
WHERE ma.invdte <= ? AND artype IN (‘ ‘, ‘I’, ‘C’, ‘W’)”;
EXECUTE stmt1 USING @v_date;
DEALLOCATE PREPARE stmt1;
ALTER TABLE tempres ADD INDEX custno(custno);
/* UNION of invoices still open without anything applied and futures*/

IF @v_custno IS null AND @v_state IS NULL AND @v_salesmn IS NULL THEN
Prepare stmt1 FROM ”
INSERT INTO tempres
SELECT ma.custno, ma.ticket AS refno, ma.dtepaid, ma.artype, ma.ponum AS checkno, 0000000000.00 AS future,
ma.invno, ma.balance, ma.entered, ma.invdte, ma.invamt, ma.paidamt, getpd(@v_date, ma.invdte) AS gp
FROM `invoice master` ma LEFt JOIN tempsel t2 ON ma.invno = t2.invno
WHERE t2.invno IS NULL and ma.open = 1 and ma.invdte <= ? AND artype IN (‘ ‘, ‘I’, ‘C’, ‘W’) 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) – 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 > ? AND ma.entered <= ? AND artype IN (‘ ‘, ‘I’, ‘C’, ‘W’)”;
EXECUTE stmt1 USING @v_date, @v_date, @v_date;
ELSEIF @v_custno IS NOT NULL AND @v_salesmn IS NULL THEN
……. The other statements for selection by salesmn, state, etc

We then do the same for the payments and end up with a temporary table “TEMPRES” that holds all the records needed for our aging.

In VFP we then used “SET RELATION TO” 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

SELECT te.*, cu.email, cu.statml, cu.statfmt, cu.company, cu.phone, cu.faxno, cu.contact, cu.address1,
cu.address2, cu.city, cu.state AS state, cu.zip, cu.pterms, cu.lastpay AS lastpay, cu.limit,
cu.lpymt, cu.ldate AS ldate, cu.lsale, cu.inactive AS inactive,
IF(te.gp=’C',te.invamt – te.paidamt,0.00) AS current,
IF(te.gp=’1′,te.invamt – te.paidamt,0.00) AS pd1,
IF(te.gp=’2′,te.invamt – te.paidamt,0.00) AS pd2,
IF(te.gp=’3′,te.invamt – te.paidamt,0.00) AS pd3,
IF(te.gp=’3′,te.invamt – te.paidamt,0.00) AS pd4,
IF(te.gp=’O',te.invamt – te.paidamt,0.00) AS pdover
FROM tempres te INNER JOIN customer cu ON te.custno = cu.custno
ORDER by te.custno ASC, te.invdte ASC, te.invno ASC;

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.

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