Что мне следует учитывать при использовании в дизайне базы данных для нового приложения, которое должно поддерживать наиболее распространенные системы реляционных баз данных (SQL Server, MySQL, Oracle, PostgreSQL ...)?
Стоит ли даже усилий? Какие подводные камни?


Или используйте технологию сохранения, такую как hibernate / nHibernate, которая абстрагирует различия между разными базами данных.
Имена полей и таблиц должны быть короткими (<30 символов) и нечувствительными к регистру. например TABLE_NAME и FIELD_NAME
Переносимость на 95% почти так же хороша, как и переносимость, если вы можете изолировать платформенно-зависимый код на определенном уровне. Так же, как Java была описана как "Напиши один раз, тест везде", все равно нужно протестировать приложение на каждой платформе, на которой вы собираетесь его запускать.
Если вы осмотрительны с кодом вашей платформы, вы можете использовать переносимый код для 95 +% функциональности, которая может быть адекватно реализована переносимым способом. Остальные части, которые необходимо выполнить в хранимой процедуре или другой платформенно-зависимой конструкции, могут быть встроены в серию платформо-зависимых модулей стандартного интерфейса. В зависимости от платформы вы используете модуль, соответствующий этой платформе.
В этом разница между «Тестировать везде» и «Создавать модули для конкретных платформ и тестировать везде». В любом случае вам нужно будет протестировать на всех поддерживаемых платформах - от этого никуда не деться. Дополнительная сборка относительно незначительна и, вероятно, меньше, чем создание действительно запутанной архитектуры, чтобы попытаться сделать эти вещи полностью переносимыми.
В настоящее время я поддерживаю Oracle, MySQL и SQLite. И если честно, это тяжело. Вот некоторые рекомендации:
Стоит ли ... ну как. С коммерческой точки зрения это того стоит для приложений корпоративного уровня, но для блога или, скажем, веб-сайта вы могли бы также придерживаться одной платформы, если можете.
Прокатить собственный автоинкремент не так уж и сложно
+1, чтобы избежать процедур и использовать достойную тестовую среду, чтобы убедиться, что ваше приложение действительно зависит от является БД; Я бы не согласился с использованием триггеров и предпочел бы использовать стратегию распределения идентификаторов, которая сама по себе не зависит от БД.
Люди часто говорят вам один ответ: не использовать sql, специфичный для базы данных, а просто код в соответствии со стандартами ansi. Они часто говорят, что разговаривают с базой данных только через сохраненные процедуры, чтобы абстрагироваться от любого sql. Это неправильные ответы, которые приводят только к боли. Кодирование под «стандартный» sql практически невозможно, потому что у каждого поставщика такие разные интерпретации.
Что вам нужно, так это иметь какой-то уровень устойчивости базы данных, который абстрагирует различия между базами данных (извините, Джонсток, это почти именно то, что вы сказали). Для каждой платформы существует множество других ORM и аналогичных продуктов,
Также сложно кодировать стандартный SQL, учитывая, что большинство, если не все коммерческие СУБД не поддерживают 100% функций стандарта ANSI. Более практичный ответ: кодируйте подмножество SQL, которое поддерживается на всех интересующих вас платформах, т.е. найдите пересечение, в смысле теории множеств, их реализаций.
Проблема в том, что, Джей, как только вы нашли подмножество функций, которые являются общими для поставщиков, все, что вам остается, это очень простые операторы SELECT, INSERT, UPDATE.
@Crag, SQL следует считать первоклассным в вашем приложении. Скрыть это в клиенте и коде ORM - это кошмар, который может подтвердить большинство. Абстрагирование функций БД с помощью процедур и представлений - это почти универсальная концепция.
На вашем месте я бы хорошенько подумайте о окупаемости ваших инвестиций здесь.
Всегда звуки - это отличная идея - иметь возможность подключиться к любой серверной части или изменить ее, когда захотите, но, по моему опыту, это очень редко случается в Реальном мире.
Может оказаться, что вы можете покрыть 95% своих потенциальных клиентов, поддерживая только Oracle и SQL Server (или MySQL и SQL Server, или ... и т. д.).
Изучите, прежде чем идти дальше, и удачи!
Вы правы, это редко случается с приложениями корпоративного типа, но это довольно распространено для программного обеспечения с термоусадочной пленкой. Например, в настоящее время я разрабатываю инструмент ETL, который мы используем на многих клиентских сайтах. Он должен работать с Oracle и SQL Server и, возможно, с другими в будущем.
Я собираюсь обобщить ответ Джонстока: 1) Не использовать хранимые процедуры и 2) Не использовать SQL, зависящий от поставщика, и добавлять к нему.
Вы также спросили: «Стоит ли это усилий?». Я бы сказал ... может быть. Я написал программу отслеживания ошибок с открытым исходным кодом, BugTracker.NET, основанную на SQL Server. Есть много разработчиков, которые просто не попробуют, потому что им нравится придерживаться технологий, которые им удобны. И, когда я подумывал о запуске службы хостинга, я заметил, что выделенные виртуальные серверы Linux намного дешевле, чем услуги Windows (не виртуальные). Теоретически я мог бы запустить C# в монофоническом режиме, но мой SQL настолько специфичен для SQL Server (хотя я не использую хранимые процессы), что для его переноса потребуются огромные усилия.
Если вы ориентируетесь на бизнес / корпоративный рынок, вы обнаружите, что некоторые магазины строго ориентированы на Oracle или строго на SQL Server, и что ваше приложение может быть исключено на ранних этапах конкурса в зависимости от технологии, которую оно использует.
Так что, может быть, для вас важно быть открытым. Что это за приложение? Кто им воспользуется?
Вы также спросили: «Что такое птифоллы». Не тестирование по мере продвижения. Если вы планируете поддерживать перечисленные вами 4 дБ, то вам следует проводить тестирование с ними на раннем этапе и часто, а не просто нацеливаться на один, думая, что его будет легко преобразовать в другие. К тому времени вы можете оказаться в архитектурном тупике.
Изучите заранее наименьший общий знаменатель для типов данных. Например, в SQL Server есть целое число, а в Oracle - число.
Я понимаю другие ответы здесь, но почему бы не использовать хранимые процедуры? Неужели так, чтобы логика не скрывалась?
Сравните и сопоставьте, скажем, T-SQL (Sybase, MS SQL) и PL / SQL (Oracle). Тогда ты узнаешь. :) Но если подумать, с годами я стал ненавидеть все, что напоминало бизнес-логику, также скрываемую в БД ...
См. Мой комментарий ниже - у них есть место, когда вы вынуждены использовать конструкции, специфичные для db.
Короткий ответ - придерживаться стандартных или близких к стандартным функциям. Более подробно это означает:
Избегайте всего, что использует процедурный язык базы данных (хранимые процедуры или триггеры), поскольку именно здесь проявляются огромные различия между системами. Возможно, вам придется использовать их для имитации некоторых функций, но не используйте их для создания собственных функций.
Отделяйте последовательности полей с автоинкрементом от самих полей. Это будет выглядеть немного принудительно для MSSQL, но будет реализовано чисто в Oracle, DB / 2 и т. д. Без каких-либо исправлений эмуляции.
Поля char и varchar должны быть меньше минимального максимального размера для набора движков, на который вы нацеливаетесь.
Когда вы пишете запросы, используйте полный синтаксис JOIN и заключите JOIN в скобки, чтобы каждое соединение происходило между одной таблицей и выражением в квадратных скобках.
Сохраняйте логику обработки даты в коде, а не в запросах, поскольку многие функции даты выходят за рамки стандарта. (Например: если вы хотите получить данные за последние две недели, вычислите дату две недели назад в коде и используйте ее в запросе.)
Кроме того, прилагаемые усилия не должны быть слишком пугающими, так что, возможно, оно того стоит.
В 2001 году я работал над продуктом, который должен был поддерживать Oracle 8, MS SQL Server 2000 и MS Jet 3.51 (он же Access97). Теоретически мы могли бы нанять специалистов для каждого из этих продуктов и провести тестирование, которое гарантировало бы получение одинаковых результатов. На практике наблюдается тенденция к наименьшему общему знаменателю.
Один из подходов заключался в создании связанных таблиц в Access / Jet для Oracle и SQL Server с последующим написанием исключительно Jet SQL. Проблема здесь в том, что синтаксис Jet SQL очень ограничен.
Другой подход (обычно применяемый даже в системах, которые когда-либо использовали только один продукт СУБД!) - это попытаться выполнить больше работы, которая действительно должна выполняться во внешнем интерфейсе, то есть вещи, которые должны быть областью СУБД. Проблема здесь в том, что это часто катастрофично с точки зрения целостности данных. Я уверен, что вы знаете ситуацию: приложение должен воздерживается от записи недопустимых данных, но без ограничений в самой СУБД оно широко открыто для ошибок приложения. Кроме того, есть пользователи, которые знают, как подключаться к данным через Excel, SQL Management Studio и т. д., И тем самым полностью обходить приложение, которое должно обеспечивать целостность данных ...
Лично я обнаружил, что все чаще пишу код по скользящей шкале того, что, как я только позже обнаружил, было названо «переносимостью». В идеале, в первую очередь, это «ванильный» код, понятный всеми СУБД, которые мы поддерживаем, и при этом я обнаружил стандарты SQL-89 и SQL-92. Затем был код SQL, который можно было легко транслировать (возможно, используя код) для каждой СУБД, например. Oracle использовал этот ужасный инфиксный синтаксис внешнего соединения, но концепция внешнего соединения присутствовала; Oracle и SQL Server использовали SUBSTRING, но Jet требовал, чтобы ключевое слово было MID $; и т. д. Наконец, есть вещи, которые просто должны быть специфичными для реализации, которых, очевидно, следует избегать, если это вообще возможно, при этом уделяя должное внимание целостности данных, функциональности и производительности.
К счастью, за прошедшие годы продукты приблизились к стандартам ANSI SQL (кроме Jet, которая была объявлена устаревшей MS, теперь поддерживается только командой MS Access, по-видимому, за счет сокращения основных функций, таких как безопасность и репликация). Так что я сохранил привычку писать стандартный SQL там, где это возможно.
Принимая во внимание множество хороших и разумных ответов здесь, я бы также добавил, что что-то вроде ActiveRecord миграции (из Ruby On Rails, но вы можете просто использовать библиотеку) может быть полезно. Он абстрагирует такие вещи, как создание / изменение таблиц, соответствующие типы столбцов, более простое управление индексами и (до определенной степени) упорядочение на довольно простом языке описания.
Хранимые процедуры и триггеры в значительной степени игнорируются, но если вы переходите на кроссплатформенность, такая функциональность, вероятно, в любом случае должна быть на уровне кода.
Из любопытства я переключался между Oracle, MS SQL, MySQL и SQLite с тем же набором миграций, и худшая проблема, с которой я столкнулся, заключалась в том, что я обнаружил, что мне нужно убедиться, что имена моих столбцов и таблиц не входят в объединение списков зарезервированных слов в БД.
Правило 1: не используйте особенности базы данных
Правило 2: не используйте хранимые процедуры.
Правило 3: Если вы нарушите Правило 1, то нарушите и Правило 2.
Было много комментариев по поводу неиспользования хранимых процедур. Это связано с тем, что синтаксис / семантика сильно различаются и поэтому переносить их сложно. Вам не нужны кучи кода, который вам придется переписывать и повторно тестировать.
Если вы решите, что вам действительно нужно использовать специфические функции базы данных, вам следует скрыть эти детали за хранимой процедурой. Вызов хранимых процедур из разных баз данных довольно похож. Внутри процедуры, написанной на PL / SQL, вы можете использовать любые конструкции Oracle, которые сочтете полезными. Затем вам нужно написать эквивалент для других целевых баз данных. Таким образом, части, относящиеся к базе данных, находятся только в этой базе данных.
Если возможно, я бы этого не делал. Я работал с несколькими из этих баз данных в прошлом, и они были ужасно медленными (один особенно болезненный пример, о котором я могу вспомнить, было приложение центра обработки вызовов, которое занимало десять минут, чтобы перейти с одного экрана на другой в напряженный день) из-за необходимости писать общий sql и не использовать настройку производительности, которая лучше всего подходит для конкретной серверной части.
В дополнение к этот ответ и по общему правилу не позволяйте серверу генерировать или вычислять данные. Всегда отправляйте прямые инструкции SQL, за исключением формул. Не используйте свойства значений по умолчанию (или делайте их простыми, а не формулами). Не используйте правила проверки На стороне клиента должны быть реализованы как значения по умолчанию, так и правила проверки.
Я не голосую против, но вы сильно теряете скорость и увеличиваете сложность, перемещая логику из БД. Давайте не будем предлагать игнорировать все функции БД (это были 1990-е годы); т.е. валидацию можно легко спроектировать на базовом SQL.
Первое, что следует учитывать, это то, что стоимость независимой работы ниже, чем зависит от базы данных. Я думаю, что иногда это важно для некоторых продуктов, которые хотят предоставить выбор клиентам, но они теряют множество функций базы данных (это означает, что код нужно писать снова).
Для крупных клиентов (большие приложения) они должны быть полностью зависимы от базы данных. Для небольших настроек действительно проблема иметь Oracle XE и MySQL на одном сервере (или двух).
На самом деле, я предпочитаю использовать более одной базы данных и чтобы приложение знало, какая база данных является «абстрактным» кодом.
ИМО, это зависит от типа разрабатываемого вами приложения:
Для случая 1 просто выберите одну СУБД, которая лучше всего соответствует вашим потребностям, и кодируйте ее, используя всю мощь всех ее проприетарных функций.
В случае 2 вы, вероятно, обнаружите, что вполне возможно придерживаться общего подмножества операций, поддерживаемых всеми СУБД, которые вы намереваетесь поддерживать.
Если бы у этого были причины для каждого, это было бы полезно.