<?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>润物无声 &#187; SQL</title>
	<atom:link href="http://blog.zhourunsheng.com/tag/sql/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.zhourunsheng.com</link>
	<description>天空一朵雨做的云</description>
	<lastBuildDate>Sat, 08 May 2021 05:17:21 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.41</generator>
	<item>
		<title>创建自定义的SQLite Loader</title>
		<link>http://blog.zhourunsheng.com/2013/08/%e5%88%9b%e5%bb%ba%e8%87%aa%e5%ae%9a%e4%b9%89%e7%9a%84sqlite-loader/</link>
		<comments>http://blog.zhourunsheng.com/2013/08/%e5%88%9b%e5%bb%ba%e8%87%aa%e5%ae%9a%e4%b9%89%e7%9a%84sqlite-loader/#comments</comments>
		<pubDate>Wed, 21 Aug 2013 02:30:52 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[移动开发]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQLite]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1794</guid>
		<description><![CDATA[<p>在开发Android的过程中，我们经常会用到SQLite进行资料的存储，本文给出了一种自定义Loader的方式 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/%e5%88%9b%e5%bb%ba%e8%87%aa%e5%ae%9a%e4%b9%89%e7%9a%84sqlite-loader/">创建自定义的SQLite Loader</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>在开发Android的过程中，我们经常会用到SQLite进行资料的存储，本文给出了一种自定义Loader的方式，直接访问SQLite的API进行交互。</p>
<p>Today we’ll discuss about custom loaders in Android. Loaders have been introduced from Android 3.0 but we can also use loaders in prior versions of Android starting from Android 1.6 with the help of Android compatibility library.</p>
<p>In this tutorial we won’t discuss the basics of loader which is vastly available in the Internet. A very good tutorial on loaders in Android can be found here . Here we’ll discuss about how to create a custom loader in Android which can read data from SQLite Database and return the read data as a collection of POJO.</p>
<p>Android provides a ‘CursorLoader’ class which can read data from content providers but to read data directly from the SQLite database we need our own custom loader. A very nice loader library is written for this which reads data from cursor and can be found here and I’ve taken the idea of the custom loader from these mentioned links. But our custom loader is capable to read data from the SQLite database without using any content provider and returns the data as collection of objects.<span id="more-1794"></span></p>
<p>Create a new Android project and remember to use the library ‘android-support-v4.jar’ and implement the custom loader as follows :</p>
<p>First we’ll create a generic data source class file ‘DataSource.java’ with common methods to perform CRUD operations on data. The code is as follows :</p>
<pre>package com.example.customloaderexample.db;
import java.util.List;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
public abstract class DataSource {
	protected SQLiteDatabase mDatabase;
	public DataSource(SQLiteDatabase database) {
		mDatabase = database;
	}

	public abstract boolean insert(T entity);
	public abstract boolean delete(T entity);
	public abstract boolean update(T entity);
	public abstract List read();
	public abstract List read(String selection, String[] selectionArgs, String groupBy, String having, String orderBy);
}</pre>
<p>Next we’ll create our model class file ‘Test.java’ with the following code. This class represents the data that we want to store and retrieve from the database.</p>
<pre>package com.example.customloaderexample.model;
public class Test {
	private int id;
	private String name;
	public Test() {} 
	public Test(String name) {
		this.name = name;
	} 
	public int getId() {
		return id;
	} 
	public void setId(int id) {
		this.id = id;
	} 
	public String getName() {
		return name;
	} 
	public void setName(String name) {
		this.name = name;
	} 
	@Override 
	public String toString() {
		return name;
	}
}</pre>
<p>Next we’ll create a subclass of ‘TestDataSource.java’ to specifically perform CRUD operations on ‘Test’ objects.</p>
<pre>package com.example.customloaderexample.db;
import java.util.ArrayList;
import java.util.List;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.example.customloaderexample.model.Test;
public class TestDataSource extends DataSource {
	public static final String TABLE_NAME = "test";
	public static final String COLUMN_ID = "_id";
	public static final String COLUMN_NAME = "name";
	// Database creation sql statement
	public static final String CREATE_COMMAND = "create table " + TABLE_NAME + "(" + COLUMN_ID + " integer primary key autoincrement, " + COLUMN_NAME + " text not null);";
	public TestDataSource(SQLiteDatabase database) {
		super(database);
		// TODO Auto-generated constructor stub
	}
	@Override
	public boolean insert(Test entity) {
		if (entity == null) {
			return false;
		}
		long result = mDatabase.insert(TABLE_NAME, null, generateContentValuesFromObject(entity));
		return result != -1;
	}
	@Override
	public boolean delete(Test entity) {
		if (entity == null) {
			return false;
		}
		int result = mDatabase.delete(TABLE_NAME, COLUMN_ID + " = " + entity.getId(), null);
		return result != 0;
	}
	@Override
	public boolean update(Test entity) {
		if (entity == null) {
			return false;
		}
		int result = mDatabase.update(TABLE_NAME, generateContentValuesFromObject(entity), COLUMN_ID + " = " + entity.getId(), null);
		return result != 0;
	}
	@Override
	public List read() {
		Cursor cursor = mDatabase.query(TABLE_NAME, getAllColumns(), null, null, null, null, null);
		List tests = new ArrayList();
		if (cursor != null &amp;&amp; cursor.moveToFirst()) {
			while (!cursor.isAfterLast()) {
				tests.add(generateObjectFromCursor(cursor));
				cursor.moveToNext();
			}
			cursor.close();
		}
		return tests;
	}
	@Override
	public List read(String selection, String[] selectionArgs, String groupBy, String having, String orderBy) {
		Cursor cursor = mDatabase.query(TABLE_NAME, getAllColumns(), selection, selectionArgs, groupBy, having, orderBy);
		List tests = new ArrayList();
		if (cursor != null &amp;&amp; cursor.moveToFirst()) {
			while (!cursor.isAfterLast()) {
				tests.add(generateObjectFromCursor(cursor));
				cursor.moveToNext();
			}
			cursor.close();
		}
		return tests;
	}
	public String[] getAllColumns() {
		return new String[] { COLUMN_ID, COLUMN_NAME };
	}
	public Test generateObjectFromCursor(Cursor cursor) {
		if (cursor == null) {
			return null;
		}
		Test test = new Test();
		test.setId(cursor.getInt(cursor.getColumnIndex(COLUMN_ID)));
		test.setName(cursor.getString(cursor.getColumnIndex(COLUMN_NAME)));
		return test;
	}
	public ContentValues generateContentValuesFromObject(Test entity) {
		if (entity == null) {
			return null;
		}
		ContentValues values = new ContentValues();
		values.put(COLUMN_NAME, entity.getName());
		return values;
	}
}</pre>
<p>Now we’ll create our SQLite database open helper ‘DbHelper.java’. This class extends the SQLiteOpenHelper which helps us to create the initial database and tables.</p>
<pre>package com.example.customloaderexample.db;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DbHelper extends SQLiteOpenHelper {
	private static final String DATABASE_NAME = "test.db";
	private static final int DATABASE_VERSION = 1;
	public DbHelper(Context context) {
		super(context, DATABASE_NAME, null, DATABASE_VERSION);
	}
	@Override
	public void onCreate(SQLiteDatabase database) {
		database.execSQL(TestDataSource.CREATE_COMMAND);
	}
	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		db.execSQL("DROP TABLE IF EXISTS " + TestDataSource.TABLE_NAME);
		onCreate(db);
	}
}</pre>
<p>Next we’ll create a generic AsyncTask subclass ‘ContentChangingTask.java’ to perform content changing in background. This class is the base class for changing contents. We’ll create subclasses of this base class to perform CRUD operation on data in background.</p>
<pre>package com.example.customloaderexample.loader;
import android.os.AsyncTask;
import android.support.v4.content.Loader;
public abstract class ContentChangingTask extends AsyncTask {
	private Loader loader=null;
	ContentChangingTask(Loader loader) {
		this.loader=loader;
	}
	@Override
	protected void onPostExecute(T3 param) {
		loader.onContentChanged();
	}
}</pre>
<p>Now we’ll create a generic base class for our custom loader ‘AbstractDataLoader.java’. This class is the base class of our custom loader.</p>
<pre>package com.example.customloaderexample.loader;
import java.util.List;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
public abstract class AbstractDataLoader&gt; extends AsyncTaskLoader {
	protected E mLastDataList = null;
	protected abstract E buildList();
	public AbstractDataLoader(Context context) {
		super(context);
	}
	/** * Runs on a worker thread, loading in our data. Delegates the real work to * concrete subclass' buildCursor() method. */
	@Override
	public E loadInBackground() {
		return buildList();
	}
	/** * Runs on the UI thread, routing the results from the background thread to * whatever is using the dataList. */ @Override
	public void deliverResult(E dataList) {
		if (isReset()) {
			// An async query came in while the loader is stopped
			emptyDataList(dataList);
			return;
		}
		E oldDataList = mLastDataList;
		mLastDataList = dataList;
		if (isStarted()) {
			super.deliverResult(dataList);
		}
		if (oldDataList != null &amp;&amp; oldDataList != dataList &amp;&amp; oldDataList.size() &gt; 0) {
			emptyDataList(oldDataList);
		}
	}
	/** * Starts an asynchronous load of the list data. When the result is ready * the callbacks will be called on the UI thread. If a previous load has * been completed and is still valid the result may be passed to the * callbacks immediately. * * Must be called from the UI thread. */
	@Override
	protected void onStartLoading() {
		if (mLastDataList != null) {
			deliverResult(mLastDataList);
		}
		if (takeContentChanged() || mLastDataList == null || mLastDataList.size() == 0) {
			forceLoad();
		}
	}
	/** * Must be called from the UI thread, triggered by a call to stopLoading(). */
	@Override
	protected void onStopLoading() {
		// Attempt to cancel the current load task if possible.
		cancelLoad();
	}
	/** * Must be called from the UI thread, triggered by a call to cancel(). Here, * we make sure our Cursor is closed, if it still exists and is not already * closed. */
	@Override
	public void onCanceled(E dataList) {
		if (dataList != null &amp;&amp; dataList.size() &gt; 0) {
			emptyDataList(dataList);
		}
	}
	/** * Must be called from the UI thread, triggered by a call to reset(). Here, * we make sure our Cursor is closed, if it still exists and is not already * closed. */
	@Override
	protected void onReset() {
		super.onReset();
		// Ensure the loader is stopped
		onStopLoading();
		if (mLastDataList != null &amp;&amp; mLastDataList.size() &gt; 0) {
			emptyDataList(mLastDataList);
		}
		mLastDataList = null;
	}
	protected void emptyDataList(E dataList) {
		if (dataList != null &amp;&amp; dataList.size() &gt; 0) {
			for (int i = 0; i &lt; dataList.size(); i++) {
				dataList.remove(i);
			}
		}
	}
}</pre>
<p>Now we’ll create the specific loader ‘SQLiteTestDataLoader.java’ for ‘Test’ object which is a subclass of the previous generic loader. This class is the custom loader for ‘Test’ objects. We’ll instantiate this class as our loader.</p>
<pre>package com.example.customloaderexample.loader;
import java.util.List;
import android.content.Context;
import com.example.customloaderexample.db.DataSource;
import com.example.customloaderexample.model.Test;
public class SQLiteTestDataLoader extends AbstractDataLoader {
	private DataSource mDataSource;
	private String mSelection;
	private String[] mSelectionArgs;
	private String mGroupBy;
	private String mHaving;
	private String mOrderBy;
	public SQLiteTestDataLoader(Context context, DataSource dataSource, String selection, String[] selectionArgs, String groupBy, String having, String orderBy) {
		super(context);
		mDataSource = dataSource;
		mSelection = selection;
		mSelectionArgs = selectionArgs;
		mGroupBy = groupBy;
		mHaving = having;
		mOrderBy = orderBy;
	}
	@Override
	protected List buildList() {
		List testList = mDataSource.read(mSelection, mSelectionArgs, mGroupBy, mHaving,	mOrderBy);
		return testList;
	}
	public void insert(Test entity) {
		new InsertTask(this).execute(entity);
	}
	public void update(Test entity) {
		new UpdateTask(this).execute(entity);
	}
	public void delete(Test entity) {
		new DeleteTask(this).execute(entity);
	}
	private class InsertTask extends ContentChangingTask {
		InsertTask(SQLiteTestDataLoader loader) {
			super(loader);
		}
		@Override
		protected Void doInBackground(Test... params) {
			mDataSource.insert(params[0]);
			return (null);
		}
	}
	private class UpdateTask extends ContentChangingTask {
		UpdateTask(SQLiteTestDataLoader loader) {
			super(loader);
		}
		@Override
		protected Void doInBackground(Test... params) {
			mDataSource.update(params[0]);
			return (null);
		}
	}
	private class DeleteTask extends ContentChangingTask {
		DeleteTask(SQLiteTestDataLoader loader) {
			super(loader);
		}
		@Override
		protected Void doInBackground(Test... params) {
			mDataSource.delete(params[0]);
			return (null);
		}
	}
}</pre>
<p>Next we’ll create the initial view xml ‘activity_main.xml’ for our launcher activity into the res/layout folder.</p>
<pre>&lt;FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"android:layout_height="match_parent"&gt;
&lt;fragmentclass="com.example.customloaderexample.CustomLoaderExampleListFragment" android:id="@+id/titles" android:layout_width="match_parent"android:layout_height="match_parent"/&gt;</pre>
<p>Now we’ll create the launcher activity to launch our project. This is a FragmentActivity which holds our ListFragment and performs initial record insertion into database.</p>
<pre>package com.example.customloaderexample;
import java.util.List;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import com.example.customloaderexample.db.DbHelper;
import com.example.customloaderexample.db.TestDataSource;
import com.example.customloaderexample.model.Test;
public class MainFragmentActivity extends FragmentActivity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		DbHelper helper = new DbHelper(this);
		SQLiteDatabase database = helper.getWritableDatabase();
		TestDataSource dataSource = new TestDataSource(database);
		List list = dataSource.read();
		if(list == null || list.size() == 0) {
			dataSource.insert(new Test("Samik"));
			dataSource.insert(new Test("Piyas"));
			dataSource.insert(new Test("Sujal"));
		}
		helper.close();
		database.close();
	}
}</pre>
<p>Don’t forget to maintain the entry of the launcher activity into the ‘AndroidManifest.xml’ along with proper intent filter (to make it the launcher activity).</p>
<p>Now we’ll create the ListFragment to retrieve the list of Test objects from the database and show using our custom loader.</p>
<pre>package com.example.customloaderexample;
import java.util.List;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.ArrayAdapter;
import com.example.customloaderexample.db.DbHelper;
import com.example.customloaderexample.db.TestDataSource;
import com.example.customloaderexample.loader.SQLiteTestDataLoader;
import com.example.customloaderexample.model.Test;
public class CustomLoaderExampleListFragment extends ListFragment implements LoaderManager.LoaderCallbacks {
	private ArrayAdapter mAdapter;
	// The Loader's id (this id is specific to the ListFragment's LoaderManager)
	private static final int LOADER_ID = 1;
	private static final boolean DEBUG = true;
	private static final String TAG = "CustomLoaderExampleListFragment";
	private SQLiteDatabase mDatabase;
	private TestDataSource mDataSource;
	private DbHelper mDbHelper;
	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onActivityCreated(savedInstanceState);
		//setHasOptionsMenu(true);
		mDbHelper = new DbHelper(getActivity());
		mDatabase = mDbHelper.getWritableDatabase();
		mDataSource = new TestDataSource(mDatabase);
		mAdapter = new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1);
		setEmptyText("No data, please add from menu.");
		setListAdapter(mAdapter);
		setListShown(false);
		if (DEBUG) {
			Log.i(TAG, "+++ Calling initLoader()! +++");
			if (getLoaderManager().getLoader(LOADER_ID) == null) {
				Log.i(TAG, "+++ Initializing the new Loader... +++");
			} else {
				Log.i(TAG, "+++ Reconnecting with existing Loader (id '1')... +++");
			}
		}
		// Initialize a Loader with id '1'. If the Loader with this id already // exists, then the LoaderManager will reuse the existing Loader.
		getLoaderManager().initLoader(LOADER_ID, null, this);
	}
	@Override
	public Loader onCreateLoader(int id, Bundle args) {
		SQLiteTestDataLoader loader = new SQLiteTestDataLoader(getActivity(), mDataSource, null, null, null, null, null);
		return loader;
	}
	@Override
	public void onLoadFinished(Loader loader, List data) {
		if (DEBUG) {
			Log.i(TAG, "+++ onLoadFinished() called! +++");
		}
		mAdapter.clear();
		for(Test test : data) {
			mAdapter.add(test);
		}
		if (isResumed()) {
			setListShown(true);
		} else {
			setListShownNoAnimation(true);
		}
	}
	@Override
	public void onLoaderReset(Loader arg0) {
		mAdapter.clear();
	}
	@Override
	public void onDestroy() {
		super.onDestroy();
		mDbHelper.close();
		mDatabase.close();
		mDataSource = null;
		mDbHelper = null;
		mDatabase = null;
	}
}</pre>
<p>Now run the project as Android application and you’ll get the following screen :<br />
<img alt="android custom loader" src="http://farm6.staticflickr.com/5449/9698741410_dfb33b0d91_o.png" width="180" height="300" /></p>
<h1>Acknowledgement :</h1>
<p><a href="http://www.vogella.com/articles/AndroidSQLite/article.html" target="_blank" rel="nofollow">http://www.vogella.com/articles/AndroidSQLite/article.html</a></p>
<p><a href="https://github.com/commonsguy/cwac-loaderex" target="_blank" rel="nofollow">https://github.com/commonsguy/cwac-loaderex</a></p>
<p><a href="http://www.androiddesignpatterns.com/2012/07/loaders-and-loadermanager-background.html" target="_blank" rel="nofollow">http://www.androiddesignpatterns.com/2012/07/loaders-and-loadermanager-background.html</a></p>
<p>文章节选：<a href="http://www.phloxblog.in/android-custom-loader-load-data-sqlite-database-android-version-1-6/?utm_source=Android+Weekly&amp;utm_campaign=aa8a9379f2-Android_Weekly_66&amp;utm_medium=email&amp;utm_term=0_4eb677ad19-aa8a9379f2-337263089#.UhQV5mX53ws">http://www.phloxblog.in/android-custom-loader-load-data-sqlite-database-android-version-1-6/</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/%e5%88%9b%e5%bb%ba%e8%87%aa%e5%ae%9a%e4%b9%89%e7%9a%84sqlite-loader/">创建自定义的SQLite Loader</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/08/%e5%88%9b%e5%bb%ba%e8%87%aa%e5%ae%9a%e4%b9%89%e7%9a%84sqlite-loader/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Java语言开发之SQL语句改善</title>
		<link>http://blog.zhourunsheng.com/2013/08/java%e8%af%ad%e8%a8%80%e5%bc%80%e5%8f%91%e4%b9%8bsql%e8%af%ad%e5%8f%a5%e6%94%b9%e5%96%84/</link>
		<comments>http://blog.zhourunsheng.com/2013/08/java%e8%af%ad%e8%a8%80%e5%bc%80%e5%8f%91%e4%b9%8bsql%e8%af%ad%e5%8f%a5%e6%94%b9%e5%96%84/#comments</comments>
		<pubDate>Mon, 05 Aug 2013 10:57:15 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1727</guid>
		<description><![CDATA[<p>在基于Java语言的开发过程中，DB相关的开发相信大家都接触过SQL语句，增删改查作为基本的操作，比较熟悉，那 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/java%e8%af%ad%e8%a8%80%e5%bc%80%e5%8f%91%e4%b9%8bsql%e8%af%ad%e5%8f%a5%e6%94%b9%e5%96%84/">Java语言开发之SQL语句改善</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>在基于Java语言的开发过程中，DB相关的开发相信大家都接触过SQL语句，增删改查作为基本的操作，比较熟悉，那么JOIN，UNION, MERGE, DISTINCT这些高级的操作使用过没有呢？使用的过程中有哪些误用呢？有没有可能改善现在的SQL语句来提高程序的性能。本文介绍了几种常见的SQL误用场合，有语法方面的，改善效率方面的，下次，自己再做开发的时候，想想当前的SQL处理语句有没有可以完善的情况。</p>
<p>Java developers mix object-oriented thinking with imperative thinking, depending on their levels of:</p>
<ul>
<li>Skill (anyone can code imperatively)</li>
<li>Dogma (some use the “Pattern-Pattern”, i.e. the pattern of applying patterns everywhere and giving them names)</li>
<li>Mood (true OO is more clumsy to write than imperative code. At first)</li>
</ul>
<p>But when Java developers write SQL, everything changes. SQL is a declarative language that has nothing to do with either object-oriented or imperative thinking. It is very easy to express a query in SQL. It is not so easy to express it optimally or correctly. Not only do developers need to re-think their programming paradigm, they also need to think in terms of set theory.</p>
<p>Here are common mistakes that a Java developer makes when writing SQL (in no particular order):<span id="more-1727"></span></p>
<h3>1. Forgetting about NULL</h3>
<p>Misunderstanding NULL is probably the biggest mistake a Java developer can make when writing SQL. This is also (but not exclusively) due to the fact that NULL is also called UNKNOWN. If it were only called UNKNOWN, it would be easier to understand. Another reason is that JDBC maps SQL NULL to Java null when fetching data or when binding variables. This may lead to thinking that NULL = NULL (SQL) would behave the same way as null == null (Java)</p>
<p>One of the crazier examples of misunderstanding NULL is when <a title="Row value expressions and the NULL predicate" href="http://blog.jooq.org/2012/12/24/row-value-expressions-and-the-null-predicate/">NULL predicates are used with row value expressions</a>.</p>
<p>Another, subtle problem appears when misunderstanding the meaning of<a title="NOT IN vs. NOT EXISTS vs. LEFT JOIN / IS NULL: MySQL" href="http://blog.jooq.org/2012/07/27/not-in-vs-not-exists-vs-left-join-is-null-mysql/">NULL in NOT IN anti-joins</a>.</p>
<p><strong>The Cure:</strong></p>
<p>Train yourself. There’s nothing but explicitly thinking about NULL, every time you write SQL:</p>
<ul>
<li>Is this predicate correct with respect to NULL?</li>
<li>Does NULL affect the result of this function?</li>
</ul>
<h3>2. Processing data in Java memory</h3>
<p>Few Java developers know SQL very well. The occasional JOIN, the odd UNION, fine. But window functions? Grouping sets? A lot of Java developers load SQL data into memory, transform the data into some appropriate collection type, execute nasty maths on that collection with verbose loop structures (at least, before <a href="http://blog.informatech.cr/2013/03/24/java-streams-preview-vs-net-linq/">Java 8′s Collection improvements</a>).</p>
<p>But some SQL databases support advanced (and SQL standard!) OLAP features that tend to perform a lot better and are much easier to write. A (non-standard) example is <a href="http://stackoverflow.com/a/12245888/521799">Oracle’s awesome MODEL clause</a>. Just let the database do the processing and fetch only the results into Java memory. Because after all some very smart guys have optimised these expensive products. So in fact, by moving OLAP to the database, you gain two things:</p>
<ul>
<li>Simplicity. It’s probably easier to write correctly in SQL than in Java</li>
<li>Performance. The database will probably be faster than your algorithm. And more importantly, you don’t have to transmit millions of records over the wire.</li>
</ul>
<p><strong>The Cure:</strong></p>
<p>Every time you implement a data-centric algorithm in Java, ask yourself: Is there a way to let the database perform that work for me?</p>
<h3>3. Using UNION instead of UNION ALL</h3>
<p>It’s a shame that UNION ALL needs an extra keyword compared to UNION. It would be much better if the SQL standard had been defined to support:</p>
<ul>
<li>UNION (allowing duplicates)</li>
<li>UNION DISTINCT (removing duplicates)</li>
</ul>
<p>Not only is the removal of duplicates rarely needed (or sometimes even wrong), it is also quite slow for large result sets with many columns, as the two subselects need to be ordered, and each tuple needs to be compared with its subsequent tuple.</p>
<p>Note that even if the SQL standard specifies INTERSECT ALL and EXCEPT ALL, hardly any database implements these less useful set operations.</p>
<p><strong>The Cure:</strong></p>
<p>Every time you write a UNION, think if you actually wanted to write UNION ALL.</p>
<h3>4. Using JDBC Paging to page large results</h3>
<p>Most databases support some way of paging ordered results through LIMIT .. OFFSET, TOP .. START AT, OFFSET .. FETCH clauses. In the absence of support for these clauses, there is still the possibility for <a href="http://stackoverflow.com/q/6033080/521799">ROWNUM (Oracle)</a>or <a href="http://stackoverflow.com/q/7073263/521799">ROW_NUMBER() OVER() filtering (DB2, SQL Server 2008 and less)</a>, which is much faster than paging in memory. This is specifically true for large offsets!</p>
<p><strong>The Cure:</strong></p>
<p>Just use those clauses, or a tool (such as <a href="http://www.jooq.org/">jOOQ</a>) that can simulate those clauses for you.</p>
<h3>5. Joining data in Java memory</h3>
<p>From early days of SQL, some developers still have an uneasy feeling when expressing JOINs in their SQL. There is an inherent fear of JOIN being slow. This can be true if a cost-based optimiser chooses to perform a nested loop, possibly loading complete tables into database memory, before creating a joined table source. But that happens rarely. With appropriate predicates, constraints and indexes, MERGE JOIN and HASH JOIN operations are extremely fast. It’s all about the correct metadata <a title="How schema meta data impacts Oracle query transformations" href="http://blog.jooq.org/2011/11/25/how-schema-meta-data-impacts-oracle-query-transformations/">(I cannot cite Tom Kyte often enough for this)</a>. Nonetheless, there are probably still quite a few Java developers who will load two tables from separate queries into maps and join them in Java memory in one way or another.</p>
<p><strong>The Cure:</strong></p>
<p>If you’re selecting from various tables in various steps, think again to see if you cannot express your query in a single statement.</p>
<h3>6. Using DISTINCT or UNION to remove duplicates from an accidental cartesian product</h3>
<p>With heavy joining, one can loose track of all the relations that are playing a role in a SQL statement. Specifically, if multi-column foreign key relationships are involved, it is possible to forget to add the relevant predicates in JOIN .. ON clauses. This might result in duplicate records, but maybe only in exceptional cases. Some developers may then choose to use DISTINCT to remove those duplicates again. This is wrong in three ways:</p>
<ul>
<li>It (may) solve the symptoms but not the problem. It may as well not solve the symptoms in edge-cases.</li>
<li>It is slow for large result sets with many columns. DISTINCT performs an ORDER BY operation to remove duplicates.</li>
<li>It is slow for large cartesian products, which will still load lots of data into memory</li>
</ul>
<p><strong>The Cure:</strong></p>
<p>As a rule of thumb, when you get unwanted duplicates, always review your JOIN predicates. There’s probably a subtle cartesian product in there somewhere.</p>
<h3>7. Not using the MERGE statement</h3>
<p>This isn’t really a mistake, but probably some lack of knowledge or some fear towards the <a title="Arcane magic with the SQL:2003 MERGE statement" href="http://blog.jooq.org/2011/11/29/arcane-magic-with-the-sql2003-merge-statement/">powerful MERGE statement</a>. Some databases know other forms of UPSERT statements, e.g. MySQL’s ON DUPLICATE KEY UPDATE clause. But MERGE is really so powerful, most importantly in databases that heavily extend the SQL standard, <a href="http://msdn.microsoft.com/de-de/library/bb510625.aspx">such as SQL Server</a>.</p>
<p><strong>The Cure:</strong></p>
<p>If you’re UPSERTING by chaining INSERT and UPDATE or by chaining SELECT .. FOR UPDATE and then INSERT or UPDATE, think again. Apart from risking race conditions, you might be able to express a simpler MERGE statement.</p>
<h3>8. Using aggregate functions instead of window functions</h3>
<p>Before the introduction of window functions, the only means to aggregate data in SQL was by using a GROUP BY clause along with aggregate functions in the projection. This works well in many cases, and if aggregation data needed to be enriched with regular data, the grouped query can be pushed down into a joined subquery.</p>
<p>But SQL:2003 defined window functions, which are implemented by many popular database vendors. Window functions can aggregate data on result sets that are not grouped. In fact, each window function supports its own, independent PARTITION BY clause, which is an awesome tool for reporting.</p>
<p>Using window functions will:</p>
<ul>
<li>Lead to more readable SQL (less dedicated GROUP BY clauses in subqueries)</li>
<li>Improve performance, as a RDBMS is likely to optimise window functions more easily</li>
</ul>
<p><strong>The Cure:</strong></p>
<p>When you write a GROUP BY clause in a subquery, think again if this cannot be done with a window function.</p>
<h3>9. Using in-memory sorting for sort indirections</h3>
<p>The SQL ORDER BY clause supports many types of expressions, including CASE statements, which can be very useful for sort indirections. You should probably never sort data in Java memory because you think that</p>
<ul>
<li>SQL sorting is too slow</li>
<li>SQL sorting cannot do it</li>
</ul>
<p><strong>The Cure:</strong></p>
<p>If you sort any SQL data in memory, think again if you cannot push sorting into your database. This goes along well with pushing paging into the database.</p>
<h3>10. Inserting lots of records one by one</h3>
<p>JDBC knows batching, and you should use it. Do not INSERT thousands of records one by one, re-creating a new PreparedStatement every time. If all of your records go to the same table, create a batch INSERT statement with a single SQL statement and multiple bind value sets. Depending on your database and database configuration, you may need to commit after a certain amount of inserted records, in order to keep the UNDO log slim.</p>
<p><strong>The Cure:</strong></p>
<p>Always batch-insert large sets of data.</p>
<h3>Some interesting books</h3>
<p>Some very interesting books on similar topics are</p>
<ul>
<li><a href="http://pragprog.com/book/bksqla/sql-antipatterns">SQL Antipatterns by Bill Karwin</a></li>
<li><a href="http://winand.at/book/sql-performance-explained">SQL Performance Explained by Markus Winand</a></li>
</ul>
<p>本文选自：<a href="http://blog.jooq.org/2013/07/30/10-common-mistakes-java-developers-make-when-writing-sql/">10-common-mistakes-java-developers-make-when-writing-sql</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/java%e8%af%ad%e8%a8%80%e5%bc%80%e5%8f%91%e4%b9%8bsql%e8%af%ad%e5%8f%a5%e6%94%b9%e5%96%84/">Java语言开发之SQL语句改善</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/08/java%e8%af%ad%e8%a8%80%e5%bc%80%e5%8f%91%e4%b9%8bsql%e8%af%ad%e5%8f%a5%e6%94%b9%e5%96%84/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
