This lesson:

- learn query method in details
- use sorting, grouping, conditions, having

 

We use query to read all table data last lessons. We only used the table name as an input parameter and got all rows. But query has more parameters:

columns determines list of attributes we want to get;
selection is a string of WHERE condition;
selectionArgs is a array of arguments for selection. You can use ? marks within selection, which will be replaced with this values;
groupBy is for grouping;
having is for using conditions of aggregate functions;
orderBy is for sorting.

Now, let’s try to use them within an example. We will create a “country guide” application. We will take 10 countries and write to database their names, population and regions. The next functions we will implement in the application:
- displaying all rows;
- displaying the value of aggregate function (SUM, MIN, MAX, COUNT);
- displaying countries with more population than a number which was entered by user;
- grouping countries by region;
- displaying regions with more population than a number which was entered by user;
- sorting countries by name, population and region;

Data will be displayed in logs.

 

Let’s create a project:

 

Project name: P0361_SQLiteQuery
Build Target: Android 2.3.3
Application name: SQLiteQuery
Package name: ru.startandroid.develop.p0361sqlitequery
Create Activity: MainActivity

 

Let’s code main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Справочник стран"
        android:textSize="14sp"
        android:gravity="center_horizontal"
        android:layout_marginBottom="5dp"
        android:layout_marginTop="5dp">
    </TextView>
    <Button
        android:id="@+id/btnAll"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Все записи"
        android:layout_marginTop="5dp">
    </Button>
    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp">
        <Button
            android:id="@+id/btnFunc"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Функция">
        </Button>
        <EditText
            android:id="@+id/etFunc"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1">
            <requestFocus>
            </requestFocus>
        </EditText>
    </LinearLayout>
    <LinearLayout
        android:id="@+id/linearLayout2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp">
        <Button
            android:id="@+id/btnPeople"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Население">
        </Button>
        <EditText
            android:id="@+id/etPeople"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:inputType="number">
        </EditText>
    </LinearLayout>
    <Button
        android:id="@+id/btnGroup"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Население по региону"
        android:layout_marginTop="5dp">
    </Button>
    <LinearLayout
        android:id="@+id/linearLayout4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp">
        <Button
            android:id="@+id/btnHaving"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Население по региону >">
        </Button>
        <EditText
            android:id="@+id/etRegionPeople"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:inputType="number">
        </EditText>
    </LinearLayout>
    <LinearLayout
        android:id="@+id/linearLayout3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp">
        <Button
            android:id="@+id/btnSort"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Сортировка">
        </Button>
        <RadioGroup
            android:id="@+id/rgSort"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
            <RadioButton
                android:id="@+id/rName"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:checked="true"
                android:text="Наименование">
            </RadioButton>
            <RadioButton
                android:id="@+id/rPeople"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Население">
            </RadioButton>
            <RadioButton
                android:id="@+id/rRegion"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Регион">
            </RadioButton>
        </RadioGroup>
    </LinearLayout>
</LinearLayout>

Here are 6 buttons for 6 functions we are going to implement and editText fields for data entering where it needs. RadioGroup will be used for sorting.

 

MainActivity.java code:

package ru.startandroid.develop.p0361sqlitequery;

import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioGroup;

public class MainActivity extends Activity implements OnClickListener {

  final String LOG_TAG = "myLogs";

  String name[] = { "China", "USA", "Brazil", "Russia", "Japan",
      "Germany", "Egypt", "Italy", "France", "Canada" };
  int people[] = { 1400, 311, 195, 142, 128, 82, 80, 60, 66, 35 };
  String region[] = { "Asia", "America", "America", "Europa", "Asia",
      "Europe", "Africa", "Europe", "Europe", "America" };

  Button btnAll, btnFunc, btnPeople, btnSort, btnGroup, btnHaving;
  EditText etFunc, etPeople, etRegionPeople;
  RadioGroup rgSort;

  DBHelper dbHelper;
  SQLiteDatabase db;

  /** Called when the activity is first created. */

  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    btnAll = (Button) findViewById(R.id.btnAll);
    btnAll.setOnClickListener(this);

    btnFunc = (Button) findViewById(R.id.btnFunc);
    btnFunc.setOnClickListener(this);

    btnPeople = (Button) findViewById(R.id.btnPeople);
    btnPeople.setOnClickListener(this);

    btnSort = (Button) findViewById(R.id.btnSort);
    btnSort.setOnClickListener(this);

    btnGroup = (Button) findViewById(R.id.btnGroup);
    btnGroup.setOnClickListener(this);

    btnHaving = (Button) findViewById(R.id.btnHaving);
    btnHaving.setOnClickListener(this);

    etFunc = (EditText) findViewById(R.id.etFunc);
    etPeople = (EditText) findViewById(R.id.etPeople);
    etRegionPeople = (EditText) findViewById(R.id.etRegionPeople);

