Як перевірити, чи існує таблиця в SQLite?


893

Як я надійно перевіряю в SQLite, чи існує певна таблиця користувачів?

Я не прошу недостовірних способів, таких як перевірка, чи "select *" на столі повернув помилку чи ні (це навіть гарна ідея?).

Причина така:

У своїй програмі мені потрібно створити, а потім заповнити деякі таблиці, якщо вони вже не існують.

Якщо вони вже існують, мені потрібно оновити деякі таблиці.

Чи повинен я замість цього взяти якийсь інший шлях, щоб сигналізувати про те, що розглядаються таблиці вже створені - скажімо, наприклад, створивши / поставивши / встановивши певний прапор у файлі ініціалізації / налаштування програми на диску чи щось таке?

Або мій підхід має сенс?


SQLite видасть виняток, якщо таблиця у виділеному не існує. Просто немає необхідності в більш вигадливій роботі.
NoChance

34
@NoChance це буде, але так само буде і з будь-якою кількістю інших речей. Це трохи схоже на те, чи є це дерево насправді там, рухаючи вперед із заплющеними очима, ви дізнаєтесь так чи інакше :)
randomsock

@randomsock, приємний приклад, але трохи страшно, особливо якщо машина була моєю машиною ...
NoChance

@randomsock, я не знаю, що таке конвенція sqlite, але просити прощення, ніж дозволу, пітонічніше. тобто зловити виняток замість використання умовного.
Ерік

1
@Eric На сьогодні питання не стосується Python, але якщо припустити, що помилка є загальною sqlite3.OperationalError, тому вам потрібно розібрати повідомлення про помилку, щоб переконатися, що це повідомлення, наприклад, "таблиця TABLE_NAME вже існує" під час створення таблицю, а якщо ні, повторно помиліться, і я думаю, що немає гарантії, формулювання помилки не зміниться.
Маркус фон

Відповіді:


1020

Я пропустив цей запис FAQ.

У будь-якому випадку, для подальшого ознайомлення, повний запит:

SELECT name FROM sqlite_master WHERE type='table' AND name='{table_name}';

Звідки {table_name}назва таблиці для перевірки.

Довідковий розділ для довідки: Формат файлу бази даних. 2.6. Зберігання схеми баз даних SQL

  • Це поверне список таблиць із вказаною назвою; тобто курсор матиме кількість 0 (не існує) або число 1 (існує)

7
Яка документація на SQLite охоплює ці системні таблиці?
Павло Веселов

29
@Павель Веселов: Розділ під назвою "Формат файлів для баз даних SQLite": sqlite.org/fileformat2.html
Брайан Оуклі,

14
Однак це не працюватиме для таблиць TEMP. Таблиці TEMP знаходяться в "sqlite_temp_master."
PatchyFog

11
Це повертається булевим? Що повертається, якщо таблиця існує чи не існує?
Dagrooms

8
@Dagrooms Це поверне список таблиць із вказаною назвою; тобто курсор матиме кількість 0 (не існує) або число 1 (існує).
Rein S

555

Якщо ви використовуєте SQLite версії 3.3 або новішої, ви можете легко створити таблицю за допомогою:

create table if not exists TableName (col1 typ1, ..., colN typN)

Таким же чином ви можете видалити таблицю, лише якщо вона існує, використовуючи:

drop table if exists TableName

3
Зауважте, що create tableоператор є неповним (відсутня специфікація стовпців таблиці).
Ерік Платон

11
існує також аналогічна конструкція для індексів: створити індекс, якщо його немає, TableName_col1 на TableName (col1)
lowtech

26
Це не має бути прийнятою відповіддю, але якби питання було сформульовано інакше. ОП не запитувало, як перевірити таблицю перед тим, як відкинути або створити. Що робити, якщо вам доведеться запитувати таблицю, яка, можливо, не існує? Це проблема, з якою я стикаюся зараз, і прийнята відповідь найкраще працює в цій загальній постановці проблеми. Це хороша швидка альтернатива.
Dagrooms

