Экспериментировал на досуге. Может кому пригодится. Небольшое введение: Существует множество СУРБД/СУБД и все они используют для своей работы SQL и собственные агрегатные функции. Но синтаксис SQL и агрегатные функции в СУБД от разных производителей немного отличаются друг от друга. Вследствие чего запросы, составленные для одной СУБД, могут не работать в другой. Данная особенность усложняет разработку ПО, ориентированного на множество СУБД. Проблемы, возникающие при разработке ПО, ориентированного на использование множества СУБД: 1. “Сложность” составления универсальных запросов, одинаково работающих в разных СУБД, а иногда и невозможность составления таковых. 2. Нагромождение условных операторов в коде программы при невозможности написания универсального запроса для всех СУРБД на которые ориентированна программа. 3. Существенное замедление работы приложения при использовании инструментов, работающих по принципу: • Все запросы составляются с учётом использования лексем некого универсального SQL. • Инструмент, получив некий универсальный запрос, анализирует его и составляет на его основе запросы, удовлетворяющие требования к синтаксису запросов той или иной СУБД. Существуют и другие проблемы, как существуют и другие варианты решений по обеспечению работы приложения с разными СУРБД. Разумеется каждое решение обладает своими достоинствами и недостатками. *** Задачка: Разработать концепцию и прототип решения для разработки ПО, ориентированного на работу с множеством СУБД. Условия: 1. Простота использования 2. Высокая скорость 3. Отсутствие ограничений на работу с СУРБД (то-есть создать иллюзию, как будто работаешь напрямую с функциями PHP - это основная идея) 4. Высокая гибкость решения 5. Легкая модифицируемость Концепция: Решение должно создавать иллюзию, что идёт работа напрямую с функциями по работе с СУБД языка PHP. При этом автоматизируются некоторые функции. Прототип оформлен в виде класса. Написан на PHP. Основные принципы: 1. Класс обеспечивает автоматическое подключение и отключение от сервера БД, данная функциональность реализована в конструкторе и деструкторе соответственно. Вследствие чего работа приложения возможна только при использовании PHP5. 2. Запросы к БД и экранирование символов осуществляются при использовании соответствующих методов класса. Выбор типа сервера БД происходит автоматически в зависимости от конфигурации. 3. Все запросы, используемые приложением, размещаются в отдельных файлах. То-есть для СУРБД MySQL запросы находятся в файле “mysql_queries.php”, для PostgreSQL в файле “pgsql_queries.php” соответственно. Подключение данных файлов производится в теле самой программы при помощи условного оператора. Тем самым написав один раз программу с использованием данного класса, в случае необходимости её портирования на новую СУРБД, нам не придётся изменять код самой программы, а придётся дописать файл с запросами, удовлетворяющими синтаксису новой СУРБД. Соответственно, в случае отсутствия поддержки классом новой СУБД, придётся реализовать данную поддержку. Так же возможна разработка ПО способного изначально без проблем работать на серверах с разными БД. Класс создаёт иллюзию, как будто напрямую работаешь с функциями языка PHP, что, на мой взгляд, очень удобно. В дальнейшем можно внести в класс поддержку большего числа функций по работе с СУБД. Сейчас реализована, исключительно для примера, поддержка MySQL и PgSQL. Так же реализованы только основные функции, например mysql_query и pg_query, а так же ещё несколько. Пример решения: сlass_db.php – файл с классом для работы с БД. Описание методов класса прилагается. config.php – файл конфигурации db_error.log – лог ошибок mysql_queries.php – файл с запросами для MySQL pgsql_queries.php – файл с запросами для PostgreSQL test.php – пример написании маленькой программки с использование вышеописанного класса. Не буду углубляться дальше, просто скачайте и посмотрите исходники. Там всё понятно. Если алгоритм и принципы решения понравятся тогда сделаю полнофункциональный класс с хорошим описанием. Скачать: www.hight.fatal.ru/trash/class_db.rar жду комментариев.
PDO не рулед? В PDO уже многие функции есть + туева куча поддерживаемых типов баз данных Обрабатывать ошибки можно стандартно для всей системы сайта - через исключения. PHP: <? class db extends PDO { function factory($params) { $class = "db_$params[adapter]"; $obj = new $class($params); $obj->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); # обработка ошибок - исключения return $obj; } # общие функции для всех db_* ... } class db_mysql extends db { function __construct($params) { # значения по-умолчанию для mysql $persistent = false; # постоянное подключение? # получение параметров extract($params); $PDOParams = array( PDO::ATTR_PERSISTENT => $persistent ); # подключение parent::__construct("mysql:host=$host;dbname=$dbname", $user, $pass, $PDOParams); } # специфичные функции для mysql ... } # использование $params = array( 'adapter'=>'mysq', 'host'=>'localhost', 'user'=>'user', 'pass'=>'password', 'dbname'=>'test', 'persistent'=>true ); db::factory($params); а функции мы выносим в отдельный класс db_{название_класса}, для каждого класса который юзает db, по той же схеме, т. е. db_{название_класса} - общие для всех db, расширяющий его класс db_{название_класса}_mysql - для специфичные для mysql
не рулед. не надо навязывать другие решения... то-есть DbSimple не предлагать. Меня интересует исключительно теория - как обеспечить поддержку множества БД приложением. Первый пост в 4:52 am написал, самому сложно читать.
ИМХО, не гуманно все функции работы сайта с db хранить в одном файле: есть класс topic - ему объект БД возвращает объект db_topic (или, расширяющий его, объект db_topic_mysql) ок. Но кто мешает юзать другие бибиотеки по той же схеме? ЗЫ. а PDO уже так же стандартен как DOM
Мне решение не понравилось своим подходом. В нем реализовано что-то типа поддержки "многоязычности", но только для языков запросов - для каждого запроса, использованного в проекте, нужно написать его отдельно для MySQL, отдельно для HySQL... неудобно, слишком много писанины когда мы делаем подобным образом поддержку многоязычности, с этим неудобством можно смириться - разные языки различаются всеми фразами. А в данном случае будет различаться только небольшая часть запросов, а подавляющая часть будет одинаковой для всех СУБД, в результате получается куча копи-паста... даже в тестовом примере, словарь запросов для MySQL ничем не отличается от словаря для Postgre. И еще одна заруба - при добавлении новой СУБД файл class_db.php придется дописывать весьма основательно.
всё, пошёл диалог! =) Вот я как раз и ищу подход. Насчёт писанины согласен, конечно её будет много... например: если в программе используется 50 запросов то будет 2 файла с теми самыми 50 запросами. Но с другой стороны этот подход надёжен и быстр, не в плане разработки, а в плане производительности. Ибо не придётся парсить какой-то один запрос и подстраивать его под определённый сервер БД. Да и файлик с запросами погружается только один, а можно вообще разделить файлы с запросами, то-есть каждый модуль программы будет подгружать только свои запросы. а вот это можно легко обойти. можно изменить структура класса и разнести функции БД по разным файлам, тем самым, чтобы добавить поддержку новой БД нужно будет просто написать файл с функциями по уже готовому шаблону.
Hight Я не против такого подхода в принципе, мне просто не по душе идея хранения всех запросов где-то там отдельно... хранить все запросы системы в куче, имхо, нерационально - вырванные из контекста, они не несут никакого смысла. В программе же участки кода без запросов станут совершенно непонятными, придется каждый раз сверяться со словарем запросов, чтобы понять, что у нас в программе происходит... тоже небольшое удовольствие. Ti "Боб Доул очень любит, когда Боб Доул говорит о Бобе Доуле. Боб Доул!" ("Симпсоны")
Буду спорить. Хранить запросы отдельно вполне здраво. В участке кода, вместо запросов, будет абстрактная функция получения данных. Инкапсуляция рулед.
Ti Если вместо них остается что-то читабельное, например метод класса, по которому можно сказать, что именно он возвращает, тогда возможно. Если же вместо запроса останется только обращение к массиву, да еще по малопонятному ключу... во-первых, начинается беготня по словарю, стоит его хоть чуть-чуть подзабыть, во-вторых, представь, что тебе нужно изменить какой-то из запросов. Что ты будешь делать? Просто изменишь текст запроса в словаре? Как бы не так тебе придется пройтись по всему проекту, чтобы убедиться, что этот запрос нигде больше не используется, кроме модифицируемой точки, и если используется - сделать копию, ее уже и править. Это типичная ситуация из категории "хотели как лучше".
на мой взгляд притянуто за уши... всегда можно сделать комментарий перед выполнением функции query в котором написать выполняемый запрос и полностью разъяснить зачем, куда и почему, даже ключ самого массива $query можно сделать идентичным запросу, чтобы было наглядно, но это конечно извращение да, просто изменю сам запрос. даже если запрос и используется где-то ещё по ходу программы это ничего не меняет. всё это надо предусматривать по ходу разработки. Dagdamor кстати, а как бы ты реализовал данную функциональность?
Глубоко в топик не вдавался (времени небыло), но из общей мысли и возникшего спора возникла вот такая идея: PHP: <?php /* * DB - это прослойка для универсальности функций запросов к базе (mysql_query, pgsql_query и.т.д.) */ class Data extends DB{ public __construct(){ } /* * Тупейший пример. Просто надо выбрать что-то для чего-то из таблицы */ public getSomeSimpleData($id){ $return $this->query('SELECT * FROM table1 WHERE table1_id = '.(int)$id); } /* * А вот тут уже что-то сложное */ public getSomeComplexData($id){ /* * Просто создаем пустой метод */ } /* * и.т.д. Везде, где запросы универсальны, пишем их сюда. Там где сложные запросы, * которые требуют учёта спицифики каждой из баз данных, делаем как с getSomeComplexData() */ } class Data_MySQL extends Data{ public __construct(){ parent::__construct(); } /* * Просто тупо переопределяем функцию и пишем специфичный для MySQL запрос */ public getSomeComplexData($id){ return $this->query('SELECT CONCAT(field1, field2) As field3 FROM table1 WHERE table1_id = '.(int)$id); } /* * И.т.д. */ } class Data_PgSQL extends Data{ public __construct(){ parent::__construct(); } /* * Просто тупо переопределяем функцию и пишем специфичный для PgSQL запрос */ public getSomeComplexData($id){ return $this->query('SELECT (field1||field2) AS field3 FROM table1 WHERE table1_id = '.(int)$id); } /* * И.т.д. */ } ?> Юзать це просто. В зависимости от используемой базы генерируем имя класса, инициализируем и вызываем методы PHP: <?php $class = 'Data_'.$current_db_name; $db = new $class; $resource1 = $db->getSomeSimpleData(); $resource2 = $db->getSomeComplexData(); ?> Ну и.т.д. Таким образом можно реализовать поддержку многих баз данных с минимально необходимым кол-вом Copy/Paste
Dagdamor, SQL различных СУБД различается настолько, что может быть совместим лиш при использовании самых примитивных запросов. Даже наиболее юзаемые LIMIT|OFFSET|TOP не совместимы с наиболее юзаемыми СУБД, про INSERT SELECT| REPLACE| ALTER TABLE не даже смысла упоминать. Попытки создать создать супер универсальный драйвер баз данных обречены на провал. ИМО, правильный путь - это индивидуальные решения. Как на уровне замены шаблонов SQL запросов в зависимости от текущей БД, так и на более высоком уровне типа get_some_data_as_array() (убирание процесса выбора драйвера, составления запроса и специфических действий в процедуру абстрактного запроса данных). Лично мне больше нравится идея с шаблонами запросов и универсальным интерфейсом работы с СУБД, за которым скрывается драйвер конкретной СУБД. Т.к. это сохраняет возможность пострения универсальных SQL запросов + позволяет избежать дополнительного кода кода и сэкономить время разработки. Всё это моё личное мнение и чистая теория, т.к. у меня пока не доходило до необходимости работать с разными СУБД одновременно, хотя я над этим думал.
несколько тезисов: 1. а надо ли менять СУБД как перчатки? на мой взгляд - нет ничего плохого в привязке к конкретной СУБД, просто нужно здраво подходить к её выбору. 2. СУБД очень сильно отличаются. на разных СУБД одно и то же я бы писал совершенно по разному (из соображений эффективности и наглядности). это касается не только самих запросов, но и логики работы с БД. поэтому на мой взгляд многобазовость нужно реализовывать не на уровне драйвера СУБД, а на уровне модели.
приведу пример, в sqlite можно написать такую конструкцию: Код (Text): select * from [catalog] where '$url' like [path]||'%' мускул, емнип, на такое не способен впринципе. да, это накладывает ограничение на размеры каталога, зато какое красивое решение =^_^=
Ой, ну это да Хотя mysql и postgresql поддерживают одинаковое написание вот mssql вроде мимо, там TOP что ли...
оффтоп завязывайте... давно известно, что запросы к разным БД отличаются. речь идёт о решении данной проблемы, а не о её констатации как факта.
dark-demon Это выборка ветки каталога, у которой path является подстрокой $url? Учти, что в твоем решении SQLite не сможет ни использовать индексы, ни как-либо оптимизировать маску поиска. Другими словами, запрос получается короткий, но тормозной. Кроме того, если у какой-то из записей path содержит символ '%', запрос вернет что попало Для мускула запрос может выглядеть так: Код (Text): SELECT * FROM catalog WHERE path=LEFT('$url',LENGTH(path)) Не так уж и плохо смотрится, имхо.
dark-demon Согласен и с тем, и с другим. Если проект легкий - можно обойтись простыми запросами, а если тяжелый - тут и словарь запросов не поможет
а он полюбому тут не быстрый, поэтому и "ограничение на размер"... этого не может быть, потому, что этого не может быть впринципе ладно, беру свои слова обратно. на мускуле так сделать можно, но менее красиво. по скорости - наврятли это быстрее... Горбунов Олег, неужели там реализованы курсоры?
Курсоры там есть, но совсем для других целей. =) Так же для оракла нет команд типа mysql_num_rows — все это связано с архитектурой Оракла — записи из результирующего набора начинают отдаватся сразу, еще ДО завершения всей выборки. Отсюда и невозможность посчитать число строк перед отдачей напрямую. (обходные пути ессно, есть)
Горбунов Олег, это как можно отсортировать строки, не обработав весь рекордсет, он что волшебный этот ОРАКЛ?