    rgSort = (RadioGroup) findViewById(R.id.rgSort);

    dbHelper = new DBHelper(this);
    // connect to database
    db = dbHelper.getWritableDatabase();

    // check if the table is empty
    Cursor c = db.query("mytable", null, null, null, null, null, null);
    if (c.getCount() == 0) {
      ContentValues cv = new ContentValues();
      // filling the table
      for (int i = 0; i < 10; i++) {
        cv.put("name", name[i]);
        cv.put("people", people[i]);
        cv.put("region", region[i]);
        Log.d(LOG_TAG, "id = " + db.insert("mytable", null, cv));
      }
    }
    c.close();
    dbHelper.close();
    // emulate btnAll click
    onClick(btnAll);

  }

  public void onClick(View v) {

    // connect to database
    db = dbHelper.getWritableDatabase();

    // data from the screen
    String sFunc = etFunc.getText().toString();
    String sPeople = etPeople.getText().toString();
    String sRegionPeople = etRegionPeople.getText().toString();

    // arguments for query
    String[] columns = null;
    String selection = null;
    String[] selectionArgs = null;
    String groupBy = null;
    String having = null;
    String orderBy = null;

    // cursor
    Cursor c = null;

    // determine which button was clicked
    switch (v.getId()) {
    // All data
    case R.id.btnAll:
      Log.d(LOG_TAG, "--- Все записи ---");
      c = db.query("mytable", null, null, null, null, null, null);
      break;
    // Function
    case R.id.btnFunc:
      Log.d(LOG_TAG, "--- Function " + sFunc + " ---");
      columns = new String[] { sFunc };
      c = db.query("mytable", columns, null, null, null, null, null);
      break;
    // Population more than
    case R.id.btnPeople:
      Log.d(LOG_TAG, "--- Population more than " + sPeople + " ---");
      selection = "people > ?";
      selectionArgs = new String[] { sPeople };
      c = db.query("mytable", null, selection, selectionArgs, null, null,
          null);
      break;
    // Population by region
    case R.id.btnGroup:
      Log.d(LOG_TAG, "--- Population by region ---");
      columns = new String[] { "region", "sum(people) as people" };
      groupBy = "region";
      c = db.query("mytable", columns, null, null, groupBy, null, null);
      break;
    // Regions with population more than
    case R.id.btnHaving:
      Log.d(LOG_TAG, "--- Regions with population more than " + sRegionPeople
          + " ---");
      columns = new String[] { "region", "sum(people) as people" };
      groupBy = "region";
      having = "sum(people) > " + sRegionPeople;
      c = db.query("mytable", columns, null, null, groupBy, having, null);
      break;
    // Sorting
    case R.id.btnSort:
      // sorting by
      switch (rgSort.getCheckedRadioButtonId()) {
      // name
      case R.id.rName:
        Log.d(LOG_TAG, "--- Sorting by name ---");
        orderBy = "name";
        break;
      // population
      case R.id.rPeople:
        Log.d(LOG_TAG, "--- Sorting by population ---");
        orderBy = "people";
        break;
      // region
      case R.id.rRegion:
        Log.d(LOG_TAG, "--- Sorting by region ---");
        orderBy = "region";
        break;
      }
      c = db.query("mytable", null, null, null, null, null, orderBy);
      break;
    }

    if (c != null) {
      if (c.moveToFirst()) {
        String str;
        do {
          str = "";
          for (String cn : c.getColumnNames()) {
            str = str.concat(cn + " = "
                + c.getString(c.getColumnIndex(cn)) + "; ");
          }
          Log.d(LOG_TAG, str);

        } while (c.moveToNext());
      }
      c.close();
    } else
      Log.d(LOG_TAG, "Cursor is null");

    dbHelper.close();
  }

  class DBHelper extends SQLiteOpenHelper {

    public DBHelper(Context context) {
      // superclass constructor
      super(context, "myDB", null, 1);
    }

    public void onCreate(SQLiteDatabase db) {
      Log.d(LOG_TAG, "--- onCreate database ---");
      // create table with columns
      db.execSQL("create table mytable ("
          + "id integer primary key autoincrement," + "name text,"
          + "people integer," + "region text" + ");");
    }

    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
  }

}

 

I didn’t include any validation checks for entered values for not to make the code heavy. It’s already rather big, but there is nothing difficult with it.

Here we have three arrays name, people and region which determines countrie’s names, population and regions they are located in. We will fill the table with this data.

We determine views within onCreate method, set listeners, create dbHelper object for database management, connect to database and get db object for interaction with it. Then we check if the table is empty and fill it with data, close connection and programmatically click “All data” button to display all the list in logs.

