@daaaa1002

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Android Studio SQliteの日付比較や特定レコードの抜き出しについて

解決したいこと

Android Studioでクイズアプリを作成しており、SQliteで問題データを保存しています。
問題を間違えた際にその日付を文字列型として保存する処理までは作成できたのですが、
保存された日付と今日の日付を比較し、1日以上前のレコードだけを抜き出して配列に格納して画面に表示するという処理を実現したいです。

処理

ReviewActivity.javaの中でクイズ配列(quizArray)を作成し、QuizDatabase.java内の 'getRecordsOlderThanOneDay' メソッドを呼び出しています。メソッドから受け取ったCursorオブジェクトからデータを取り出し、クイズ配列に格納しています。
その後、クイズ配列内の問題文、正解、選択肢、解説を取り出して表示するという処理を行っています。
'getRecordsOlderThanOneDay' メソッドでは、今日から1日以上前の日付を文字列型の変数oneDayAgoに代入し、データベース内の日付とData型で比較、条件に合うレコードだけを戻り値Cursorとして渡しています。

発生している問題・エラー

保存されている日付が1日以上前の問題データだけを抜き出してクイズ配列に格納したいのですが、すべてのレコードが格納されてしまいます。

該当するソースコード

package com.example.testapp;

public class ReviewActivity extends AppCompatActivity implements View.OnClickListener {

    private TextView review_countLabel, questionLabel;
    private Button answerBtn1, answerBtn2, answerBtn3, answerBtn4;

    private String rightAnswer;
    private String kaisetu;
    private int rightAnswerCount; // 正解数
    private int quizCount = 1;
    static final private int QUIZ_COUNT = 3; // 問題の出題数

    ArrayList<ArrayList<String>> quizArray = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_review);

        review_countLabel = findViewById(R.id.review_countLabel);
        questionLabel = findViewById(R.id.questionLabel);
        answerBtn1 = findViewById(R.id.answerBtn1);
        answerBtn2 = findViewById(R.id.answerBtn2);
        answerBtn3 = findViewById(R.id.answerBtn3);
        answerBtn4 = findViewById(R.id.answerBtn4);

        answerBtn1.setOnClickListener(this);
        answerBtn2.setOnClickListener(this);
        answerBtn3.setOnClickListener(this);
        answerBtn4.setOnClickListener(this);

        QuizDatabase dbHelper = new QuizDatabase(this);
        SQLiteDatabase db = dbHelper.getWritableDatabase();

        try {
            // getRecordsOlderThanOneDay メソッドを呼び出して1日以上前のレコードを取得
            Cursor cursor2 = dbHelper.getRecordsOlderThanOneDay();

            // quizArrayを作成
            // tmpArrayでクイズを1問ずつ作成してquizArrayにセット
            // 文字列型のgetString()で取り出す
            while (cursor2.moveToNext()) {
                ArrayList<String> tmpArray = new ArrayList<>();
                tmpArray.add(cursor2.getString(0)); // 問題
                tmpArray.add(cursor2.getString(1)); // 正解
                tmpArray.add(cursor2.getString(2)); // 選択肢1
                tmpArray.add(cursor2.getString(3)); // 選択肢2
                tmpArray.add(cursor2.getString(4)); // 選択肢3
                tmpArray.add(cursor2.getString(6)); // 解説
                quizArray.add(tmpArray);
            }

            // Cursorを閉じる
            cursor2.close();
        } finally {
            // データベースを閉じる
            db.close();
        }

    }

    public void showNextQuiz() {
        // クイズカウントラベルを更新
        review_countLabel.setText(getString(R.string.review_countLabel, quizCount));

        // ランダムな数字を取得
        Random random = new Random();
        int randomNum = random.nextInt(quizArray.size());

        // randomNumを使って、quizArrayからクイズを一つ取り出す
        ArrayList<String> quiz = quizArray.get(randomNum);

        // 問題文を表示
        questionLabel.setText(quiz.get(0));

        // 正解と解説をrightAnswerにセット
        rightAnswer = quiz.get(1);
        kaisetu = quiz.get(5);

        // 正解と選択肢3つをシャッフル
        Collections.shuffle(quiz);

        // 解答ボタンに正解と選択肢3つを表示
        answerBtn1.setText(quiz.get(0));
        answerBtn2.setText(quiz.get(1));
        answerBtn3.setText(quiz.get(2));
        answerBtn4.setText(quiz.get(3));

        // このクイズをquizArrayから削除
        quizArray.remove(randomNum);
    }

    @Override
    public void onClick(View view) {

        // どの解答ボタンが押されたか
        Button answerBtn = findViewById(view.getId());
        String btnText = answerBtn.getText().toString();

        String alertTitle;
        if (btnText.equals(rightAnswer)) {
            alertTitle = "正解";
            rightAnswerCount++;
        } else {
            alertTitle = "不正解";
            //日付カラムを更新 QuizDatabaseからメソッドを呼び出す
            QuizDatabase dbHelper = new QuizDatabase(this);
            dbHelper.updatedaydateColumn(questionLabel.getText().toString());
        }

        //ダイアログオブジェクトを作成
        DialogFragment dialogFragment = new AnswerDialogFragment();

        //ダイアログに「正解・解説文」を渡す
        Bundle args = new Bundle();
        args.putString("alertTitle", alertTitle);    //putString(キー,値)
        args.putString("rightAnswer", rightAnswer);
        args.putString("btnText", btnText);
        args.putString("kaisetu", kaisetu);
        dialogFragment.setArguments(args);

        //ダイアログが閉じないようにする
        dialogFragment.setCancelable(false);

        //ダイアログの表示
        dialogFragment.show(getSupportFragmentManager(), "answer_dialog");
    }
}
package com.example.testapp;

