BLToolkit как инструмент генерации SQL
От: _FRED_ Черногория
Дата: 08.03.11 07:34
Оценка:
На сколько "продвинут"?
Требуется инструмент для "кросдатабазной" генереции запросов.

Есть ли / можно ли самому добавить поддержку WITH например и оконных функций (с тем, что бы можно было бы определить что определённый провайдер эту возможность не поддерживает)?
Можно ли сериализовать объект-запрос? Анализировать его (есть ли визитор)?

На сколько вообще разумно использование BLToolkit в таком ракурсе? Может, генерилка sql в нём делалась сторого для нужд ОРМ и всякие SQL-изыски там категорически не к чему?
Help will always be given at Hogwarts to those who ask for it.
Re: BLToolkit как инструмент генерации SQL
От: IT Россия linq2db.com
Дата: 08.03.11 14:20
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>На сколько "продвинут"?

_FR>Требуется инструмент для "кросдатабазной" генереции запросов.

Если имеется ввиду возможность указывать имя базы при указании таблицы, то в наличии соответствующие атрибуты для классов и метод DatabaseName в linq запросе.

_FR>Есть ли / можно ли самому добавить поддержку WITH например и оконных функций (с тем, что бы можно было бы определить что определённый провайдер эту возможность не поддерживает)?


Не понял.

_FR>Можно ли сериализовать объект-запрос? Анализировать его (есть ли визитор)?


Можно. Linq over WCF именно такой фигнёй и занимается.

_FR>На сколько вообще разумно использование BLToolkit в таком ракурсе? Может, генерилка sql в нём делалась сторого для нужд ОРМ и всякие SQL-изыски там категорически не к чему?


SQL изыски планируется только расширять.
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: BLToolkit как инструмент генерации SQL
От: _FRED_ Черногория
Дата: 08.03.11 15:21
Оценка: 2 (1)
Здравствуйте, IT, Вы писали:

_FR>>На сколько "продвинут"?

_FR>>Требуется инструмент для "кросдатабазной" генереции запросов.
IT>Если имеется ввиду возможность указывать имя базы при указании таблицы, то в наличии соответствующие атрибуты для классов и метод DatabaseName в linq запросе.

Имеется в виду возможность такого псевдокода:

var query = new SelectBuilder();
query.From = new Table("Orders");
query.AddColumn("OrderID");
var commandText = query.ToString(Db.MsSql);

SELECT [OrderID]
FROM [Orders]


То есть создание некоего объекта (по сути AST) и дальнейшая возможность трансляции его в SQL для различных СУБД.

_FR>>Есть ли / можно ли самому добавить поддержку WITH например и оконных функций (с тем, что бы можно было бы определить что определённый провайдер эту возможность не поддерживает)?

IT>Не понял.

Только помимо простейших и наиболее общих вещей нужна широкая поддержка sql:

var cte = new SelectBuilder();
cte.From = new Table("Order Details");
cte.AddColumns("OrderID", "ProductID");
var column = cte.AddColumn("UnitPrice");
query /* из предыдущего */.AddWith(cte);
query.AddJoin(cte, query["OrderID"], Sql.Equals, cte["OrderID"]);
query.AddColumn(column);

WITH [CTE Order Details] ([OrderID], [ProductID], [UnitPrice]) AS (
  SELECT [OrderID], [ProductID], [UnitPrice]
  FROM [Order Details]
)
SELECT [O].[OrderID], [D].[UnitPrice]
FROM [Orders] [O]
JOIN [CTE Order Details] [D] ON [O].[OrderID] = [D].[OrderID];


То есть объектная модель должна поддерживать common table expressions / оконные функции (с атрибутами PARTITION BY / ORDER BY) и прочие "изыски". Быть удобной для сложных манипуляций при построении и изменении объекта-запроса.

_FR>>На сколько вообще разумно использование BLToolkit в таком ракурсе? Может, генерилка sql в нём делалась сторого для нужд ОРМ и всякие SQL-изыски там категорически не к чему?

IT>SQL изыски планируется только расширять.

Ага, вот меня по примерам и подкупила возможность в пользовательском коде вносить расширения (ну так мне по примерам показалось что такая возможность есть для функций / подвыражений).
Help will always be given at Hogwarts to those who ask for it.
Re[3]: BLToolkit как инструмент генерации SQL
От: IT Россия linq2db.com
Дата: 08.03.11 16:24
Оценка: 24 (1)
Здравствуйте, _FRED_, Вы писали:

_FR>Имеется в виду возможность такого псевдокода:


Это Sql Builder pattern. В BLToolkit этот класс называется SqlQuery.

_FR>То есть создание некоего объекта (по сути AST) и дальнейшая возможность трансляции его в SQL для различных СУБД.


Потом он скармливается конкретному SqlProvider и тот строит по нему запрос.