@Dagrooms, ви можете мати рацію. Хоча ОП цього не запитувала, я шукала цю відповідь :)
earik87

169

Варіантом буде використання SELECT COUNT (*) замість SELECT NAME, тобто

SELECT count(*) FROM sqlite_master WHERE type='table' AND name='table_name';

Це поверне 0, якщо таблиця не існує, 1, якщо вона є. Це, мабуть, корисно у вашому програмуванні, оскільки числовий результат швидше / простіше обробляти. Далі показано, як ви зробили це в Android за допомогою SQLiteDatabase, Cursor, rawQuery з параметрами.

boolean tableExists(SQLiteDatabase db, String tableName)
{
    if (tableName == null || db == null || !db.isOpen())
    {
        return false;
    }
    Cursor cursor = db.rawQuery("SELECT COUNT(*) FROM sqlite_master WHERE type = ? AND name = ?", new String[] {"table", tableName});
    if (!cursor.moveToFirst())
    {
        cursor.close();
        return false;
    }
    int count = cursor.getInt(0);
    cursor.close();
    return count > 0;
}

33
Я вважаю, що "ВИБІР 1" був би ще швидшим.
PatchyFog

Чому cursor.getInt (0) дорівнює числу записів у базі даних?
Семен Данилов

1
Ми підраховуємо, скільки разів TABLE з'являється в схемі sqlite. Кількість 0 означає, що таблиці не існує. Підрахунок 1 означає, що таблиця існує. Це єдині два очікуваних значення рахунку.
Стівен Куан

1
Хоча число (від COUNT(*)) легко обробляти, ще простіше повернути існування рядка чи ні; якщо там є рядок, то він існує, якщо немає рядка, він не є. (Ви вже перевіряєте невдачу в moveToFirst, тому робота буде виконана в цей момент.)
dash-tom-bang

Оновіть свій код, щоб закрити курсор, перш ніж повернути помилковий.
Дейв Томас

43

Ви можете спробувати:

SELECT name FROM sqlite_master WHERE name='table_name'

4
type = table буде корисним tho
mafu

Якщо ви використовуєте C #, не використовуйте цю команду в a SQLiteReader reader = cmd.ExecuteReader();і виконайте dt.Load(reader)(де dtє DataTable). Я виявив, що це дає це Object reference is not an instance of an objectвиняток у .Load()випадку, якщо таблиця не знайдена. Натомість використовуйте a SQLiteDataAdapter adapter = new SQLiteDataAdapter(cmd); і do adapter.Fill(ds), де dsє DataSet. Потім ви можете побачити, якщо ds.Tables.Count > 0і return ds.Tables[0];якщо так (або else return null). Тоді ви можете перевірити це DataTableна наявність null, якщо dt.Rows != null, і якщоdt.Rows.Count>0
vapcguy

34

Використання:

PRAGMA table_info(your_table_name)

Якщо отримана таблиця порожня your_table_name, її не існує.

Документація:

PRAGMA schema.table_info (ім'я таблиці);

Ця прагма повертає один рядок для кожного стовпця у названій таблиці. Стовпці в наборі результатів містять назву стовпця, тип даних, може бути стовпець NULL або значення за замовчуванням для стовпця. Стовпець "pk" у наборі результатів дорівнює нулю для стовпців, які не є частиною первинного ключа, і є індексом стовпця в первинному ключі для стовпців, що входять до складу первинного ключа.

Таблиця, названа в прагмі table_info, також може бути переглядом.

Приклад виводу:

cid|name|type|notnull|dflt_value|pk
0|id|INTEGER|0||1
1|json|JSON|0||0
2|name|TEXT|0||0

Це прекрасний спосіб визначити, чи існує таблиця в Python.
Майкл Мерфі

або Xamarin Forms
SerenityNow

4
Це прекрасний спосіб програмно визначитись із визначеннями стовпців
w00t

33

Назви таблиць SQLite нечутливі до регістру, але порівняння за замовчуванням чутливе до регістру. Щоб зробити цю роботу належним чином у всіх випадках, вам потрібно додати COLLATE NOCASE.

SELECT name FROM sqlite_master WHERE type='table' AND name='table_name' COLLATE NOCASE

33

Якщо ви отримуєте помилку "таблиця вже існує", внесіть зміни в рядок SQL, як показано нижче:

CREATE table IF NOT EXISTS table_name (para1,para2);

Таким чином ви можете уникнути винятків.



23

Якщо ви використовуєте fmdb , я думаю, ви можете просто імпортувати FMDatabaseAdditions та використовувати функцію bool:

[yourfmdbDatabase tableExists:tableName].

1
Переконайтесь, що ви імпортуєте "FMDatabaseAdditions.h", щоб скористатися цим методом, інакше вам буде цікаво, чому вони його видалили! :)
Чи буде

