SQLite की परफ़ॉर्मेंस के लिए सबसे सही तरीके

Android, SQLite के लिए बिल्ट-इन सपोर्ट की सुविधा देता है, के हिसाब से काम करते हैं. अपने ऐप्लिकेशन की परफ़ॉर्मेंस को ऑप्टिमाइज़ करने के लिए, ये सबसे सही तरीके अपनाएं. इससे, डेटा बढ़ने के साथ-साथ ऐप्लिकेशन की परफ़ॉर्मेंस भी तेज़ बनी रहेगी. इन सबसे सही तरीकों का इस्तेमाल करके, जिनसे परफ़ॉर्मेंस में आने वाली ऐसी समस्याएं हों जिन्हें ठीक करना मुश्किल हो और समस्या हल करने के लिए किया जा सकता है.

बेहतर परफ़ॉर्मेंस पाने के लिए, परफ़ॉर्मेंस से जुड़ी इन नीतियों का पालन करें:

  • कम पंक्तियां और कॉलम पढ़ें: अपनी क्वेरी को इस तरह ऑप्टिमाइज़ करें कि वह सिर्फ़ पेज के ज़रूरी डेटा. डेटाबेस से पढ़े जाने वाले डेटा की संख्या कम करें, क्योंकि ज़्यादा डेटा वापस पाने से परफ़ॉर्मेंस पर असर पड़ सकता है.

  • SQLite इंजन पर काम करें: कंप्यूटेशन (हिसाब लगाना), फ़िल्टर करना, और क्रम से लगाना कार्रवाइयों के लिए इस्तेमाल किया जाता है. SQLite के क्वेरी इंजन का इस्तेमाल करने से परफ़ॉर्मेंस बेहतर बनाने के लिए किया जा सकता है.

  • डेटाबेस स्कीमा में बदलाव करना: SQLite में मदद करने के लिए, अपना डेटाबेस स्कीमा डिज़ाइन करना और डेटा निरूपण बना सकते हैं. सही तरीके से इंडेक्स टेबल और टेबल स्ट्रक्चर को ऑप्टिमाइज़ करके परफ़ॉर्मेंस को बेहतर बनाएं.

इसके अलावा, समस्या हल करने वाले टूल का इस्तेमाल करके, अपने SQLite डेटाबेस की परफ़ॉर्मेंस का आकलन किया जा सकता है. इससे, आपको उन जगहों की पहचान करने में मदद मिलेगी जहां ऑप्टिमाइज़ेशन की ज़रूरत है.

हमारा सुझाव है कि आप Jetpack Room लाइब्रेरी का इस्तेमाल करें.

परफ़ॉर्मेंस के लिए डेटाबेस कॉन्फ़िगर करना

अपने डेटाबेस को बेहतर तरीके से कॉन्फ़िगर करने के लिए, इस सेक्शन में दिया गया तरीका अपनाएं परफ़ॉर्मेंस को बेहतर बनाने के लिए किया जा सकता है.

पहले से लिखने की सुविधा चालू करना

SQLite, म्यूटेशन को लॉग में जोड़कर उन्हें लागू करता है. हालांकि, ऐसा कभी-कभी होता है छोटी हो जाती हैं. इसे लिखकर आगे बढ़ना कहा जाता है (WAL) पर टैप करें.

चालू करें वाल बशर्ते, ATTACH DATABASE का इस्तेमाल न किया जा रहा हो.

सिंक करने के मोड को आसान बनाना

WAL का इस्तेमाल करने पर, हर बार fsync की समस्या को डिफ़ॉल्ट तौर पर हल किया जाता है. इससे यह पक्का करने में मदद मिलती है कि तो डेटा डिस्क तक पहुंचता है. इससे डेटा को सेव रखने की अवधि बढ़ जाती है, लेकिन कमिट करने की प्रोसेस धीमी हो जाती है.

SQLite में, सिंक्रोनाइज़ेशन मोड को कंट्रोल करने का विकल्प होता है. अगर आपको WAL को चालू करने के लिए, सिंक्रोनस मोड को NORMAL पर सेट करें:

Kotlin

db.execSQL("PRAGMA synchronous = NORMAL")

Java

db.execSQL("PRAGMA synchronous = NORMAL");

इस सेटिंग में, डिस्क में डेटा सेव होने से पहले ही कमिट हो सकता है. अगर कोई डिवाइस बंद हो जाता है, जैसे कि पावर खो जाने या कर्नेल घबराहट होने पर, तय डेटा खो सकता है. हालांकि, लॉगिंग के कारण, आपका डेटाबेस करप्ट है.