_FR>То есть объектная модель должна поддерживать common table expressions / оконные функции (с атрибутами PARTITION BY / ORDER BY) и прочие "изыски".


Прямой поддержки WITH нет, хотя некоторые провайдеры строят SQL с использованием WITH для некоторых конструкций, например, для пейджинга.

_FR>Быть удобной для сложных манипуляций при построении и изменении объекта-запроса.


Удобным для сложных манипуляций может быть только паттерн-матчинг. Всё остальное — закат солнца в ручную.

_FR>Ага, вот меня по примерам и подкупила возможность в пользовательском коде вносить расширения (ну так мне по примерам показалось что такая возможность есть для функций / подвыражений).


На уровне выражений уже сейчас можно расширяться куда угодно. Сейчас переписывается парсер линк выражений и тогда появится возможность расширяться на уровне цепочки методов, например, добавлять свои методы вроде LeftJoin и т.п.
Если нам не помогут, то мы тоже никого не пощадим.
Re[4]: BLToolkit как инструмент генерации SQL
От: _FRED_ Черногория
Дата: 08.03.11 17:03
Оценка:
Здравствуйте, IT, Вы писали:

_FR>>Быть удобной для сложных манипуляций при построении и изменении объекта-запроса.

IT>Удобным для сложных манипуляций может быть только паттерн-матчинг. Всё остальное — закат солнца в ручную.

Нет, мне достаточно суметь, например, имея какой-то SqlQuery
SELECT X1, X2 FROM X

заиспользовать его в качестве FROM в другом SELECT SqlQuery,
SELECT X1 + X2, X1 - X2
FROM (SELECT X1, X2 FROM X) T

и потом результат указать как источник для INSERT, то есть что бы из трёх независимых кусочков собралось бы что-то вроде
INSERT Y (Y1, Y2)
SELECT X1 + X2, X1 - X2
FROM (SELECT X1, X2 FROM X) T


А после этого вдруг дабавит в самый внутренний SELECT какой-нить WHERE.

С анализом/распознованием будем пока закатыванием заниматься

В общем, буду пробывать
Help will always be given at Hogwarts to those who ask for it.
Re[5]: BLToolkit как инструмент генерации SQL
От: Neco  
Дата: 15.03.11 14:16
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>А после этого вдруг дабавит в самый внутренний SELECT какой-нить WHERE.

Хотел бы сказать, что тема топика для меня тоже очень насущная (если я правильно понял суть вопроса).
Где-то год назад я начинал рабочий проект, в котором даже хотел использовать BLT, но меня смутила на тот момент связность сборок BLT с System.Web, System.Windows.Forms, OracleClient и прочими специфичностями. Показалось что всё это как-то сыровато пока и решил ограничиться пробой Linq-подхода в проекте. Т.е. казалось очень логичным забирать только нужные данные из базы.
Однако, на практике выяснилось, что это удобно только когда пишешь запрос ручками. Когда же надо написать что-то автоматическое, то всё сразу превращается в кошмар — в необходимость перестраивать деревья, понимать как все эти Expression стыкуются между собой.

Сейчас в фоновом режиме, не торопясь, пишу свой DataAccess, в котором предусматриваю три уровня работы с БД:
— конструирование запросов на низшем уровне (работа с элементами sql, гибче всего, но и сложнее, всё нетипизировано)
— конструктор среднего уровня (проще составлять простые запросы, типизированный и нетипизированный варианты)
— ORM для типичных сущностей, может быть тут же место и Linq-образным запросам.

на низком уровне конструирование выглядит так (должны поддерживаться все возможности sql — ранжирование, cte. Расширяются специфичными для субд элементами (на данный момент сиквенсы), обобщаются, где возможно):

        [TestMethod()]
        public void RangeAggregateTest() {
            var q = new Query();
            var my_table = new QSrcWithAlias(new TableSource("my_table"), new Alias("tbl"));
            var clmn1 = new SourceColumn(my_table, "id");
            var clmn2 = new SourceColumn(my_table, "value");
            var clmn3 = new SourceColumn(my_table, "purchase_date");
            q.Select.Add(new QExWithAlias(new RangeFunction(new RAggRank(), new List<SourceColumn>(), new List<SourceColumn>() { clmn3, clmn1 }), new Alias("rnk")));
            q.Select.Add(new JustQEx(clmn1));
            q.From = my_table;

            ILowTranslator translator = new DummyLowTranslator();
            var pq = translator.Translate(q);

            var sb = new StringBuilder();
            sb.AppendLine("select rank() over (order by tbl.purchase_date, tbl.id) rnk, tbl.id");
            sb.AppendLine("from my_table tbl");

            Shared.AreSqlEqual(sb.ToString(), pq.SqlQuery);
            Assert.AreEqual(0, pq.Parameters.Count());
        }


