Есть большая разница в скорости запросов между первичными ключами CHAR_INT и INT? - PullRequest
0 голосов
/ 15 мая 2019

Я знаю, что обычно рекомендуется использовать поле INTEGER для вашего первичного ключа, но, к сожалению, из-за API, с которым я работаю, у меня может быть только первичный ключ в формате: CHAR_INT (пример: ABC_12345 ).

У меня будет много данных (+1 миллиард записей), и скорость запросов и вставок является приоритетом, окажет ли использование первичный ключ CHAR_INT большое влияние на скорость? Или это относительно незначительно?

Кроме того, будет ли создание числового идентификатора для части строки CHAR более эффективным? Итак, используя предыдущий пример: ABC_12345 станет чем-то вроде 1_12345. Я знаю, что они оба будут строками, просто интересно, есть ли какая-то эффективность с использованием только чисел.

Я использую SQLite.

Спасибо!

Ответы [ 3 ]

5 голосов
/ 15 мая 2019

Насколько мне известно, нет встроенного типа данных "CHAR_INT".

Тем не менее, SQLite достаточно гибок при вводе текста и позволяет любой строке быть именем типа. SQLite не является строго типизированным, поэтому значение представляется в виде строки.

Индексы по номерам более эффективны. Одна важная причина в том, что числа имеют фиксированную длину. Строки имеют переменную длину, что увеличивает накладные расходы при сохранении значений ключа в индексе. Другая причина в том, что аппаратное обеспечение лучше поддерживает числовые сравнения. Сравнение строк становится намного сложнее, если учитывать наборы символов и сопоставления.

Тем не менее, накладные расходы на поиск и обслуживание индекса на самом деле довольно малы по сравнению с преимуществами использования индекса. Так что я бы не беспокоился, что в индексе есть только строки. Однако я бы больше беспокоился об инструменте, который налагает такие ограничения. Вы должны быть в состоянии выбрать ключи, которые вы хотите в ваших таблицах.

4 голосов
/ 15 мая 2019

Тип (сходство типов) с одним исключением не имеет никакого значения.

Исключением является the_column_name INTEGER PRIMARY KEY (с или без AUTOINCREMENT), которое определяет столбец как псевдоним столбца rowid .INT PRIMARY KEY этого не делает.

так что the_column_name CHAR_INT PRIMARY KEY, the_column_name INT CHAR PRIMARY KEY или даже the_column_name INT PRIMARY KEY на самом деле одинаковы, the_column_name RUMPLESTILTSKIN PRIMARY KEY может даже использоваться (хотя последний будет иметь различное сходство типов).

Это правила, которые определяютродство типов.Есть 5. Правило с наивысшим приоритетом: если у типа есть INT, то сродство с типом - INTEGER.RUMPLESTILTSKIN как тип пропускает все правила, кроме последнего, т. Е. Если ни одно из предыдущих правил не применимо, то сходство типов равно NUMERIC.

3.1.Определение сродства столбца

Сходство столбца определяется объявленным типом столбца в соответствии со следующими правилами в указанном порядке:

Если объявленный тип содержитстрока "INT", тогда ей присваивается сходство INTEGER.

Если объявленный тип столбца содержит какие-либо из строк "CHAR", "CLOB" или "TEXT", то этот столбец имеет сходство TEXT.Обратите внимание, что тип VARCHAR содержит строку «CHAR» и, таким образом, ему присваивается сходство TEXT.

Если объявленный тип для столбца содержит строку «BLOB» или если тип не указан, столбец имеет сходство BLOB.

Если объявленный тип для столбца содержит какие-либо из строк «REAL», «FLOA» или «DOUB», тогда столбец имеет РЕАЛЬНОЕ сходство.

В противном случае сходство является ЦИФРОВЫМ.

Обратите внимание, что порядок правил для определения соответствия столбцов важен.Столбец с объявленным типом «CHARINT» будет соответствовать обоим правилам 1 и 2, но первое правило имеет приоритет, и поэтому сходство столбцов будет INTEGER.

Типы данных в SQLite

Сказать, что сходство типов не определяет способ хранения данных.Каждый столбец хранится в соответствии с классом хранения, который зависит от хранимых данных.

Ноль хранится как ноль, строка чисел (заключенная или не в виде строки) как целое число.Короче говоря, данные будут храниться в соответствии с определением SQLite, и SQlite будет пытаться хранить данные настолько эффективно, насколько это возможно, и в как можно меньшем пространстве, вплоть до байта, являющегося наименьшей из сохраняемых единиц.