Хоча це може бути правильною відповіддю, питання стосується sqlite, а не конкретної бібліотеки певною мовою. Я думаю, що відповідь повинна полягати в наданні sql-коду, а не виклику до одного з методів бібліотеки
nacho4d

13

Наступний код повертає 1, якщо існує таблиця, або 0, якщо таблиці не існує.

SELECT CASE WHEN tbl_name = "name" THEN 1 ELSE 0 END FROM sqlite_master WHERE tbl_name = "name" AND type = "table"

1
Це все одно не поверне нічого, якщо таблиця не існує, оскільки умова де запобігає будь-який результат.
Девід Гаусман

10

Зауважте, що для перевірки існування таблиці в базі даних TEMP необхідно використовувати sqlite_temp_masterзамість sqlite_master:

SELECT name FROM sqlite_temp_master WHERE type='table' AND name='table_name';

9

Ось функція, яку я використав:

Дано об’єкт бази даних SQLD = db

public boolean exists(String table) {
    try {
         db.query("SELECT * FROM " + table);
         return true;
    } catch (SQLException e) {
         return false;
    }
}

1
Мені, на жаль, довелося використовувати це в додатку для Android, оскільки я виявив, що пристрої Samsung не використовують стандартну структуру таблиці sqlite_master, з якою працюють усі інші.
Ентоні Чуйнард

7

Використовуйте цей код:

SELECT name FROM sqlite_master WHERE type='table' AND name='yourTableName';

Якщо кількість повернутого масиву дорівнює 1, це означає, що таблиця існує. Інакше його не існує.


4
class CPhoenixDatabase():
    def __init__(self, dbname):
        self.dbname = dbname
        self.conn = sqlite3.connect(dbname)

    def is_table(self, table_name):
        """ This method seems to be working now"""
        query = "SELECT name from sqlite_master WHERE type='table' AND name='{" + table_name + "}';"
        cursor = self.conn.execute(query)
        result = cursor.fetchone()
        if result == None:
            return False
        else:
            return True

Примітка. Зараз це працює на моєму Mac з Python 3.7.1


Це виглядає більш чисто, ніж усі інші відповіді .. Дякую !!
Харша Вардан

Не працює для мене: доведеться стерти дужки {} навколо table_name, а потім його штраф.
Банан

1
Переконайтеся, що table_nameвін не надається від неперевіреного джерела (наприклад, введення користувача), інакше він буде вразливим до введення SQL. Завжди краще використовувати параметри замість методів маніпуляції текстом
astef

3

Використовуйте

SELECT 1 FROM table LIMIT 1;

щоб запобігти прочитанню всіх записів.


Це повертає NULL, якщо таблиця існує, але не має записів.
радіошпіль

Якщо таблиці не існує, вона видасть помилку. Злови це, і ти знаєш, що його не існує.
luckydonald

використання помилок як контролю потоку, як правило, не вважається найкращою практикою. Цього, мабуть, слід уникати.
Джефф Вуддар

3

Ви можете написати наступний запит, щоб перевірити наявність таблиці.

SELECT name FROM sqlite_master WHERE name='table_name'

Тут 'table_name' - це назва вашої таблиці, що ви створили. Наприклад

 CREATE TABLE IF NOT EXISTS country(country_id INTEGER PRIMARY KEY AUTOINCREMENT, country_code TEXT, country_name TEXT)"