अगर सिर्फ़ आपका ऐप्लिकेशन क्रैश होता है, तब भी आपका डेटा डिस्क तक पहुंचता है. ज़्यादातर ऐप्लिकेशन के लिए, यह सेटिंग से, बिना किसी अतिरिक्त शुल्क के परफ़ॉर्मेंस को बेहतर बनाया जाता है.

कुशल टेबल स्कीमा तय करना

परफ़ॉर्मेंस को ऑप्टिमाइज़ करने और डेटा की खपत को कम करने के लिए, टेबल स्कीमा. SQLite, क्वेरी के बेहतर प्लान और डेटा बनाता है. इससे तेज़ी से डेटा वापस पाया जा सकता है. इस सेक्शन में, टेबल बनाने के सबसे सही तरीके बताए गए हैं स्कीमा शामिल करने हैं.

INTEGER PRIMARY KEY पर विचार करें

इस उदाहरण के लिए, नीचे बताए गए तरीके से टेबल तय करें और उसे अपने-आप भरे:

CREATE TABLE Customers(
  id INTEGER,
  name TEXT,
  city TEXT
);
INSERT INTO Customers Values(456, 'John Lennon', 'Liverpool, England');
INSERT INTO Customers Values(123, 'Michael Jackson', 'Gary, IN');
INSERT INTO Customers Values(789, 'Dolly Parton', 'Sevier County, TN');

टेबल का आउटपुट इस तरह का होता है:

पंक्ति आईडी id नाम शहर
1 456 जॉन लेनन लिवरपूल, इंग्लैंड
2 123 माइकल जैक्सन गैरी, इंडियाना
3 789 डॉली पार्टन सेवियर काउंटी, टेनसी

कॉलम rowid यह है वह इंडेक्स जो इंसर्शन ऑर्डर को सुरक्षित रखता है. ऐसी क्वेरी जो rowid के फ़िल्टर का इस्तेमाल करके तेज़ी से बी-ट्री खोज की जा सकती है, लेकिन id के हिसाब से फ़िल्टर करने पर, टेबल स्कैन होने में ज़्यादा समय लगता है.

अगर आप id तक लुकअप करने की योजना बना रहे हैं, तो आप कम स्टोरेज और कुल स्टोरेज के लिए rowid कॉलम तेज़ डेटाबेस:

CREATE TABLE Customers(
  id INTEGER PRIMARY KEY,
  name TEXT,
  city TEXT
);

अब आपकी टेबल इस तरह दिखेगी:

id नाम शहर
123 माइकल जैक्सन गैरी, इंडियाना
456 जॉन लेनन लिवरपूल, इंग्लैंड
789 डॉली पार्टन सेवियर काउंटी, टेनसी

आपको rowid कॉलम को सेव करने की ज़रूरत नहीं है, इसलिए id क्वेरी तेज़ होती हैं. नोट जोड़ें कि टेबल को अब इंसर्शन ऑर्डर के बजाय id के हिसाब से क्रम में लगाया गया है.

इंडेक्स की मदद से क्वेरी की रफ़्तार बढ़ाना

क्वेरी को तेज़ करने के लिए, SQLite इंडेक्स का इस्तेमाल करता है. फ़िल्टर (WHERE), क्रम में लगाने के लिए (ORDER BY) या (GROUP BY) कॉलम को एग्रीगेट करने, अगर टेबल में कॉलम के लिए इंडेक्स है, तो क्वेरी को तेज़ कर दिया गया है.

पिछले उदाहरण में, city के हिसाब से फ़िल्टर करने के लिए, पूरी टेबल को स्कैन करना ज़रूरी है:

SELECT id, name
WHERE city = 'London, England';

शहर से जुड़ी बहुत सारी क्वेरी वाले ऐप्लिकेशन के लिए, इसकी मदद से इंडेक्स:

CREATE INDEX city_index ON Customers(city);

इंडेक्स को एक अतिरिक्त टेबल के तौर पर लागू किया जाता है. इसे इंडेक्स कॉलम के हिसाब से क्रम में लगाया जाता है और rowid पर मैप किया जाता है:

शहर rowid
गैरी, इंडियाना 2
लिवरपूल, इंग्लैंड 1
सेवियर काउंटी, टेनसी 3

