![]() |
|
|||
WebMoney: WMZ Z294115950220 WMR R409981405661 WME E134003968233 |
Visa 4274 3200 2453 6495 |
SQLite предоставляет разработчикам свободу сохранить содержание в любом
нужном формате, независимо от заявленного типа данных колонки. Некоторые люди
считают эту особенность неприятной. Некоторые разработчики потрясены тем,
что возможно вставить текст в колонку, отмеченную INTEGER. Эта статья выступает в пользу гибких правил типа в SQLite. Детали относительно гибкой системы типов SQLite найдены в отдельном
документе. Вот быстрое резюме: Имена типов данных на определениях столбца дополнительные.
Определение столбца может состоять просто из имени столбца. Когда имена типов данных обеспечиваются, они могут быть примерно любым
текстом. SQLite пытается вывести предпочтительный тип данных для колонки на
основе имени типа данных в определении столбца, но предпочтенный тип данных
консультативный, не обязательный. Предпочтительный тип данных это
требование "близости колонки". Попытка предпринята, чтобы преобразовать поступающие данные в
предпочтительный тип данных колонки. Все базы данных SQL делают это.
Если это преобразование успешно, все хорошо. Но если неудачно,
вместо того, чтобы поднять ошибку, SQLite просто хранит содержание,
используя его оригинальный тип данных. Вышеупомянутое может привести к ситуациям, которые защитники твердой
типизации считают неудобными: Обратите внимание на то, что значение INTEGER или REAL
никогда не будут заканчивать тем, что были сохранены в столбце TEXT,
так как INTEGER или REAL не могут всегда преобразовываться в его
эквивалентное представление TEXT. Точно так же INTEGER никогда не будет
храниться в колонке REAL, потому что он будет всегда преобразовываться в
REAL. Но TEXT не всегда похож на INTEGER или REAL
и не может всегда преобразовываться.
BLOB тоже ни во что не может быть преобразован, и ничто иное не может
быть преобразовано в BLOB. Некоторые читатели спрашивают себя, "как это могло когда-либо быть
полезно?" Вот попытка ответить на тот вопрос:
Многим приложениям, особенно тем, которые используют SQLite в качестве
формата файла приложения, нужно место, чтобы
сохранить разные признаки, такие как изображения миниатюр (как BLOB),
короткие части текста (такие как имя пользователя), а также числа, дата и
JSON. Удобно составить единственную таблицу, чтобы
обращаться с этим хранением: Без гибкой типизации такая таблица должна была бы быть более сложной с
отдельными колонками для каждого возможного типа данных. Гибкая типизация
колонки "value" делает процесс концептуально более простым, более
эффективным и легче по доступу и обновлению. В Fossil version control system
у каждого хранилища есть таблица CONFIG, которая используется, чтобы
сохранить все виды параметров настройки с каждым возможным типом данных.
Определенный для пользователя конфигурационный файл для Fossil (~/.fossil)
является отдельной базой данных SQLite, которая содержит единственную
таблицу атрибутов, хранящую определенное для пользователя состояние
через все хранилища. Некоторые программы используют базу данных SQLite в качестве чистого
хранилища значения ключа, схема базы данных содержит единственную таблицу,
которая выглядит примерно так: Функции json_tree и
json_each, которые встроены в SQLite,
имеют столбец "value", который может считать значения типов
INTEGER, REAL или TEXT в зависимости от типа соответствующего
поля JSON. Например: Запрос выше возвращает три строки одной колонки со значениями
"integer", "real" и "text", соответственно. Аналитики иногда сталкиваются с файлами CSV, где некоторые колонки
содержат смесь целого числа, реального и текстовые данные.
У файлов CSV, которые получены из экспорта электронной таблицы Excel,
обычно есть эта черта. Импортируя такие "грязные данные" в базу
данных SQL, удобно гибко типизировать колонки, чтобы импортировать. Грязные данные не ограничиваются файлами CSV, выходящими из Excel,
конечно. Есть много источников данных, в которых единственная область могла
бы содержать соединение типов. Например, столбец данных мог бы содержать
число секунд с 1970 иногда или текстовую строку даты в других случаях.
Желательно очистить эти непоследовательные представления, но в то же время
удобно быть в состоянии сохранить все различные представления в той же самой
колонке промежуточной базы данных, в то время как очистка
идет полным ходом. SQLite начался как расширение TCL, которое позже стало самостоятельным.
TCL это динамический язык в том смысле, что программист не должен знать о
типах данных. Под капотом TCL отслеживает тип данных каждого значения, но
разработчику и пользователю программы TCL все похоже на последовательность.
Гибкая типизация естественно подходит к использованию с динамическими языками
программирования как TCL и другими, где вы не всегда можете предсказывать
заранее, какой тип данных переменная будет содержать.
Таким образом, когда необходимо сохранить значение
переменной в базу данных, имея базу данных, которая поддерживает гибкую
типизацию, хранение делается намного легче. У каждого движка базы данных SQL, кажется, есть свой собственный
уникальный набор поддержанных имен типов данных: То, что SQLite примет любое из этих имен как действительное имя типа и
позволит вам сохранить любой вид содержания в колонку, увеличивает
возможность того, что скрипт, написанный, чтобы работать на некотором другом
движке базы данных SQL, будет также работать в SQLite. Поскольку файл базы данных SQLite это
единственный файл на диске, некоторые приложения используют SQLite в качестве
формата файла приложения. Это означает, что
единственный экземпляр приложения в течение его жизни мог бы говорить с
сотнями или тысячами отдельных баз данных, каждая в отдельном файле.
Когда такие программы разовьются за годы, некоторым колонкам в основной
базе данных тонко изменят их значения.
Или могло бы быть желательно, чтобы существующий столбец мог служить
двум или больше целям. Это намного легче сделать, если у колонки есть
гибкий тип данных. Следующие воспринятые недостатки гибкой типизации
собраны от бесчисленных постов на Hacker News и Reddit и подобных форумах,
где разработчики обсуждают эти виды вещей.
Если можно думать о других причинах, почему гибкая типизация это
плохая идея, пожалуйста, свяжитесь с разработчиками SQLite или
опубликуйте пост на SQLite Forum
, чтобы идея могла быть добавлена к списку. Многие скептики гибкой типизации просто выражают шок и недоверие, не
предлагая объяснения для того, почему они думают, что гибкая типизация
это плохая идея. Можно предположить, что причина того, что им не нравится
гибкая типизация состоит в том, что это отличается от того, к
чему они привыкли. Да, гибкая типизация это новый образ мыслей о данных в базе данных SQL.
Но новый не значит плохой. Иногда, и я думаю особенно в случае гибкой
типизации, инновации приводят к улучшению. Это стало пунктом доктрины среди многих программистов, что лучшим способом
предотвратить прикладные ошибки является строгое принудительное присвоение
типов. Но я не нахожу доказательств в поддержку этого. Безусловно, строгое принудительное присвоение типов действительно помогает
предотвратить некоторые виды ошибок на языках низшего уровня как C и C++,
которые представляют модель, которая является близкой к
машинным аппаратным средствам. Но это, кажется, не имеет место для языков
более высокой абстракции, на которых все данные розданы в суперклассе
"Value" некоторого вида, который подклассифицируется для различных типов
данных низшего уровня. Когда все объект Value,
определенные типы данных прекращают быть важными. У TCL нет принудительного присвоения типов вообще. Класс "Value" в TCL
(Tcl_Obj) может содержать много различных типов данных, но это представляет
содержание программе и пользователю приложения как последовательность.
И у меня было много ошибок в тех программах TCL за эти годы.
Но я не вспоминаю случая, где ошибки, возможно, были бы пойманы жесткой
системой типов. Я также написал много кода С за 35 лет.
Я нашел, что система типов в C очень полезна при нахождении и предотвращении
проблем. Для Fossil Version Control System
, написанной на C, я даже осуществил дополнительные программы
статического анализа, которые сканируют исходный код Fossil
до компиляции, ища проблемы, которые компиляторы пропустят. Языковая модель SQL более высокоуровневая абстракция, чем C/C++.
В SQLite каждый элемент данных сохранен в памяти, как объект "sqlite3_value".
Есть подклассы этого объекта для последовательностей, целого числа, чисел с
плавающей запятой, blob и других представлений. Все роздано в языке SQL,
осуществленном SQLite как объекты "sqlite3_value",
таким образом, базовый тип данных действительно не имеет значения.
Я никогда не находил, что принудительное присвоение типов полезно на таких
языках, как TCL и SQLite, у которых есть единственный суперкласс "Value",
чтобы представлять любой элемент данных. Fossil делает широкое применение
SQLite в его внедрении. Были многие ошибки в Fossil,
но я не могу вспомнить ошибку, которая, возможно, была бы предотвращена
принудительным присвоением типов в SQLite. Некоторые ошибки языка C,
возможно, были пойманы лучшим принудительным присвоением типов, но
не ошибки SQL. На основе десятилетий опыта я отклоняю тезис, что принудительное
присвоение типов помогает предотвратить прикладные ошибки.
Я буду принимать и верить немного измененному тезису:
принудительное присвоение типов помогает предотвратить прикладные ошибки
на языках, которые испытывают недостаток в единственном суперклассе
"Value". Но у SQLite действительно есть единственный
суперкласс "sqlite3_value". Некоторые люди утверждают, что, если у вас есть строгие ограничения на
схему и особенно строгое осуществление типов данных колонки, это поможет
препятствовать тому, чтобы неправильные данные были добавлены к базе данных.
Это не верно. Верно, что принудительное присвоение типов могло бы помочь
препятствовать тому, чтобы в высшей степени неправильные данные
вошли в систему. Но принудительное присвоение типов не препятствует тому,
чтобы тонко неправильные данные были зарегистрированы. Так, например, жесткое принудительное присвоение типов может успешно
предотвратить имя клиента (текст) от того, чтобы быть вставленным в колонку
целого числа Customer.creditScore.
С другой стороны, если та ошибка происходит, очень легко определить
проблему и найти все затронутые строки. Но принудительное присвоение типов не
поможет в предотвращении ошибки, где фамилия и имя полностью изменены, так
как оба текстовые поля. Несколько Десятилетий назад, я работал над командой, где была женщина по
имени "Merritt Tracy". "Merritt" это имя, "Tracy" ее фамилия.
Она сообщила, что провела немалое количество времени и энергии, пытаясь
исправлять базы данных, в которых была "Tracy" как имя и
"Merritt" в качестве фамилии. Подавляя легко обнаруживаемые ошибки и проходя только через
труднообнаруживаемые ошибки, принудительное присвоение типов может на самом
деле сделать более трудным поиск и исправление ошибки. Ошибки данных имеют
тенденцию группироваться. Если у вас будет 20 различных источников данных,
большинство ошибок данных будет обычно прибывать всего от 2 или 3 из тех
источников. Присутствие вопиющих ошибок (таких как текст в колонке целого
числа) является удобным сигналом дальнего обнаружения,
что что-то неправильно. Источник проблемы может быть прослежен быстро, и
дополнительное исследование относилось к источнику вопиющих ошибок, таким
образом надо надеяться, также фиксируя тонкие ошибки также.
Когда вопиющие ошибки подавлены,
вы теряете важный сигнал, который помогает вам обнаружить и
исправить тонкие ошибки. Ошибки данных неизбежны. Они произойдут независимо от того, сколько
проверок типа сделано. Принудительное присвоение типов может поймать
только маленькое подмножество тех случаев, самые очевидные случаи.
Это не делает ничего, чтобы помочь найти и зафиксировать более тонкие
случаи. И, подавляя сигнал, что источники данных проблематичны, это
может иногда затруднить отлов ошибок. Поскольку SQLite менее строг и позволяет вам делать больше вещей,
скрипты SQL, которые работают с другими ядрами базы данных, будут также
обычно работать с SQLite, но скрипт, написанный первоначально для SQLite, не
мог бы работать с более строгими ядрами базы данных. Это может вызвать
проблемы, когда разработчики используют SQLite для разработки прототипа и
тестирования и затем мигрируют их приложение к более строгому движку
SQL для развертывания. Если приложение (неумышленно) использовало в своих
интересах гибкую типизацию, доступную в SQLite, то это потерпит
неудачу, когда мигрирует. Люди используют эту проблему, чтобы утверждать, что SQLite должен быть
более строгим с типами данных. Но вы могли столь же легко изменить к лучшему
тот аргумент и сказать, что другие ядра базы данных должны быть более гибкими
относительно типов данных. Применение работало правильно под SQLite, до
миграции, в конце концов. С SQLite version 3.37.0 (2021-11-27) SQLite поддерживает этот стиль
развития, используя таблицы STRICT. Если вы находите реальный случай, где таблицы STRICT
предотвратили бы ошибку в применении, пожалуйста, повесьте объявление на
SQLite Forum, чтобы мы могли
добавить вашу историю к этому документу. Если гибкая типизация в базе данных SQL это новое понятие,
я поощряю вас дать ей шанс. Это, вероятно, не вызовет у вас никаких проблем,
и это могло бы сделать вашу программу более простой и легкой в разработке и
поддержке. Я думаю, что, даже если вы скептически настроены сначала, если вы
просто дадите шанс гибкой типизации, вы в конечном счете поймете, что это
лучший подход и начнете поощрять других поставщиков
базы данных поддерживать, по крайней мере, тип данных ANY, если не полную
гибкость в стиле SQLite. Большую часть времени гибкая типизация
не имеет значения, потому что колонка хранит единственный четко определенный
тип. Но иногда вы будете натыкаться на ситуации, где наличие гибкой системы
типов делает решение вашей задачи легче.
Choose any three.
1. Введение
2. О гибкой типизации
Тип данных колонки Типы, позволенные в этой колонке
INTEGER INTEGER, REAL, TEXT, BLOB
REAL REAL, TEXT, BLOB
TEXT TEXT, BLOB
BLOB INTEGER, REAL, TEXT, BLOB
3.
Случаи, где гибкая типизация полезна
3.1. Таблицы атрибутов
CREATE TABLE attribute(name TEXT PRIMARY KEY, value) WITHOUT ROWID;
CREATE TABLE storage(name TEXT PRIMARY KEY, value ANYTHING);
3.2. Столбец "value", произведенный от виртуальных таблиц json_tree
SELECT typeof(value) FROM json_each('{"a":1,"b":2.5,"c":"hello"}');
3.3. Хранение для грязных данных
3.4.
Динамические языки программирования
3.5.
Поперечная совместимость имени типа данных
3.6. Переназначение неиспользованных или вышедших из употребления
колонок в унаследованных базах данных
4. Воспринятые недостатки гибкой типизации
4.1.
Мы никогда не делали этого раньше
4.2. Жесткое принудительное присвоение типов помогает
предотвратить прикладные ошибки
4.3.
Жесткое принудительное присвоение типов предотвращает загрязнение данных
4.4.
Другие базы данных SQL не работают так
5.
Если вы настаиваете на жестком принудительном присвоении типов...
6. Свобода типов