Showing posts with label MySQL. Show all posts
Showing posts with label MySQL. Show all posts

Wednesday, 19 September 2012

Selecting duplicates in a database table

Selecting duplicates in a database table can be accomplished in various ways and all these approaches involve various trade-offs with respect to efficiency and speed but before we start examining some of these approaches we need a table with duplicates so here is one. The Primary Key in this table is the ID and the Emp_Id is meant to be Unique and will be used for filtering duplicates.


Listing the number of times each record occurs with duplicate rows listed once.

(1) select *, count(*) from employee group by Emp_Id;



Listing the number of times each record occurs with a listing of the first row in the group.

(2) select *, count(*) from employee group by Emp_Id having count(*) > 1;



To list all duplicate rows, the following query would work;


(3) select   a.* from
    employee as a
inner join
    (select Emp_Id
     from employee
     group by Emp_Id
     having count(*) = 2) as b
  on a.Emp_Id = b.Emp_Id;

Now our table has a Tele_No column and some of these numbers are incorrect

Selecting the duplicates using a self-join.

In this case, we need to select all records that have the same Emp_Id but a different Tele_No. We can use a simple self-join with the Tele_No and the column as the distinguishing key to separate our records.
Option 1

(4) select e1.* from employee e1, employee e2 where e1.Emp_Id = e2.Emp_Id and e1.Tele_No != e2.Tele_No order by e1.Id asc;




The exact same result could also be obtained with a sub-query as follows:
Option 2

(5) select e1.* from
employee e1 where e1.Emp_Id IN (
select e2.Emp_Id from employee e2
where e1.Emp_Id = e2.Emp_Id
and e1.Tele_No != e2.Tele_No
) 
order by e1.Id asc;

In Option 1 & 2, we have the condition e1.Tele_No != e2.Tele_No that prevents us from getting a cartesian product of the records. If such a condition is not available then just use the Primary Key column as follows:


(6) select e1.* from
employee e1 where e1.Emp_Id IN (
select e2.Emp_Id from employee e2
where e1.Emp_Id = e2.Emp_Id
and e1.ID <> e2.ID
)
order by e1.Id asc;


If you are wondering whether a join or a sub-query would work best, it would ofcourse depend upon your query optimizer and the size of your tables but generally Joins are the way to go.

So, we have 6 records where the Emp_Id are the same but the Tele_Nos are different. To view these records in a tabular format we could modify (6) as follows.

(7) select e1.ID, e1.Tele_No,e2.ID, e2.Tele_no
from
employee e1, employee e2
         where e1.Emp_Id = e2.Emp_Id
               and e1.Tele_No != e2.Tele_No
               and e1.Id < e2.id
        order by e1.Id asc;



The change in the condition comparing the Primary Keys in the query above e1.Id < e2.id prevents a cartesian product.

Monday, 20 October 2008

Software caused connection abort: recv failed with MySQL and Hibernate

If you get "Software caused connection abort: recv failed" after attempting to login to your JBOSS server after a long period of idle time then you need to take a cup of coffee and sit down as this is going to take a while to fix.

In a web application that we developed,  we were using a JBOSS server version 4.04, connecting to MYSQL 5.0.24 with Hibernate 3.1 and we started getting these messages after the server had been sitting idle for some time and a login was attempted. The initial diagnosis was that the MySQL /JDBC connection was getting stale and it could be resolved by updating your MySQL driver to the latest version and adding a few properties to your data-source connection file. We updated the driver to mysql-connector-java-5.0.8 and as explained in this excellent post we added the following lines :
       
 (exception-sorter-class-name)
com.mysql.jdbc.integration.jboss.ExtendedMysqlExceptionSorter
(/exception-sorter-class-name)
(valid-connection-checker-class-name)
com.mysql.jdbc.integration.jboss.MysqlValidConnectionChecker
(/valid-connection-checker-class-name)        
The basic idea was that the JNDI would ping the DB periodically and keep the data source alive. Well, it didn't work for us but atleast we had the latest driver.

The next approach was to write a custom class that would operate on the database every hour or so and keep the connection fresh. (According to the docs, MySQL marked the connection stale after 8 hours but I was not in a trusting mood). I wrote a Java Timer class that was called by a servlet every 30 minutes. The objective of the class was to check the table which housed our support requests and if found a new request, it would shoot me an email. The Timer class and the Servlet hookup worked fine and I stated getting emails if there was a support request waiting to be serviced but the original problem still remained. The server would still spit out a long stream of exceptions, starting with Software caused connection abort: recv failed if a login was attempted after a few hours of idle time.This was getting annoying!

I then approached the problem in a different way and went over my Hibernate connection pooling setup. We were using C3PO but maybe something was missing? I reset the Hibernate connection pooling parameters to the following :
(property name="connection.provider_class")org.hibernate.connection.C3P0ConnectionProvider(/property)
(property name="c3p0.acquire_increment")1(/property)
(property name="c3p0.idle_test_period")100(/property) (!-- seconds --)
(property name="c3p0.max_size")100(/property)
(property name="c3p0.max_statements")0(/property)
(property name="c3p0.min_size")10(/property)
(property name="c3p0.timeout")100(/property) (!-- seconds --)

and Voila, the exceptions disappeared. We finally had a clean console when attempting login ever after several hours. So while, the Hibernate connection pooling params appear to be the main culprit, I feel that its equally important to update your MySQL driver and make sure that there are no loose ends in the JNDI data-source params.

Thursday, 11 October 2007

Configuring Middlgen to generate Hibernate files from MySQL