ध्यान दें कि city कॉलम के स्टोरेज की लागत अब दोगुनी हो गई है, क्योंकि यह यह जानकारी अब ओरिजनल टेबल और इंडेक्स, दोनों में मौजूद है. चूंकि आप इंडेक्स करने के लिए कहा जाता है, तो अतिरिक्त स्टोरेज की लागत ज़्यादा तेज़ी से की जाने वाली क्वेरी के लिए फ़ायदेमंद है. हालांकि, ऐसे इंडेक्स का रखरखाव न करें जिसका इस्तेमाल आप बिना क्वेरी की परफ़ॉर्मेंस गेन के लिए स्टोरेज लागत.

एक से ज़्यादा कॉलम वाले इंडेक्स बनाएं

अगर आपकी क्वेरी में एक से ज़्यादा कॉलम जोड़े गए हैं, तो आपके पास एक से ज़्यादा कॉलम वाला कॉलम बनाने का विकल्प होता है इंडेक्स का इस्तेमाल करें. इंडेक्स का इस्तेमाल, किसी बाहरी कॉलम पर भी किया जा सकता है और आंतरिक खोज को लीनियर स्कैन की तरह किया जाना चाहिए.

उदाहरण के लिए, दी गई क्वेरी:

SELECT id, name
WHERE city = 'London, England'
ORDER BY city, name

एक से ज़्यादा कॉलम वाले इंडेक्स का इस्तेमाल करके, क्वेरी को उसी क्रम में तेज़ी से लोड किया जा सकता है क्वेरी में बताया गया है:

CREATE INDEX city_name_index ON Customers(city, name);

हालांकि, अगर आपके पास सिर्फ़ city पर इंडेक्स है, तो बाहर के क्रम में तेज़ है, जबकि अंदर के क्रम को लीनियर स्कैन की ज़रूरत होती है.

यह प्रीफ़िक्स वाली क्वेरी के साथ भी काम करता है. उदाहरण के लिए, इंडेक्स में ON Customers (city, name) से फ़िल्टर करने, क्रम से लगाने, और ग्रुप में बांटने की सुविधा तेज़ी से काम करती है city के हिसाब से, क्योंकि एक से ज़्यादा कॉलम वाले इंडेक्स के लिए इंडेक्स टेबल दिए गए इंडेक्स को दिए गए क्रम में रखें.

WITHOUT ROWID पर विचार करें

डिफ़ॉल्ट रूप से, SQLite आपकी टेबल के लिए एक rowid कॉलम बनाता है, जहां rowid एक इंप्लिसिट INTEGER PRIMARY KEY AUTOINCREMENT. अगर आपके पास पहले से ही कोई ऐसा कॉलम है INTEGER PRIMARY KEY है, तो यह कॉलम rowid का उपनाम बन जाता है.

उन टेबल के लिए जिनमें INTEGER के अलावा कोई मुख्य कुंजी या कंपोज़िट शामिल है कॉलम, WITHOUT ROWID पर विचार करें.

छोटे डेटा को BLOB के तौर पर और बड़े डेटा को फ़ाइल के तौर पर सेव करें

अगर आपको बड़े डेटा को किसी लाइन में जोड़ना है, जैसे कि किसी इमेज का थंबनेल या संपर्क की फ़ोटो है, तो आप डेटा को BLOB कॉलम में या a फ़ाइल एक्सपोर्ट करें और फिर फ़ाइल पाथ को कॉलम में स्टोर करें.

आम तौर पर, फ़ाइलों का साइज़ 4 केबी की बढ़ोतरी तक ही किया जाता है. बहुत छोटी फ़ाइलों के लिए, जहां पूर्णांकन गड़बड़ी बड़ी है, तो उन्हें BLOB के तौर पर डेटाबेस. SQLite, फ़ाइलसिस्टम कॉल को कम करता है और यह फ़ाइल सिस्टम कुछ मामलों में.

क्वेरी की परफ़ॉर्मेंस को बेहतर बनाएं

SQLite में, क्वेरी की परफ़ॉर्मेंस को बेहतर बनाने के लिए, इन सबसे सही तरीकों को अपनाएं जवाब देने में लगने वाला समय और प्रोसेसिंग की क्षमता को ज़्यादा से ज़्यादा बढ़ाने के लिए किया जा सकता है.

सिर्फ़ अपनी ज़रूरत की लाइन पढ़ें

