안드로이드 SQLite 데이터베이스란?
본격적인 예제에 들어가기 앞서, 안드로이드에서 SQLite 데이터베이스가 무엇인지 간단히 짚고 넘어가겠습니다. SQLite는 기기 내부의 텍스트 파일 형태로 데이터를 저장하는 오픈소스 SQL 데이터베이스입니다. 안드로이드에는 SQLite 데이터베이스 구현체가 기본으로 내장되어 있어 별도의 설정 없이 바로 사용할 수 있으며, 관계형 데이터베이스의 모든 핵심 기능을 지원합니다.
특히 SQLite는 JDBC나 ODBC처럼 별도의 연결(Connection)을 설정할 필요가 없다는 점이 큰 장점입니다. 데이터베이스 파일에 직접 접근하여 쿼리를 실행할 수 있기 때문에 모바일 환경에서 가볍고 빠른 데이터 처리가 가능합니다.
이번 글에서는 안드로이드 SQLite에서 DISTINCT(중복 제거)와 COUNT(개수 집계)를 함께 사용하여, 테이블에 저장된 이름(name) 값 중 중복을 제거한 고유 레코드의 개수를 조회하는 방법을 단계별로 살펴보겠습니다.
Step 1 — 새 프로젝트 생성
Android Studio를 열고 File → New Project 메뉴로 이동한 뒤, 프로젝트 생성에 필요한 모든 정보를 입력하여 새 프로젝트를 만듭니다.
Step 2 — 레이아웃 작성 (res/layout/activity_main.xml)
아래 코드를 activity_main.xml 파일에 추가합니다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<EditText
android:id="@+id/name"
android:layout_width="match_parent"
android:hint="Enter Name"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/salary"
android:layout_width="match_parent"
android:inputType="numberDecimal"
android:hint="Enter Salary"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="@+id/save"
android:text="Save"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/refresh"
android:text="Refresh"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/udate"
android:text="Update"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/Delete"
android:text="DeleteALL"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>
</LinearLayout>위 레이아웃은 두 개의 EditText(이름과 급여 입력용), 네 개의 버튼(Save, Refresh, Update, DeleteALL), 그리고 결과를 표시할 ListView로 구성되어 있습니다. 사용자가 Save 버튼을 누르면 입력한 데이터가 SQLite 데이터베이스에 저장되고, Refresh 버튼을 누르면 ListView에 저장된 데이터가 갱신되어 표시됩니다.
Step 3 — MainActivity 작성 (src/MainActivity.java)
다음 코드를 MainActivity.java 파일에 추가합니다.
package com.example.andy.myapplication;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
Button save, refresh;
EditText name, salary;
ArrayAdapter arrayAdapter;
private ListView listView;
@Override
protected void onCreate(Bundle readdInstanceState) {
super.onCreate(readdInstanceState);
setContentView(R.layout.activity_main);
final DatabaseHelper helper = new DatabaseHelper(this);
final ArrayList array_list = helper.getAllCotacts();
name = findViewById(R.id.name);
salary = findViewById(R.id.salary);
listView = findViewById(R.id.listView);
arrayAdapter = new ArrayAdapter(MainActivity.this, android.R.layout.simple_list_item_1, array_list);
listView.setAdapter(arrayAdapter);
findViewById(R.id.Delete).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (helper.delete()) {
Toast.makeText(MainActivity.this, "Deleted", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(MainActivity.this, "NOT Deleted", Toast.LENGTH_LONG).show();
}
}
});
findViewById(R.id.udate).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!name.getText().toString().isEmpty() && !salary.getText().toString().isEmpty()) {
if (helper.update(name.getText().toString(), salary.getText().toString())) {
Toast.makeText(MainActivity.this, "Updated", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(MainActivity.this, "NOT Updated",
Toast.LENGTH_LONG).show();
}
} else {
name.setError("Enter NAME");
salary.setError("Enter Salary");
}
}
});
findViewById(R.id.refresh).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
array_list.clear();
array_list.addAll(helper.getAllCotacts());
arrayAdapter.notifyDataSetChanged();
listView.invalidateViews();
listView.refreshDrawableState();
}
});
findViewById(R.id.save).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!name.getText().toString().isEmpty() && !salary.getText().toString().isEmpty()) {
if (helper.insert(name.getText().toString(), salary.getText().toString())) {
Toast.makeText(MainActivity.this, "Inserted", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(MainActivity.this, "NOT Inserted", Toast.LENGTH_LONG).show();
}
} else {
name.setError("Enter NAME");
salary.setError("Enter Salary");
}
}
});
}
}MainActivity에서는 각 버튼에 클릭 리스너를 등록하여 데이터 삽입(insert), 수정(update), 삭제(delete), 그리고 목록 갱신(refresh) 기능을 처리합니다. 입력값이 비어 있으면 setError()를 통해 사용자에게 값을 입력하도록 안내합니다.
Step 4 — DatabaseHelper 작성 (src/DatabaseHelper.java)
핵심인 데이터베이스 헬퍼 클래스입니다. 아래 코드를 DatabaseHelper.java 파일에 추가합니다.
package com.example.andy.myapplication;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
class DatabaseHelper extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "salaryDatabase9";
public static final String CONTACTS_TABLE_NAME = "SalaryDetails";
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, 2);
}
@Override
public void onCreate(SQLiteDatabase db) {
try {
db.execSQL(
"create table " + CONTACTS_TABLE_NAME + "(id INTEGER PRIMARY KEY, name text,salary float,datetime default current_timestamp)"
);
} catch (SQLiteException e) {
try {
throw new IOException(e);
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + CONTACTS_TABLE_NAME);
onCreate(db);
}
public boolean insert(String s, String s1) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put("name", s);
contentValues.put("salary", s1);
db.replace(CONTACTS_TABLE_NAME, null, contentValues);
return true;
}
public ArrayList getAllCotacts() {
SQLiteDatabase db = this.getReadableDatabase();
ArrayList<String> array_list = new ArrayList<String>();
Cursor res = db.rawQuery("select (count(distinct name)) as countRecords from " + CONTACTS_TABLE_NAME, null);
res.moveToFirst();
while (res.isAfterLast() == false) {
if ((res != null) && (res.getCount() > 0))
array_list.add(res.getString(res.getColumnIndex("countRecords")));
res.moveToNext();
}
return array_list;
}
public boolean update(String s, String s1) {
SQLiteDatabase db = this.getWritableDatabase();
db.execSQL("UPDATE " + CONTACTS_TABLE_NAME + " SET name = " + "'" + s + "', " + "salary = " + "'" + s1 + "'");
return true;
}
public boolean delete() {
SQLiteDatabase db = this.getWritableDatabase();
db.execSQL("DELETE from " + CONTACTS_TABLE_NAME);
return true;
}
}핵심 포인트 — DISTINCT와 COUNT 조합 쿼리
이 예제의 핵심은 getAllCotacts() 메서드 안에 있는 다음 쿼리입니다.
select (count(distinct name)) as countRecords from SalaryDetails
count(distinct name)는 name 컬럼에서 중복된 값을 제거한 고유한 값만 대상으로 개수를 집계합니다. 즉, 동일한 이름이 여러 번 저장되어 있더라도 한 번만 계산되므로, 실질적으로 몇 명의 서로 다른 인물이 등록되어 있는지를 정확히 파악할 수 있습니다. 결과는 countRecords라는 별칭(alias)으로 반환되며, Cursor를 통해 읽어 ArrayList에 담아 ListView에 표시하게 됩니다.
애플리케이션 실행 및 결과 확인
이제 애플리케이션을 실행해 보겠습니다. 실제 안드로이드 스마트폰을 컴퓨터에 연결했다고 가정하고 진행합니다. Android Studio에서 프로젝트의 액티비티 파일 중 하나를 연 뒤, 툴바의 Run 아이콘을 클릭하세요. 실행 기기 선택 창에서 자신의 모바일 기기를 선택하면, 기기 화면에 아래와 같은 기본 화면이 나타납니다.

위 실행 결과에서 ListView에는 숫자 2가 표시되어 있습니다. 이는 테이블에 저장된 레코드 중 이름(name)의 중복을 제거했을 때 고유한 레코드가 총 2개라는 의미입니다. 만약 같은 이름으로 여러 건의 급여 데이터가 저장되어 있다면, 전체 레코드 수보다 적은 값이 출력되는 것을 확인할 수 있습니다.
이처럼 SQLite의 DISTINCT와 COUNT 함수를 조합하면 중복 데이터를 손쉽게 걸러내고 통계 정보를 얻을 수 있습니다. GROUP BY 절과 함께 활용하면 이름별 개수 집계 등 더욱 다양한 분석 쿼리로 확장할 수도 있으니 참고해 보시기 바랍니다.