Рассмотримследующее: -

DROP TABLE IF EXISTS mytable1;
DROP TABLE IF EXISTS mytable2;
DROP TABLE IF EXISTS mytable3;
CREATE TABLE IF NOT EXISTS mytable1 (c1 CHAR_INT PRIMARY KEY); 
CREATE TABLE IF NOT EXISTS mytable2 (c1 INT PRIMARY KEY); 
CREATE TABLE IF NOT EXISTS mytable3 (c1 RUMPLEstiltSkin PRIMARY KEY);
-- INSERT INTO mytable1 VALUES(12345),('12345'),('a_12345'),('1_12345'),(x'0102030405'); -- fails due to unique constraint 12345 and '12345' are the same 
-- INSERT INTO mytable2 VALUES(12345),('12345'),('a_12345'),('1_12345'),(x'0102030405'); -- fails due to unique constraint 12345 and '12345' are the same 
-- INSERT INTO mytable3 VALUES(12345),('12345'),('a_12345'),('1_12345'),(x'0102030405'); -- fails due to unique constraint 12345 and '12345' are the same 
INSERT INTO mytable1 VALUES(12345),('54321'),('a_12345'),('1_12345'),(x'0102030405');
INSERT INTO mytable2 VALUES(12345),('54321'),('a_12345'),('1_12345'),(x'0102030405');
INSERT INTO mytable3 VALUES(12345),('54321'),('a_12345'),('1_12345'),(x'0102030405');
SELECT c1, typeof(c1) FROM mytable1;
SELECT c1, typeof(c1) FROM mytable2;
SELECT c1, typeof(c1) FROM mytable3;
  • Закомментированные INSERTS (если они не закомментированы и выполнены) завершаются неудачно с УНИКАЛЬНЫМ конфликтом, поскольку SQLite считает 12345 таким же, как и 12345.

функция typeof возвращает тип столбца (тип хранения НЕ сродство к столбцу)

Результаты выглядят так: -

enter image description here enter image description here enter image description here

Использование чего-либо кроме INTEGER PRIMARY KEY (есть некоторые производные) и, таким образом, псевдоним rowid равен

  • примерно вдвое быстрее
  • имеет два индекса: rowid (если таблица не определена как ROWID) и первичный ключ.

    • Поиск записи с определенным rowid или для всех записей с rowid вуказанный диапазон примерно вдвое быстрее, чем аналогичный поиск, выполняемый путем указания любого другого PRIMARY KEY или индексированного значения. ROWID и INTEGER PRIMARY KEY

  • обработка чисел в отличие от строк займет больше места и, следовательно, сократит объем данных, которые могутбудет храниться в буфере, таким образом, это окажет некоторое влияние.

Поиск по индексу выполняется относительно быстро, по сравнению с самими данными относительно мало данных и сами данныетолько чтение.

Возможно, рассмотрим следующее: -

DROP TABLE IF EXISTS mytable1;
DROP TABLE IF EXISTS mytable2;
DROP TABLE IF EXISTS mytable3;
CREATE TABLE IF NOT EXISTS mytable1 (pk INT PRIMARY KEY, name TEXT); 
CREATE TABLE IF NOT EXISTS mytable2 (pk CHAR_INT PRIMARY KEY, name TEXT); 
CREATE TABLE IF NOT EXISTS mytable3 (pk INT PRIMARY KEY, name TEXT) WITHOUT ROWID; 

INSERT INTO mytable1
    WITH RECURSIVE cte1(a,b) AS (
            SELECT 'ABC_'||CAST(abs(random()) AS TEXT),'some data' UNION ALL 
            SELECT DISTINCT (substr(a,1,4))||CAST(abs(random()) AS TEXT),'some data' FROM cte1 LIMIT 1000000
        )
    SELECT * FROM cte1
;

INSERT INTO mytable2
    WITH RECURSIVE cte1(a,b) AS (
            SELECT '1_'||CAST(abs(random()) AS TEXT),'some data' UNION ALL 
            SELECT DISTINCT (abs(random()) % 100)||'_'||CAST(abs(random()) AS TEXT),'some data' FROM cte1 LIMIT 1000000
        )
    SELECT * FROM cte1
;
INSERT INTO mytable3 SELECT * FROM mytable1;