फ़िल्टर आपको कुछ शर्तें तय करके, नतीजों को छोटा करने देता है, जैसे कि तारीख की सीमा, जगह या नाम. सीमाओं की मदद से, संख्या कंट्रोल की जा सकती है परिणामों में से आप देखते हैं:

Kotlin

db.rawQuery("""
    SELECT name
    FROM Customers
    LIMIT 10;
    """.trimIndent(),
    null
).use { cursor ->
    while (cursor.moveToNext()) {
        ...
    }
}

Java

try (Cursor cursor = db.rawQuery("""
    SELECT name
    FROM Customers
    LIMIT 10;
    """, null)) {
  while (cursor.moveToNext()) {
    ...
  }
}

सिर्फ़ उन कॉलम को पढ़ना जिनकी आपको ज़रूरत है

ग़ैर-ज़रूरी कॉलम चुनने से बचें. इससे ये काम किए जा सकते हैं वे आपकी क्वेरी को धीमा कर देते हैं और संसाधनों की बर्बादी भी कम करते हैं. इसके बजाय, सिर्फ़ कॉलम चुनें जिनका इस्तेमाल किया जा चुका हो.

यहां दिए गए उदाहरण में, id, name, और phone को चुना गया है:

Kotlin

// This is not the most efficient way of doing this.
// See the following example for a better approach.

db.rawQuery(
    """
    SELECT id, name, phone
    FROM customers;
    """.trimIndent(),
    null
).use { cursor ->
    while (cursor.moveToNext()) {
        val name = cursor.getString(1)
        // ...
    }
}

Java

// This is not the most efficient way of doing this.
// See the following example for a better approach.

try (Cursor cursor = db.rawQuery("""
    SELECT id, name, phone
    FROM customers;
    """, null)) {
  while (cursor.moveToNext()) {
    String name = cursor.getString(1);
    ...
  }
}

हालांकि, आपको सिर्फ़ name कॉलम की ज़रूरत है:

Kotlin

db.rawQuery("""
    SELECT name
    FROM Customers;
    """.trimIndent(),
    null
).use { cursor ->
    while (cursor.moveToNext()) {
        val name = cursor.getString(0)
        ...
    }
}

Java

try (Cursor cursor = db.rawQuery("""
    SELECT name
    FROM Customers;
    """, null)) {
  while (cursor.moveToNext()) {
    String name = cursor.getString(0);
    ...
  }
}

क्वेरी को एसक्यूएल कार्ड के साथ पैरामीटर करें, न कि स्ट्रिंग जोड़ने की प्रोसेस के साथ

आपकी क्वेरी स्ट्रिंग में कोई ऐसा पैरामीटर हो सकता है जिसे सिर्फ़ रनटाइम के समय जाना जाता है, जैसे जैसे:

Kotlin

fun getNameById(id: Long): String? 
    db.rawQuery(
        "SELECT name FROM customers WHERE id=$id", null
    ).use { cursor ->
        return if (cursor.moveToFirst()) {
            cursor.getString(0)
        } else {
            null
        }
    }
}

Java

@Nullable
public String getNameById(long id) {
  try (Cursor cursor = db.rawQuery(
      "SELECT name FROM customers WHERE id=" + id, null)) {
    if (cursor.moveToFirst()) {
      return cursor.getString(0);
    } else {
      return null;
    }
  }
}

पिछले कोड में, हर क्वेरी एक अलग स्ट्रिंग बनाती है, और इस तरह स्टेटमेंट कैश मेमोरी से फ़ायदा नहीं होता. हर कॉल को कंपाइल करने के लिए, SQLite की ज़रूरत होती है उसे एक्ज़ीक्यूट करने से पहले ही उसे हटा दिया जाता है. इसके बजाय, id आर्ग्युमेंट को पैरामीटर और वैल्यू को selectionArgs से बाइंड करें:

Kotlin

fun getNameById(id: Long): String? {
    db.rawQuery(
        """
          SELECT name
          FROM customers
          WHERE id=?
        """.trimIndent(), arrayOf(id.toString())
    ).use { cursor ->
        return if (cursor.moveToFirst()) {
            cursor.getString(0)
        } else {
            null
        }
    }
}

Java

@Nullable
public String getNameById(long id) {
  try (Cursor cursor = db.rawQuery("""
          SELECT name
          FROM customers
          WHERE id=?
      """, new String[] {String.valueOf(id)})) {
    if (cursor.moveToFirst()) {
      return cursor.getString(0);
    } else {
      return null;
    }
  }
}