і перевірити

  SELECT name FROM sqlite_master WHERE name='country'

6
Чим це відрізняється від вже прийнятої відповіді з 9 років тому?
Кевін Ван Дайк

3

Найнадійніший спосіб, який я зараз знайшов у C #, використовуючи останній nuget пакет sqlite-net-pcl (1.5.231), який використовує SQLite 3, такий:

var result = database.GetTableInfo(tableName);
if ((result == null) || (result.Count == 0))
{
    database.CreateTable<T>(CreateFlags.AllImplicit);
}

2

Використання простого запиту SELECT - на мою думку - досить надійне. Найбільше він може перевірити наявність таблиці у багатьох різних типах баз даних (SQLite / MySQL).

SELECT 1 FROM table;

Це має сенс, коли ви можете використовувати інший надійний механізм визначення того, чи вдався запит (наприклад, ви запитуєте базу даних через QSqlQuery у Qt ).


1

c ++ функція перевіряє db та всі додані бази даних на наявність таблиці та (необов'язково) стовпця.

bool exists(sqlite3 *db, string tbl, string col="1")
{
    sqlite3_stmt *stmt;
    bool b = sqlite3_prepare_v2(db, ("select "+col+" from "+tbl).c_str(),
    -1, &stmt, 0) == SQLITE_OK;
    sqlite3_finalize(stmt);
    return b;
}

Редагувати: нещодавно виявлено функцію sqlite3_table_column_metadata. Звідси

bool exists(sqlite3* db,const char *tbl,const char *col=0)
{return sqlite3_table_column_metadata(db,0,tbl,col,0,0,0,0,0)==SQLITE_OK;}

загальнодоступна статична булева таблицяExists (база даних SQLiteDatabase, String tableName) {return database.rawQuery ("SELECT name FROM sqlite_master WHERE type = 'table' AND name = '" + tableName + "'", null) .moveToFirst (); }
псевдонім

Дуже неефективний і ризикований спосіб, оскільки конкатенація рядків може закінчитися всім.
Андреа Моро

0

Це мій код для SQLite Cordova:

get_columnNames('LastUpdate', function (data) {
    if (data.length > 0) { // In data you also have columnNames
        console.log("Table full");
    }
    else {
        console.log("Table empty");
    }
});

А інший:

function get_columnNames(tableName, callback) {
    myDb.transaction(function (transaction) {
        var query_exec = "SELECT name, sql FROM sqlite_master WHERE type='table' AND name ='" + tableName + "'";
        transaction.executeSql(query_exec, [], function (tx, results) {
            var columnNames = [];
            var len = results.rows.length;
            if (len>0){
                var columnParts = results.rows.item(0).sql.replace(/^[^\(]+\(([^\)]+)\)/g, '$1').split(','); ///// RegEx
                for (i in columnParts) {
                    if (typeof columnParts[i] === 'string')
                        columnNames.push(columnParts[i].split(" ")[0]);
                };
                callback(columnNames);
            }
            else callback(columnNames);
        });
    });
}

0

Я думав, що я покладу свої 2 центи на цю дискусію, навіть якщо вона досить стара. Цей запит повертає скалярний 1, якщо існує таблиця, а 0 - інакше.

select 
    case when exists 
        (select 1 from sqlite_master WHERE type='table' and name = 'your_table') 
        then 1 
        else 0 
    end as TableExists

0

Таблиця існує чи ні в базі даних швидко

func tableExists(_ tableName:String) -> Bool {
        sqlStatement = "SELECT name FROM sqlite_master WHERE type='table' AND name='\(tableName)'"
        if sqlite3_prepare_v2(database, sqlStatement,-1, &compiledStatement, nil) == SQLITE_OK {
            if sqlite3_step(compiledStatement) == SQLITE_ROW {
                return true
            }
            else {
                return false
            }
        }
        else {
            return false
        }
            sqlite3_finalize(compiledStatement)
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.