public class QuizDatabase extends SQLiteOpenHelper {

    static final private String DBNAME = "testapp.sqlite";
    static final private int VERSION = 5;
    //データベースのバージョン番号(データベース書き換えのたびに更新)

    String[][] quizData = {
            //{"問題", "正解", "選択肢1", "選択肢2", "選択肢3", "カテゴリ", "解説", 間違えた日付}
            {"基礎理論 問題1", "1", "2", "3", "4", "1", "解説(仮)","2023-12-14"},
            {"基礎理論 問題2", "1", "2", "3", "4", "1", "解説(仮)","2023-12-14"},
            {"基礎理論 問題3", "1", "2", "3", "4", "1", "解説(仮)","2023-12-14"},
            {"基礎理論 問題4", "1", "2", "3", "4", "1", "解説(仮)","0"},
            {"基礎理論 問題5", "1", "2", "3", "4", "1", "解説(仮)","0"},
            {"コンピュータシステム 問題6", "1", "2", "3", "4", "2", "解説(仮)","0"},
            {"コンピュータシステム 問題7", "1", "2", "3", "4", "2", "解説(仮)","0"},
            {"コンピュータシステム 問題8", "1", "2", "3", "4", "2", "解説(仮)","0"},
            {"コンピュータシステム 問題9", "1", "2", "3", "4", "2", "解説(仮)","0"},
            {"コンピュータシステム 問題10", "1", "2", "3", "4", "2", "解説(仮)","0"},
    };

    public QuizDatabase(Context context) {
        super(context, DBNAME, null, VERSION);
    }


    @Override
    public void onCreate(SQLiteDatabase db) {
        //クイズデータのテーブルを作成
        db.execSQL("CREATE TABLE quiz (" +
                "question TEXT PRIMARY KEY, answer TEXT, choice1 TEXT," +
                "choice2 TEXT, choice3 TEXT, category TEXT, kaisetu TEXT, daydate TEXT)");

        //トランザクションの開始(クイズデータの登録)
        db.beginTransaction();
        try{
            SQLiteStatement sql = db.compileStatement(
                    "INSERT INTO quiz (question, answer, choice1, choice2, choice3, category, kaisetu, daydate)" +
                            "VALUES(?, ?, ?, ?, ?, ?, ?, ?)"
            );

            // クイズデータを1行ずつ追加する
            for (int i = 0; i < quizData.length; i++) {
                // Valueをセット
                // bindString(index, value)でプレースホルダー(?)に値を渡す
                sql.bindString(1, quizData[i][0]); // 問題
                sql.bindString(2, quizData[i][1]); // 正解
                sql.bindString(3, quizData[i][2]); // 選択肢1
                sql.bindString(4, quizData[i][3]); // 選択肢2
                sql.bindString(5, quizData[i][4]); // 選択肢3
                sql.bindString(6, quizData[i][5]); // カテゴリ
                sql.bindString(7, quizData[i][6]); // 解説
                sql.bindString(8, quizData[i][7]); // 日付

                sql.executeInsert();
            }

            //成功フラグ
            db.setTransactionSuccessful();
        }

        catch (SQLiteException e) {
            //失敗
            e.printStackTrace();
        }

        finally {
            //成功フラグあり→トランザクション終了、成功フラグなし→ロールバック
            db.endTransaction();
        }
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if(oldVersion < newVersion) { //新しいバージョンがあるか判定
            db.execSQL("DROP TABLE IF EXISTS quiz"); //今のquizテーブルをDROP
            onCreate(db); //再度quizテーブルを作成
        }
    }

    //今日の日付を指定のカラムに書き込むメソッド
    public void updatedaydateColumn(String question) {
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues values = new ContentValues();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String todayDate = sdf.format(Calendar.getInstance().getTime());

        values.put("daydate", todayDate);

        // 第一引数がテーブル名、第二引数が更新するデータ、第三引数がWHERE句
        db.update("quiz", values, "question=?", new String[]{question});
        db.close();
    }

    // 日付が1日以上前のレコードを取得するメソッド
    public Cursor getRecordsOlderThanOneDay() {
        SQLiteDatabase db = this.getReadableDatabase();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());

        // 現在の日付から1日前の日付を計算
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DAY_OF_MONTH, -1);
        String oneDayAgo = sdf.format(calendar.getTime());

        // クエリを実行して該当するレコードを取得
        Cursor cursor = db.query(
                "quiz",
                null, // 全てのカラムを取得
                "date(daydate) <= date(?)",
                new String[]{oneDayAgo},
                null,
                null,
                null
        );

        return cursor;
    }
}
0 likes

1Answer

  • SELECT * FROM quiz WHERE date(daydate) <= date('2023-12-16')のようなSQL文を直接実行して意図した結果が返るか確認
  • db.query("quiz",null,date(daydate) <= date('2023-12-16'),null,null.null,null);の結果を同様に確認
  • db.query("quiz",null,date(daydate) <= date(?),new String[]{"2023-12-16"},null.null,null);の結果を同様に確認
  • oneDayAgoに正しい値が入っているか確認

のように順を追って確かめて、どの段階でおかしくなるのかを特定してみてはどうでしょう。
こういう問題特定方法を身に着けておくと、この件だけでなく今後別のことでうまく動かなかった場合にも役立ちます。

2Like

Comments

  1. @daaaa1002

    Questioner

    回答ありがとうございます。

    原因はdaydateにとりあえず入れておいた"0"でした。考えてみれば当たり前のことなのに、'0' <= '2023-12-16'と判定されることに全然気づきませんでした。
    助言いただいた通り、SELECT文を実行してみて、もっと根本的な部分にミスがあったと気づけました。ありがとうございました。

Your answer might help someone💌