अब क्वेरी को एक बार कंपाइल और कैश किया जा सकता है. कंपाइल की गई क्वेरी का फिर से इस्तेमाल किया गया getNameById(long) को अलग-अलग तरीके से शुरू किया गया है.

एसक्यूएल में दोहराएं, कोड में नहीं

प्रोग्राम के हिसाब से किसी प्रोग्राम के बजाय एक ही क्वेरी का इस्तेमाल करें, जो टारगेट किए गए सभी नतीजे दिखाती हो अलग-अलग नतीजे दिखाने के लिए, एसक्यूएल क्वेरी को लूप में दोहराना. प्रोग्राम के हिसाब से, अपने-आप होने वाली प्रोसेस लूप, एक एसक्यूएल क्वेरी से करीब 1,000 गुना धीमा है.

यूनीक वैल्यू के लिए DISTINCT का इस्तेमाल करें

DISTINCT कीवर्ड का इस्तेमाल करने से आपकी क्वेरी की परफ़ॉर्मेंस बेहतर हो सकती है प्रोसेस किए जाने वाले डेटा की मात्रा को कम कर देगा. उदाहरण के लिए, अगर आपको किसी कॉलम से सिर्फ़ यूनीक वैल्यू दिखाने के लिए, DISTINCT का इस्तेमाल करें:

Kotlin

db.rawQuery("""
    SELECT DISTINCT name
    FROM Customers;
    """.trimIndent(),
    null
).use { cursor ->
    while (cursor.moveToNext()) {
        // Only iterate over distinct names in Kotlin
        ...
    }
}

Java

try (Cursor cursor = db.rawQuery("""
    SELECT DISTINCT name
    FROM Customers;
    """, null)) {
  while (cursor.moveToNext()) {
    // Only iterate over distinct names in Java
    ...
  }
}

जब भी हो सके, एग्रीगेट फ़ंक्शन का इस्तेमाल करें

पंक्ति डेटा के बिना एग्रीगेट नतीजों के लिए एग्रीगेट फ़ंक्शन का इस्तेमाल करें. उदाहरण के लिए, यहां दिया गया कोड यह जांचता है कि मैच करने वाली कम से कम एक लाइन मौजूद है या नहीं:

Kotlin

// This is not the most efficient way of doing this.
// See the following example for a better approach.