на среднем (планирую этот уровень сделать независимым от субд, пока не знаю как получится):

        [TestMethod()]
        public void MixedGeneralTest() {
            var decl = new MidDeclarator(new FlatClassMetaInformationExtractor());
            var qb = decl.AQuery<result_data>();

            var tbl1 = decl.Table<my_table>("t");
            qb.Select(tbl1.Clmn(c => c.id), tbl1.Clmn(c => c.name).As(qb.Clmn(c => c.als_name)));
            qb.From(tbl1);
            var subQuery = GetUntypedSubQuery(2);
            qb.LeftOuterJoin(subQuery).On(tbl1.Clmn(c => c.ref_id).EqualsTo(subQuery["id"]));
            qb.Select(subQuery["sub_name"]);

            IMidTranslator translator = new DummyMidTranslator();
            var pq = translator.Translate(qb);

            var sb = new StringBuilder();
            sb.AppendLine("select t.id, t.name als_name, t1.sub_name");
            sb.AppendLine("from my_table t");
            sb.AppendLine("left outer join");
            sb.AppendLine("    (");
            sb.AppendLine("        select t1.id, t1.name sub_name");
            sb.AppendLine("        from ref_table t1");
            sb.AppendLine("        where");
            sb.AppendLine("            t1.state = $state");
            sb.AppendLine("            and t1.enabled = TRUE");
            sb.AppendLine("    ) t1 on (t.ref_id = t1.id)");

            Shared.AreSqlEqual(sb.ToString(), pq.SqlQuery);
            Assert.AreEqual(1, pq.Parameters.Count());
            Assert.AreEqual("state", pq.Parameters.Single().ParameterName);
        }
        private IMidSourceEntity GetUntypedSubQuery(int param) {
            var decl = new MidDeclarator();
            var qb = decl.AQuery();

            var tbl1 = decl.Table("ref_table");
            qb.Select(tbl1["id"], tbl1["name"].As("sub_name"));
            qb.From(tbl1);
            qb.Where(decl.And(tbl1["state"].EqualsToParam(param), tbl1["enabled"].EqualsToConstant(true)));

            return qb;
        }

вся типизация сводится к тому, чтобы использовать типы-таблицы для извлечения информации об именах таблиц, полей и т.п. Маппер (которого пока нет) будет должен использовать накопленную информацию, чтобы по сути сделать обратную операцию — текстовые имена колонок сопоставить со свойствами типов-таблиц.

но всё это в зачаточной стадии и не знаю, дозреет ли до чего-то серьёзного. Просто хотел присоединиться к тому, что необходимость SelectBuilder'ов (и не только Select) есть и было бы заманчиво увидеть это в BLT. И вообще узнать бы, движется ли поезд вообще в этом направлении, либо есть фундаментальные нестыковки?
всю ночь не ем, весь день не сплю — устаю
Re[6]: BLToolkit как инструмент генерации SQL
От: IT Россия linq2db.com
Дата: 15.03.11 14:42
Оценка: 33 (3)
Здравствуйте, Neco, Вы писали:

На BLToolkit это будет выглядеть примерно следующим образом:

var myTable = new SqlTable
{
    Name   = "my_table",
    Fields =
    {
        new SqlField { Name = "id"            },
        new SqlField { Name = "value"         },
        new SqlField { Name = "purchase_date" }
    }
};

var q = new SqlQuery();

q
    .Select
        .Expr (new SqlFunction("MyFunction", myTable["purchase_date"], myTable["id"]))
        .Field(myTable["id"])
    .From
        .Table(myTable)
    ;
Если нам не помогут, то мы тоже никого не пощадим.
Re[7]: BLToolkit как инструмент генерации SQL
От: Neco  
Дата: 15.03.11 17:31
Оценка:
Здравствуйте, IT, Вы писали:

IT>На BLToolkit это будет выглядеть примерно следующим образом:


Спасибо за ответ!
Тогда ещё несколько вопросов, пока вы добрый.
1. у меня лежит скачанная версия 3.2 с середины прошлого года. А сейчас финальная опубликованная версия — 3.1. Проект жив?
2. я правильно понял, что Linq AST рендерится в SqlQuery, а потом уже он рендерится в настоящий Sql запрос?
3. счас обнаружил, что SqlQuery существует разный в папках Data\Sql и DataAccess. И они оба (точнее используемые ими SqlQueryBase и разные Clause'ы соот-но) занимаются генерацией запросов. Что-то из них устаревшее или оба в силе?
4. Можно ли где почитать про концептуальное устройство библиотеки? Т.е. там же всё разделено вроде бы — сбор информации о запросе, трансформации, генерация sql, выполнение запроса, получение результатов, маппинг. К тому же по приведённому вами примеру я так понимаю, что в основе лежит нетипизированый доступ к данным, а уже на уровне выше (ближе к юзеру) он может типизироваться. Но читая код я вижу, что маппинг, чтение метаданных и генерация запроса идут как будто в одном месте:
        protected SqlQueryInfo CreateInsertSqlText(DbManager db, Type type, int nParameter)
        {
            TypeExtension        typeExt = TypeExtension.GetTypeExtension(type, Extensions);
            ObjectMapper         om      = db.MappingSchema.GetObjectMapper(type);
            List<MemberMapper>   list    = new List<MemberMapper>();
            StringBuilder        sb      = new StringBuilder();
            SqlQueryInfo         query   = new SqlQueryInfo(om);
            MetadataProviderBase mp      = MappingSchema.MetadataProvider;

            sb.Append("INSERT INTO ");
            AppendTableName(sb, db, type);
            sb.Append(" (\n");

            foreach (MemberMapper mm in GetFieldList(om))
            {
                // IT: This works incorrectly for complex mappers.
                //
                // [2009-03-24] ili: use mm.MemberAccessor instead of mm.ComplexMemberAccessor
                // as in CreateUpdateSqlText
                //

                bool isSet;

                if (mp.GetNonUpdatableAttribute(type, typeExt, mm.MapMemberInfo.MemberAccessor, out isSet) == null || !isSet)
                {
                    sb.AppendFormat("\t{0},\n",
                        db.DataProvider.Convert(mm.Name, ConvertType.NameToQueryField));
                    list.Add(mm);
                }
            }

это сильно сбивает с толку (вероятно, в большей степени из-за того, что ожидаю увидеть так как сам себе это представлял). Хотелось бы понять концепции — ключевые ответственности, интерфейсы, классы; как они между собой взаимосвязаны. Чтобы когда уже полез в код, не удивляться.
5. Есть ли в планах растащить всё по разным сборкам? Чтобы было проще понимать и использовать только то, что надо? Например, мне нравится модель маппирования BLT и я хочу её использовать, но то, что надо тащить ещё целый паровозик доступа к данным — это пугает. Хотя я подозреваю, что люди делятся на два типа — кому нравится иметь всё в разных сборках и кто любит всё в одном, поэтому может это и спорное решение, но конкретные планы этого проекта всё же интересны.
всю ночь не ем, весь день не сплю — устаю
Re[8]: BLToolkit как инструмент генерации SQL
От: IT Россия linq2db.com
Дата: 15.03.11 18:59
Оценка:
Здравствуйте, Neco, Вы писали:

N>1. у меня лежит скачанная версия 3.2 с середины прошлого года. А сейчас финальная опубликованная версия — 3.1. Проект жив?


Последняя версия здесь — Current SVN snapshot.

Проект живее всех живых. Сейчас идёт переработка Linq-парсера. Далее в планах добавление ещё одного DataProvider для одной встраиваемой БД. Есть идеи по вынесению Linq в отдельный проект. Дальше посмотрим.

N>2. я правильно понял, что Linq AST рендерится в SqlQuery, а потом уже он рендерится в настоящий Sql запрос?


Правильно.

N>3. счас обнаружил, что SqlQuery существует разный в папках Data\Sql и DataAccess. И они оба (точнее используемые ими SqlQueryBase и разные Clause'ы соот-но) занимаются генерацией запросов. Что-то из них устаревшее или оба в силе?


В DataAccess старый вариант, который не используется Linq и который не использует SqlProviders.

N>4. Можно ли где почитать про концептуальное устройство библиотеки?


К сожалению, только в коде

N>Т.е. там же всё разделено вроде бы — сбор информации о запросе, трансформации, генерация sql, выполнение запроса, получение результатов, маппинг. К тому же по приведённому вами примеру я так понимаю, что в основе лежит нетипизированый доступ к данным, а уже на уровне выше (ближе к юзеру) он может типизироваться. Но читая код я вижу, что маппинг, чтение метаданных и генерация запроса идут как будто в одном месте:


Это DataAccess из другой оперы ещё до линковских времён. На самом деле библиотека уже слегка обросла рудиментами, но по понятным причинам от них не получается избавиться.

N>5. Есть ли в планах растащить всё по разным сборкам? Чтобы было проще понимать и использовать только то, что надо? Например, мне нравится модель маппирования BLT и я хочу её использовать, но то, что надо тащить ещё целый паровозик доступа к данным — это пугает. Хотя я подозреваю, что люди делятся на два типа — кому нравится иметь всё в разных сборках и кто любит всё в одном, поэтому может это и спорное решение, но конкретные планы этого проекта всё же интересны.


Есть идея вынести всё, что касается Linq в отдельный проект и развивать его в сторону легковесного ORM.
Если нам не помогут, то мы тоже никого не пощадим.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.