Following on from my earlier post , I'll now show you how to configure Middlegen and talk to your MySQL database. You will need to have ANT installed in order to run the ANT tasks that I customized to build the hbms and the Java objects.

I performed the Middlegen connection tasks using ANT version 1.7, MySQL version 5.0.24-community-nt and MySQL client version 5.1.11 and Middlegen 2.1

In your build.xml, set the following Middlegen property
<property name="Middlegen.home" value="${lib}/Middlegen"/>

The lib directory has 2 main jars, Middlegen-2.1.jar and Middlegen-hibernate-plugin-2.1.jar.

Both these jars are required to
(1) Run Middlegen and connect to MySQL
(2) Create Hbm mappings from the database and convert the hbm mappings into Java objects.

Firstly create a directory where you are going to store your generated files using the Middlegen-init ANT task.

<target name="Middlegen-init"
description="Initializes everything, creates directories, etc.">
<mkdir dir="${gen.java}" />
</target>


The next task is Middlegen which talks to the database but inorder to do so, it needs to know where the database is located, what driver file to use and what connection properties to use, so make the task know all this by defining the following properties in the build.xml

<property name="database.initialise.script" value="${main.resources}/database/ddl/FULL_DROP_INITIALISE.sql"/>
<property name="database.driver.file" value="${lib}/mysql-connector-java-3.0.14-production-bin.jar"/>
<property name="database.driver.classpath" value="${database.driver.file}"/>
<property name="database.driver" value="org.gjt.mm.mysql.Driver"/>
<property name="database.url" value="jdbc:mysql://localhost/testdatabase"/>
<property name="database.userid" value="root"/>
<property name="database.password" value="root"/>
<property name="database.schema" value="testdatabase"/>
<property name="database.catalog" value=""/>

You'll notice that the properties talk about a 'database.driver' which should be on the 'database.driver.classpath'.
This driver can be found within the mysql-connector-java-3.0.14-production-bin.jar so it should be downloaded and made available to the application.

Now, write the following ANT tasks in your build.xml

<!-- Middlegen related Tasks --->
<!-- =================================================================== -->
<!-- Run Middlegen -->
<!-- =================================================================== -->
<target
name="Middlegen"
description="Run Middlegen"
unless="Middlegen.skip"
depends="Middlegen-init"
>

<taskdef
name="Middlegen"
classname="Middlegen.MiddlegenTask"
classpathref="lib.class.path"
/>

<Middlegen
appname="${name}"
prefsdir="${Middlegen.prefs}"
gui="${gui}"
databaseurl="${database.url}"
initialContextFactory="${java.naming.factory.initial}"
providerURL="${java.naming.provider.url}"
datasourceJNDIName="${datasource.jndi.name}"
driver="${database.driver}"
username="${database.userid}"
password="${database.password}"
schema="${database.schema}"
catalog="${database.catalog}"
includeViews="false"
>

<!-- Sets up the hibernate plug-in for Middlegen -->

<hibernate
destination="${gen.java}"
package="${name}.persistence"
genXDocletTags="true"
javaTypeMapper="Middlegen.plugins.hibernate.HibernateJavaTypeMapper"
/>
</Middlegen>

</target>

<!-- =================================================================== -->
<!-- Run hbm2java -->
<!-- =================================================================== -->
<target name="hbm2java" description="Generate .hbm and then .java from .hbm files.">
<taskdef
name="hbm2java"
classname="net.sf.hibernate.tool.hbm2java.Hbm2JavaTask"
classpathref="lib.class.path"
/>
<hbm2java output="${gen.java}">
<fileset dir="${gen.java}">
<include name="**/*.hbm.xml"/>
</fileset>
</hbm2java>
</target>
<!-- End of Middlegen related Tasks --->

To create the HBM mappings for ALL tables in your 'testdatabase' schema:
Run the 'Middlegen' task which will
(1) create a directory to store the generated files
(2) connect to your MySQL database based on the params supplied and generate the hbm mapping files.

You can customize the hbm files according to your requirements, now run the hbm2java mapper task on these files by invoking the target 'hbm2java'. The java files will be generated and stored in the same directory alongside their respective hbms.

Well, that's all there is to it.

Middlegen is a very useful tool as it takes away the pain of manually creating these files and helps you setup your Hibernate environment within a couple of hours.

For more info on the subject refer to the Middlegen homepage.

Friday, 14 September 2007

MiddleGen and Hibernate: Making life easy in the middle

If you have been working in the Hibernate or in the EJB space, chances are high that you have had to write intermediate files such as hbms, in the case of Hibernate, that bridge the gap between the database tables and the Java objects (read POJOs) that exist within the application and are responsible for transporting data around.

While writing the hbm files manually, is a good idea if you really want to learn about how they are structured and the different types of relationships that they can be define within the data model, it becomes a tedious task if your data architect is not sure of what he or she is doing and/or keeps changing the DDL regularly.

In an AGILE project scenario, where requirements are welcomed at late stages of the build process, having a tool that can generate the hbm files and the related Java files (which are essentially POJOs) can be a boon. This is where MiddleGen comes in.

While MiddleGen is basically (an open source) database driven tool for generating code from the database to the presentation layer (Struts), I found its confluence with Hibernate to be really useful. It took me basically half-an-hour to set up MiddleGen and configure the Ant Tasks that came with the sample set of examples (in the MiddleGen download), to connect to my MYSQL database and generate a set of hbm files and their corresponding Java counterparts. In my next post, I'll explain the sequence of steps that I followed in setting up MiddleGen to talk to my database and generating the hbms and the Java files.