db.rawQuery("""
    SELECT id, name
    FROM Customers
    WHERE city = 'Paris';
    """.trimIndent(),
    null
).use { cursor ->
    if (cursor.moveToFirst()) {
        // At least one customer from Paris
        ...
    } else {
        // No customers from Paris
        ...
}

Java

// This is not the most efficient way of doing this.
// See the following example for a better approach.

try (Cursor cursor = db.rawQuery("""
    SELECT id, name
    FROM Customers
    WHERE city = 'Paris';
    """, null)) {
  if (cursor.moveToFirst()) {
    // At least one customer from Paris
    ...
  } else {
    // No customers from Paris
    ...
  }
}

सिर्फ़ पहली पंक्ति फ़ेच करने के लिए, EXISTS() का इस्तेमाल करके 0 दिखाया जा सकता है. ऐसा तब किया जा सकता है, जब मैच करने वाली कोई पंक्ति मौजूद न हो. अगर एक या उससे ज़्यादा पंक्तियां मैच करती हैं, तो 1 दिखाया जा सकता है:

Kotlin

db.rawQuery("""
    SELECT EXISTS (
        SELECT null
        FROM Customers
        WHERE city = 'Paris';
    );
    """.trimIndent(),
    null
).use { cursor ->
    if (cursor.moveToFirst() && cursor.getInt(0) == 1) {
        // At least one customer from Paris
        ...
    } else {
        // No customers from Paris
        ...
    }
}

Java

try (Cursor cursor = db.rawQuery("""
    SELECT EXISTS (
      SELECT null
      FROM Customers
      WHERE city = 'Paris'
    );
    """, null)) {
  if (cursor.moveToFirst() && cursor.getInt(0) == 1) {
    // At least one customer from Paris
    ...
  } else {
    // No customers from Paris
    ...
  }
}

SQLite एग्रीगेट का इस्तेमाल करना फ़ंक्शन में बदलाव कर सकते हैं कोड:

  • COUNT: यह गिनता है कि कॉलम में कितनी पंक्तियां हैं.
  • SUM: यह किसी कॉलम में संख्या वाली सभी वैल्यू जोड़ता है.
  • MIN या MAX: यह सबसे कम या सबसे बड़ी वैल्यू तय करता है. न्यूमेरिक के लिए काम करता है कॉलम, DATE टाइप और टेक्स्ट टाइप.
  • AVG: औसत संख्या वाली वैल्यू का पता लगाता है.
  • GROUP_CONCAT: सेपरेटर की मदद से स्ट्रिंग को जोड़ता है.

Cursor.getCount() के बजाय COUNT() का इस्तेमाल करें

इस उदाहरण में, Cursor.getCount() फ़ंक्शन, डेटाबेस की सभी पंक्तियों को पढ़ता है और सभी पंक्तियों की वैल्यू दिखाता है:

Kotlin

// This is not the most efficient way of doing this.
// See the following example for a better approach.

db.rawQuery("""
    SELECT id
    FROM Customers;
    """.trimIndent(),
    null
).use { cursor ->
    val count = cursor.getCount()
}

Java

// This is not the most efficient way of doing this.
// See the following example for a better approach.

try (Cursor cursor = db.rawQuery("""
    SELECT id
    FROM Customers;
    """, null)) {
  int count = cursor.getCount();
  ...
}

हालांकि, COUNT() का इस्तेमाल करने पर डेटाबेस संख्या:

Kotlin

db.rawQuery("""
    SELECT COUNT(*)
    FROM Customers;
    """.trimIndent(),
    null
).use { cursor ->
    cursor.moveToFirst()
    val count = cursor.getInt(0)
}

Java

try (Cursor cursor = db.rawQuery("""
    SELECT COUNT(*)
    FROM Customers;
    """, null)) {
  cursor.moveToFirst();
  int count = cursor.getInt(0);
  ...
}

कोड के बजाय Nest क्वेरी

एसक्यूएल को कंपोज़ किया जा सकता है और यह सबक्वेरी, जॉइन, और फ़ॉरेन की कंस्ट्रेंट के साथ काम करता है. ऐप्लिकेशन पर जाए बिना, एक क्वेरी के नतीजे को दूसरी क्वेरी में इस्तेमाल किया जा सकता है कोड. इससे SQLite से डेटा कॉपी करने की ज़रूरत कम हो जाती है और डेटाबेस इंजन आपकी क्वेरी को ऑप्टिमाइज़ करता है.

नीचे दिए गए उदाहरण में, किसी क्वेरी को चलाकर यह पता लगाया जा सकता है कि किस शहर में सबसे ज़्यादा ग्राहक हैं. इसके बाद, उस शहर के सभी ग्राहकों को ढूंढने के लिए, नतीजे का इस्तेमाल किसी दूसरी क्वेरी में किया जा सकता है:

Kotlin

// This is not the most efficient way of doing this.
// See the following example for a better approach.

db.rawQuery("""
    SELECT city
    FROM Customers
    GROUP BY city
    ORDER BY COUNT(*) DESC
    LIMIT 1;
    """.trimIndent(),
    null
).use { cursor ->
    if (cursor.moveToFirst()) {
        val topCity = cursor.getString(0)
        db.rawQuery("""
            SELECT name, city
            FROM Customers
            WHERE city = ?;
        """.trimIndent(),
        arrayOf(topCity)).use { innerCursor ->
            while (innerCursor.moveToNext()) {
                ...
            }
        }
    }
}

Java

// This is not the most efficient way of doing this.
// See the following example for a better approach.

try (Cursor cursor = db.rawQuery("""
    SELECT city
    FROM Customers
    GROUP BY city
    ORDER BY COUNT(*) DESC
    LIMIT 1;
    """, null)) {
  if (cursor.moveToFirst()) {
    String topCity = cursor.getString(0);
    try (Cursor innerCursor = db.rawQuery("""
        SELECT name, city
        FROM Customers
        WHERE city = ?;
        """, new String[] {topCity})) {
        while (innerCursor.moveToNext()) {
          ...
        }
    }
  }
}

पिछले उदाहरण के मुकाबले आधे समय में नतीजा पाने के लिए, नेस्ट किए गए स्टेटमेंट के साथ एक SQL क्वेरी का इस्तेमाल करें:

Kotlin

db.rawQuery("""
    SELECT name, city
    FROM Customers
    WHERE city IN (
        SELECT city
        FROM Customers
        GROUP BY city
        ORDER BY COUNT (*) DESC
        LIMIT 1;
    );
    """.trimIndent(),
    null
).use { cursor ->
    if (cursor.moveToNext()) {
        ...
    }
}

Java

try (Cursor cursor = db.rawQuery("""
    SELECT name, city
    FROM Customers
    WHERE city IN (
      SELECT city
      FROM Customers
      GROUP BY city
      ORDER BY COUNT(*) DESC
      LIMIT 1
    );
    """, null)) {
  while(cursor.moveToNext()) {
    ...
  }
}

एसक्यूएल में यूनीक वैल्यू की जांच करना

अगर किसी कॉलम की वैल्यू टेबल में यूनीक होने तक, किसी लाइन को डाला नहीं जाना चाहिए, तो उस यूनीक वैल्यू को कॉलम की पाबंदी के तौर पर लागू करना ज़्यादा असरदार हो सकता है.

नीचे दिए गए उदाहरण में, एक क्वेरी चलाई गई है, ताकि लाइन और अन्य को वास्तव में शामिल करने के लिए:

Kotlin

// This is not the most efficient way of doing this.
// See the following example for a better approach.

db.rawQuery(
    """
    SELECT EXISTS (
        SELECT null
        FROM customers
        WHERE username = ?
    );
    """.trimIndent(),
    arrayOf(customer.username)
).use { cursor ->
    if (cursor.moveToFirst() && cursor.getInt(0) == 1) {
        throw AddCustomerException(customer)
    }
}
db.execSQL(
    "INSERT INTO customers VALUES (?, ?, ?)",
    arrayOf(
        customer.id.toString(),
        customer.name,
        customer.username
    )
)

Java

// This is not the most efficient way of doing this.
// See the following example for a better approach.

try (Cursor cursor = db.rawQuery("""
    SELECT EXISTS (
      SELECT null
      FROM customers
      WHERE username = ?
    );
    """, new String[] { customer.username })) {
  if (cursor.moveToFirst() && cursor.getInt(0) == 1) {
    throw new AddCustomerException(customer);
  }
}
db.execSQL(
    "INSERT INTO customers VALUES (?, ?, ?)",
    new String[] {
      String.valueOf(customer.id),
      customer.name,
      customer.username,
    });

Kotlin या Java में यूनीक कंस्ट्रेंट की जांच करने के बजाय, इसे चेक किया जा सकता है टेबल तय करते समय एसक्यूएल:

CREATE TABLE Customers(
  id INTEGER PRIMARY KEY,
  name TEXT,
  username TEXT UNIQUE
);

SQLite भी यही काम करता है:

CREATE TABLE Customers(...);
CREATE UNIQUE INDEX CustomersUsername ON Customers(username);

अब एक पंक्ति डाली जा सकती है और SQLite को कंस्ट्रेंट की जांच करने दिया जा सकता है:

Kotlin

try {
    db.execSql(
        "INSERT INTO Customers VALUES (?, ?, ?)",
        arrayOf(customer.id.toString(), customer.name, customer.username)
    )
} catch(e: SQLiteConstraintException) {
    throw AddCustomerException(customer, e)
}

Java

try {
  db.execSQL(
      "INSERT INTO Customers VALUES (?, ?, ?)",
      new String[] {
        String.valueOf(customer.id),
        customer.name,
        customer.username,
      });
} catch (SQLiteConstraintException e) {
  throw new AddCustomerException(customer, e);
}

SQLite, कई कॉलम वाले यूनीक इंडेक्स के साथ काम करता है:

CREATE TABLE table(...);
CREATE UNIQUE INDEX unique_table ON table(column1, column2, ...);

SQLite, Kotlin या Java की तुलना में कम ओवरहेड के साथ, कंस्ट्रेंट की तेज़ी से पुष्टि करता है कोड. ऐप्लिकेशन कोड के बजाय, SQLite का इस्तेमाल करना सबसे सही तरीका है.

एक ही लेन-देन में कई इंसर्शन एक साथ करना

एक ट्रांज़ैक्शन में कई कार्रवाइयां होती हैं. इससे न सिर्फ़ परफ़ॉर्मेंस, बल्कि सटीक जानकारी भी. डेटा की एक जैसी क्वालिटी बनाए रखने और परफ़ॉर्मेंस को बेहतर बनाने के लिए, एक साथ कई डेटा डाले जा सकते हैं:

Kotlin

db.beginTransaction()
try {
    customers.forEach { customer ->
        db.execSql(
            "INSERT INTO Customers VALUES (?, ?, ...)",
            arrayOf(customer.id.toString(), customer.name, ...)
        )
    }
} finally {
    db.endTransaction()
}

Java

db.beginTransaction();
try {
  for (customer : Customers) {
    db.execSQL(
        "INSERT INTO Customers VALUES (?, ?, ...)",
        new String[] {
          String.valueOf(customer.id),
          customer.name,
          ...
        });
  }
} finally {
  db.endTransaction()
}

समस्या हल करने वाले टूल इस्तेमाल करना

SQLite, परफ़ॉर्मेंस को मेज़र करने के लिए, समस्या हल करने वाले ये टूल उपलब्ध कराता है.

SQLite के इंटरैक्टिव प्रॉम्प्ट का इस्तेमाल करें

क्वेरी चलाने और सीखने के लिए, अपनी मशीन पर SQLite चलाएं. Android प्लैटफ़ॉर्म के अलग-अलग वर्शन में, SQLite के अलग-अलग बदलावों का इस्तेमाल किया जाता है. इस्तेमाल करने के लिए जो Android डिवाइस में काम करता है, उसी इंजन के लिए adb shell और अपने टारगेट डिवाइस पर sqlite3 चलाएं.

SQLite से क्वेरी के लिए समय तय करने के लिए कहा जा सकता है:

sqlite> .timer on
sqlite> SELECT ...
Run Time: real ... user ... sys ...

EXPLAIN QUERY PLAN

SQLite से, यह जानने के लिए कहा जा सकता है कि वह किसी क्वेरी का जवाब कैसे देना चाहता है EXPLAIN QUERY PLAN:

sqlite> EXPLAIN QUERY PLAN
SELECT id, name
FROM Customers
WHERE city = 'Paris';
QUERY PLAN
`--SCAN Customers

पिछले उदाहरण में, सभी को ढूंढने के लिए इंडेक्स के बिना पूरी टेबल स्कैन करना ज़रूरी है दिल्ली के ग्राहक. इसे लीनियर कॉम्प्लेक्सिटी कहा जाता है. SQLite को पढ़ना ज़रूरी है सभी पंक्तियों को शामिल करें. साथ ही, सिर्फ़ वे पंक्तियां रखें जो पेरिस के ग्राहकों से मेल खाती हों. इसे ठीक करने के लिए, इंडेक्स जोड़ा जा सकता है:

sqlite> CREATE INDEX Idx1 ON Customers(city);
sqlite> EXPLAIN QUERY PLAN
SELECT id, name
FROM Customers
WHERE city = 'Paris';
QUERY PLAN
`--SEARCH test USING INDEX Idx1 (city=?

अगर इंटरैक्टिव शेल का इस्तेमाल किया जा रहा है, तो SQLite से कहें कि वह हमेशा क्वेरी प्लान:

sqlite> .eqp on

ज़्यादा जानकारी के लिए, यह देखें क्वेरी प्लानिंग.

SQLite ऐनालाइज़र

SQLite, sqlite3_analyzer कमांड-लाइन इंटरफ़ेस (सीएलआई) का इस्तेमाल करता है. इसका इस्तेमाल परफ़ॉर्मेंस से जुड़ी समस्या हल करें. इंस्टॉल करने के लिए, इस लिंक पर जाएं SQLite डाउनलोड पेज.

adb pull का इस्तेमाल करके, टारगेट किए गए डिवाइस से डेटाबेस फ़ाइल डाउनलोड की जा सकती है विश्लेषण के लिए वर्कस्टेशन:

adb pull /data/data/<app_package_name>/databases/<db_name>.db

SQLite ब्राउज़र

इसके अलावा, जीयूआई टूल भी इंस्टॉल किया जा सकता है. SQLite पर SQLite ब्राउज़र डाउनलोड पेज.

Android में लॉग इन करने की सुविधा

Android के बार, SQLite से क्वेरी और आपके लिए उन्हें लॉग किया जाता है:

# Enable query time logging
$ adb shell setprop log.tag.SQLiteTime VERBOSE
# Disable query time logging
$ adb shell setprop log.tag.SQLiteTime ERROR
```### Perfetto tracing

### Perfetto tracing {:#perfetto-tracing}

When [configuring Perfetto](https://perfetto.dev/docs/concepts/config), you may
add the following to include tracks for individual queries:

```protobuf
data_sources {
  config {
    name: "linux.ftrace"
    ftrace_config {
      atrace_categories: "database"
    }
  }
}