Within onClick method we connect to database, wright values from editText fields to variables, determine Cursor object, variables for query method and check which button was clicked.

btnAll is for all data displaying. Here we call query method with table name for the first parameter and null for others. We’ve already done it the previous lesson.

btnFunc is for displaying aggregate function value (or any other attribute). Here we use columns parameter, which must be filled with attributes we want to get from the table which are usually written after SELECT tag in SQL request. columns is a String array, so we initialize it as an array including one string was read from etFunc field and launch query.

btnPeople is for displaying countries with population bigger than the entered value. We use select to determine conditions and use ? mark as an argument which value we get from selectionArgs (it is sPeople variable initialized with etPeople field content). Launch query.

btnGroup is for countries grouping and displaying their total population. We use columns to determine types of attributes we want to get, they are region and total population. Within groupBy we set grouping by region and launch query.

btnHaving is for displaying regions with population bigger than the entered value. This case is similar to grouping, but one more condition is added with having parameter - region population summ must be lower than sRegionPeople (it’s etRegionPeople field content value).

btnSort is for country sorting. Here we check which RadioButton is selected and specify data sort attribute within orderBy accordingly. Launch query.

In the cases described earlier we launched query and got Cursor object. Now we check if it is not null and has rows (moveToFirst). If it’s alright, we launch data search in do .. while cycle (c.moveToNext()). For each row we get columns names (getColumnNames) and their values calling getString with column number as an argument. We initialize str with chain of column names and their values and display str in logs. After that we close the connection.

At the end of the code there is a description of the DBHelper class, nothing has changed here since the previous lesson, except columns names for table creation.  

 

Let’s save and launch the application.

All rows were displayed in logs, as if we click “All data” button.

--- All data ---
id = 1; name = China; people = 1400; region = Asia;
id = 2; name = USA; people = 311; region = America;
id = 3; name = Brazil; people = 195; region = America;
id = 4; name = Russia; people = 142; region = Europe;
id = 5; name = Japan; people = 128; region = Asia;
id = 6; name = Germany; people = 82; region = Europe;
id = 7; name = Egypt; people = 80; region = Africa;
id = 8; name = Italy; people = 60; region = Europe;
id = 9; name = France; people = 66; region = Europe;
id = 10; name = Canada; people = 35; region = America;

I.e the table is filled with data and we can interact with it.

Let’s try to use aggregate function. We will get rows’ count for example.

Enter the value:

click “Function” button and look in logs:

--- Function count(*) as Count ---
Count = 10;

It’s alright, there are 10 rows in the table

 

Now let’s show countries with population over 100 million. Enter 100 and click “Population” button.

 

Logs:

--- Population more than 100 ---
id = 1; name = China; people = 1400; region = Asia;
id = 2; name = USA; people = 311; region = America;
id = 3; name = Brazil; people = 195; region = America;
id = 4; name = Russia; people = 142; region = Europe;
id = 5; name = Japan; people = 128; region = Asia;

 

Now let’s group countries by region and display regions’ population. Click “Population by region” button.

Logs:

--- Population by region ---
region = Asia; people = 1528;
region = America; people = 541;
region = Africa; people = 80;
region = Europe; people = 350;

 

Now let’s only display regions with population over 500 million. Enter 500 and click “Population by region” button.

 

Logs:

--- Regions with population more than 500 ---
region = Asia; people = 1528;
region = America; people = 541;

 

 The sorting only remained. Let’s for example choose sort by population and click “Sort” button.

 

Лог:

--- Sorting by population ---
id = 10; name = Canada; people = 35; region = America;
id = 8; name = Italy; people = 60; region = Europe;
id = 9; name = France; people = 66; region = Europe;
id = 7; name = Egypt; people = 80; region = Africa;
id = 6; name = Germany; people = 82; region = Europe;
id = 5; name = Japan; people = 128; region = Asia;
id = 4; name = Russia; people = 142; region = Europe;
id = 3; name = Brazil; people = 195; region = America;
id = 2; name = USA; people = 311; region = America;
id = 1; name = China; people = 1400; region = Asia;

By default sorting is produced in an ascending order.

Everything works the way it should. Within these examples we used all basic parameters of the query method. In addition to the described parameters, the query method also has implementations with limit and distinct parameters. I did not show them here, but I'll tell you about them in words:

limit is a string parameter, which is used to be determined in [offset], rows format. I.e if “5” will be passed to query as a limit, the query will return only five first rows. If we pass “3.5”, query will return 5 rows, beginning from the fourth one (NOT THIRD).

distinct is a boolean parameter for duplicates removing. It may be true or false.

I hope that the query method, which at first seemed like a large group of parameters, became clear and simple.

 

The next lesson:
- read data from joined tables;
- use rawQuery;

 


Присоединяйтесь к нам в Telegram:

- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance 

- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня




Language