Как я могу собрать SQL с объектно-ориентированным Perl? - PullRequest
9 голосов
/ 26 января 2009

В настоящее время я отвечаю за процесс, который, кажется, очень тесно связан с базой данных. Цель моей программы / скрипта / фреймворка - сделать единообразие из разнородных источников данных. Используя форму внедрения зависимости, мой процесс на очень высоком уровне работает нормально. Реализация каждого типа источника данных скрыта от бизнес-абстракции самого высокого уровня происходящего. Отлично. У меня два вопроса.

1) У меня длинный абзац (и его беспокоит длина), который собирает инструкцию SQL в пространстве Perl о том, как преобразовать эти разные источники данных в один, однородный конечный формат. Поэтому строка SQL всегда зависит от типа данных, с которыми я работаю. Предложение WHERE зависит, предложение FROM зависит, предложение INSERT зависит, все зависит. Меня смущает высокий уровень зависимости. Как мне моделировать этот процесс объектно-ориентированным способом? MagicObject-> buildSQL? По сути, это то, что у меня есть сейчас, но кажется, что все части кода знают слишком много, поэтому его длина.

2) Если у меня есть функция, которая что-то делает (строит SQL?), Передам ли я бизнес-объекты целиком, а затем в последнюю минуту приведу их в строку? Или я ранжирую их пораньше и позволяю моей функции обрабатывать только то, что ей нужно, а не визуализировать сами объекты?

Редактировать : Хотя я не сомневаюсь в важности ORM, я не верю, что мы еще находимся в пространстве ORM. Представьте, что данные по бейсболу для американской, национальной и вымышленной лиг хранились в совершенно разных форматах с разными уровнями нормализации. Моя работа состоит в том, чтобы читать эти источники данных и помещать их в единый нормализованный пул. Я чувствую, что действие ORM на эти объекты происходит после моего процесса. Я своего рода уборщик данных, если хотите. По сути, из-за отсутствия единого пула, который я создаю, бизнес-объектов пока не существует.

Редактировать ^ 2 : Мне стало известно, что, возможно, я не описал проблемное пространство достаточно подробно. Вот пример.

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

Эти данные предоставляются в открытом доступе 50 штатами, но в совершенно разных форматах. Некоторые представляют собой один файл данных, не нормализованный. Другие являются нормализованными таблицами в формате CSV. Некоторые из них являются документами Excel. Некоторые из них являются TSV. Предоставляются даже некоторые записи, которые не являются полными без ручного вмешательства (другие, созданные вручную источники данных).

Цель моего проекта - создать «драйвер» для каждого из 50 состояний и убедиться, что конечным продуктом процесса является основная база данных преступников в идеальной модели отношений. Все правильно набрано, схема в идеальной форме и т. Д.

Ответы [ 8 ]

9 голосов
/ 26 января 2009

Вы хотите посмотреть на Фей . Я начал использовать его несколько месяцев назад на работе, и, хотя реализация по-прежнему имеет острые углы из-за молодого возраста, идея, стоящая за ним, является твердой. F.ex., возьмите запрос, слегка адаптированный из руководства:

my $user = $schema->table( 'user' );
my $q = Fey::SQL
    ->new_select
    ->select( $user->columns( 'user_id', 'username' ) )
    ->from( $user );

Теперь вы можете написать такую ​​функцию:

sub restrict_with_group {
    my ( $q, $table, @group_id ) = @_;
    my $group = $schema->table( 'group' )->alias;
    $q
        ->from( $table, $group )
        ->where( $group->column( 'group_id' ), 'IN', @group_id );
}

Это добавит внутреннее соединение с user до group, а также условие WHERE. И вуаля, вы можете написать следующее в основной программе:

restrict_with_group( $q, $user, qw( 1 2 3 ) );

Но эта restrict_with_group функция будет работать для любого запроса, который имеет внешний ключ к таблице group! Чтобы использовать его, вы передаете запрос, который хотите restrict и таблица, к которой вы хотите применить ограничение, а также идентификаторы групп, к которым вы хотите ограничить его.

В конце вы говорите $q->sql( $dbh ) и получаете строку SQL, представляющую запрос, который вы создали в объекте $q.

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

7 голосов
/ 26 января 2009