SELECT * FROM mytable1 WHERE name LIKE('%me data%');
SELECT * FROM mytable2 WHERE name LIKE('%me data%');
SELECT * FROM mytable3 WHERE name LIKE('%me data%');

SELECT * FROM mytable3 WHERE name LIKE('%me data%');
SELECT * FROM mytable1 WHERE name LIKE('%me data%');
SELECT * FROM mytable2 WHERE name LIKE('%me data%');

SELECT * FROM mytable2 WHERE name LIKE('%me data%');
SELECT * FROM mytable3 WHERE name LIKE('%me data%');
SELECT * FROM mytable1 WHERE name LIKE('%me data%');

Это создает 3 перестановки с 1 000 000 строк

  • mytable1 имеет первичный ключ как ABC _ ???? например : -

    • enter image description here
  • mytable2 имеет первичный ключ как ?? _ ???? например : -

    • enter image description here
  • mytable3 является копией mytable1 НО таблица была определена с использованием БЕЗ ROWID

Задержка

ВЫБОРЫ для всех 3 довольно близки (многократные выборки выполнены и в другом порядке для выравнивания кэширования). Сообщения, которые включают время: -

SELECT * FROM mytable1 WHERE name LIKE('%me data%')
> OK
> Time: 0.672s


SELECT * FROM mytable2 WHERE name LIKE('%me data%')
> OK
> Time: 0.667s


SELECT * FROM mytable3 WHERE name LIKE('%me data%')
> OK
> Time: 0.702s


SELECT * FROM mytable3 WHERE name LIKE('%me data%')
> OK
> Time: 0.7s


SELECT * FROM mytable1 WHERE name LIKE('%me data%')
> OK
> Time: 0.675s


SELECT * FROM mytable2 WHERE name LIKE('%me data%')
> OK
> Time: 0.673s


SELECT * FROM mytable2 WHERE name LIKE('%me data%')
> OK
> Time: 0.676s


SELECT * FROM mytable3 WHERE name LIKE('%me data%')
> OK
> Time: 0.709s


SELECT * FROM mytable1 WHERE name LIKE('%me data%')
> OK
> Time: 0.676s
  • Я полагаю, что mytable3 выполняет небольшую попытку, потому что сканирование (в данном случае) выполняется на ПЕРВИЧНОМ КЛЮЧЕ, а не на rowid, который подходит / предпочтителен для двух других.

В дополнение к предыдущим ссылкам, вы также можете посмотреть: -

1 голос
/ 15 мая 2019

Sqlite имеет два типа таблиц.

По умолчанию Таблица ROWID .Таблицы Rowid имеют вид B * -Tree , с 64-битным целым числом со знаком в качестве первичного ключа (rowid).Если в столбце есть один столбец INTEGER PRIMARY KEY , в этом столбце используется псевдоним для идентификатора строки.Любой другой тип PRIMARY KEY или составные первичные ключи из двух или более столбцов является просто уникальным индексом.

Таким образом, ваш столбец CHAR_INT (Sqlite очень прощает за то, что требуетсядля типов столбцов: это просто подсказка о том, как попытаться сохранить и сравнить значения, хранящиеся в этом столбце, а не фактический тип), по правилам Sqlite, имеет целое число affinity , нопоскольку такие вещи, как ABC_123, не могут быть преобразованы без потерь в целые числа, они сохраняются в виде строк.Вставка строки означает обновление как основной таблицы, так и индекса первичного ключа (и любых других индексов, конечно).Поиск строки по ключу предполагает сначала поиск ее соответствующего rowid в индексе, а затем поиск этой строки в основной таблице.С другой стороны, оба поиска используют O(log N) бинарный поиск.

Другой тип таблицы - БЕЗ ROWID .Эти таблицы используют ту же простую структуру данных B-Tree, которая используется индексами, и используют первичный ключ таблицы, независимо от ее типа или количества столбцов, в качестве истинного первичного ключа.Для вставок требуется обновить только одну таблицу (плюс дополнительные индексы, разумеется), поиск должен выполнять поиск только в одной таблице, поэтому он может быть быстрее и занимать меньше места на диске, если ваш первичный ключ не является INTEGER.

Какой из них в итоге окажется лучше, зависит от множества факторов, таких как, сколько других индексов используется с таблицей, сколько данных хранится в строке, запросы выполняются в таблицах и многое другое.Документация предлагает, помимо прочего, создание баз данных с таблицами WITHOUT ROWID и без них и сравнительный анализ, чтобы увидеть, что больше подходит для конкретного использования.

...