Пожалуйста, не пишите свой собственный ORM. Используйте что-то вроде DBIx :: Class .

Все эти проблемы, о которых вы упомянули, были решены, а реализация протестирована в тысячах других приложений. Придерживайтесь написания своего приложения, а не переопределения библиотек. На самом деле вы не можете использовать DBIC в своем приложении, но вам следует взглянуть на его подход к реализации; особенно то, как он постепенно создает ResultSets (которые не являются наборами результатов, а скорее отложенными запросами).

5 голосов
/ 26 января 2009

Если вы не хотите ORM, но хотите собрать SQL из битов без прямой манипуляции / конкатенации строк, взгляните на Фей , который может делать то, что вы хочу.

Обновление: ответ Аристотеля Пагальциса намного лучше. Он на самом деле привел примеры того, как выглядит Фей и как это может помочь.

1 голос
/ 27 января 2009

Если я не понимаю, это похоже на приложение ETL (Извлечение / Преобразование / Загрузка), которое не рассчитало разделить три этапа.

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

Принимая идею 50 состояний, вы не сможете избежать 50 "извлекающих" процессов, надеемся, с библиотекой общих подпрограмм. Я бы атаковал проблему по одному входному источнику за раз, проводя рефакторинг по мере добавления новых, но при этом стараясь инкапсулировать переменные части, чтобы точно знать, где потребуется внести изменения, когда поставщик изменит свой формат.

Часть «преобразования» не должна быть слишком обременительной: просто возьмите то, что вы получили, и подготовьте его к выводу.

1 голос
/ 26 января 2009

С чисто кодовой точки зрения - у вас в руках длинный и сложный кусок кода. Тебе это не нравится. Зачем? Я могу только предположить, что там есть некоторое дублирование кода. В противном случае, что не нравится? Итак, рефакторинг для устранения дублирования ... Я знаю, это звучит банально, но, поскольку вы не публикуете код, сложно быть более конкретным. Может быть, у объекта есть методы для предложений from, where и insert, чтобы инфраструктура SQL не дублировалась? Я просто не знаю, что делать, но устранение дублирования является ключевым моментом.

0 голосов
/ 29 января 2009

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

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

Этот метод также обеспечивает гибкость, поскольку ваши потребности в доступе к данным могут измениться. Например, если вам задают вопрос: «У скольких белокурых водителей были штрафы за превышение скорости в каждом штате?». База данных SQLite может сделать это с помощью одной команды, в то время как другие решения, вероятно, потребуют возврата набора данных, который затем необходимо проанализировать, сгруппировать и установить для вывода.

0 голосов
/ 26 января 2009

Если вы не хотите иметь дело с ORM, у меня часто есть такой код:

my (@columns,@tables,@wheres,@order_bys,@values);

... # Add value to those variables as needed, using push.
... # use ? for variables to be quoted

# Build SQL statement
my $sql = "select ".join(",",@columns).
    " from ".join(",",@tables).
    " where ".join(" and ",@wheres).
    " order by ".join(",",@order_bys);

my $sth = $dbh->prepare($sql);
$sth->execute(@values);

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

0 голосов
/ 26 января 2009

Я думаю, что вы описываете динамический SQL - программный сбор запроса во время выполнения. Это общая особенность Object Relational Mappers, таких как LINQ to SQL и LLBLGenPro, и многие другие. Построить его - задача не из легких.

Как правило, ORM объективируют язык SQL. Вы пишете своего рода «объектную модель документа (DOM) SQL», которая позволяет программно создавать запросы SQL, представляя их (например) как объект «Запрос». Затем вы устанавливаете свойства для объекта запроса, такие как коллекция столбцов, коллекция таблиц и коллекция соединений (это всего лишь примеры одного подхода). Результатом будет строка запроса SQL, представленная в виде свойства объекта запроса. *

Вы также должны предоставить объекту Request возможность прочитать определение схемы ваших источников данных. Вы упоминаете, что ваше предложение WHERE зависит от типа. Поэтому ваш ассемблер SQL должен уметь читать схему и правильно строить предложение.

Это может быть излишним для вашего случая. Я думаю, что основной вопрос заключается в том, действительно ли вам требуются динамические запросы SQL или есть менее сложный вариант, который удовлетворит ваши требования?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...