Здравствуйте, alex_public, Вы писали:
G>>То есть я добавил в проекцию поле и не могу обратиться к нему? Это гениально! _>Можешь, но через специальную функцию, а не в виде обычного поля) Т.к. собственно ситуация динамической проекции (в отличие от динамических запросов) является совсем нестандартным случаем.
Сдаётся мне что требовалась не настолько "динамическая" проекция, а скорее "отложенная".
То есть не:
// #2auto query = ...;
if(condition)
{
auto projected = query.columns(p.some);
for (const auto& row : db(projected))
//...
}
То есть в варианте #2 с доступом к полям не должно быть никаких проблем.
А вот для варианта #1 аналогичные проблемы (статическая проверка доступности полей и т.п.) видимо начнутся и у C#
Здравствуйте, alex_public, Вы писали:
_>Да какие там могут быть сложности: _>
_>string query="select first_name, last_name, employment_date from Employees where 1";
_>if(!first_name.empty()) query+=" and first_name=\""+first_name+'"';
_>if(!last_name.empty()) query+=" and last_name=\""+last_name+'"';
_>if(employment_date_from) query+=" and employment_date>="+to_string(employment_date_from);
_>if(employment_date_to) query+=" and employment_date<"+to_string(employment_date_to);
_>
_>Как будем измерять? )
Давайте измерять с вот такими значениями параметров:
first_name = "'; delete Employees where 1=1; select * from Employees where first_name=''";
Остальные — пустые.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, gandjustas, Вы писали:
G>>Если кеш меряется в мегабайтах, то это настолько маленькая база, что нет смысла говорить о быстродействии. Её можно полностью затянуть в память приложения и по ней ходить. Естественно в этом случае скорость выполнения запросов составит микросекунды.
_>Откуда это тебе знать? ) Ты же до этого момента даже не подозревал о существование таких кэшей. А тут вдруг резко стал экспертом по их работе? )))
чтобы понимать бесполезность кеша запросов на стороне базы надо хотябы немного разбираться в архитктуре по. Фактически ни в одном из порулярных движков рсубд нету кеша запросов или он выключен (настолько «полезен», что по умолчанию не включают), только mysql отличился.
G>>А ты по ссылкам не читал дальше? Вот например http://www.pythian.com/blog/oracle-11g-result-cache-tested-on-eight-way-itanium/ G>>Оказывается кеш в оракле плохо масштабируется, и по умолчанию он выключен. Видимо для очень специфических сценариев применяется. Включенного кеша я ни на одном оракловом сервере не видел.
_>Я уже не сомневаюсь, что ты придумаешь отмазку на любой аргумент (и на любые другие БД). ))) Однако это всё равно не поможет — картина уже понятна. )))
Понятно, аргументы кончились.
G>>Ты удивишься, но 90%+ критичных к скорости приложений не допускают в базу другие приложения. В оставшихся 10% можно таймстемпы и тригеры прикрутить.
_>Ну так могут быть например просто несколько процессов одного и того же приложения. Как я понимаю, твоё решение в таком случае будет совсем печальным по быстродействию? )
Если несколько процессов, то способов миллион. Зачастую распределенный кеш используется. И по быстродействию это на порядок лучше.
_>>>Проверим на практике? ))) Берём некую БД с кэшированием запросов и пишем к ней 3 варианта одного и того же запроса: через sql строку, через linq, через sqlpp11. И посмотрим насколько процентов будут отличаться результаты... G>>Вперед, пиши. Пример на C# я привел, давай аналогичный на голом SQL и sqlpp11. G>>Еще раз напомню, что ты утверждал, что можно сделать быстрее без потери возможностей linq. Вот и делай пример.
_>Ты про аналог такого кода? _>
Нет, в моем примере select в конце, да еще и джоин дополнительный делает. А ты опять привел к случаю статического запроса, который неинтересен.
Еще раз — без динамического конструирования и джоинов вообще нет смысла обсуждать что либо.
G>>Случай примитивных запросов рассматривать неинтересно. В linq их можно прогнать через compiled query и расходы на создание\обход деревьев упадут до нуля.
_>"Можно" — это конечно хорошо звучит. Так покажи, как такой код будет выглядеть на практике. Вот опять же на базе того твоего примера с if'ми. )))
С ифами нет смысла, компилировать можно только статический запрос. Можешь ручками выписать все 8-16 штук, сам же предлагал https://msdn.microsoft.com/ru-ru/library/Bb399335(v=vs.100).aspx.
G>>То есть я добавил в проекцию поле и не могу обратиться к нему? Это гениально!
G>>Понятно, значит ничего не умеет, так и запишем. _>Ну если ты смог только такое понять, после просмотра тех примеров, то тогда сочувствую. )))
Других нет. Видимо неспроста.
G>>То есть типизированных джоинов тоже нет. _>И снова у тебя плохо с чтением. Если ты потребуешь не то поле, то получишь ошибку. ) Я тебе об этом писал ещё в предыдущем сообщение.
В случае простого запроса. А как быть с джоинами?
G>>Ладно, продолжать неинтересно. Я уже увидел что от силы 10% возможностей linq покрывается, и то самых простых. _>Уже сливаешься? )
Да просто надоело по кругу. Ты текста нагенерил море, а примеров все нет. Только примитивные запросы. Можешь не продолжать.
Здравствуйте, alex_public, Вы писали:
_>Всё верно сказано. Но есть один неучтённый нюанс. В C++ действительно сложно заниматься метапрограммированием. А вот пользоваться результатами метапрограммирования обычно как раз наоборот очень просто и удобно. Т.е. грубо говоря, если по данной тематике существует проработанная готовая библиотека, то не смотря на дикую жуть внутри, пользоваться ею сможет даже новичок. Собственно по async такой библиотеки в то время не было и пришлось писать свою реализацию, а вот по sql, как видно, всё есть и даже не одна.
Я согласен, что пользоваться такими библиотеками вовсе не сложно. Однако, в книгах по C# для начинающих обязательно рассказывается про linq. В доброй половине книг рассказывается про EF.
В то время как в книгах по С++ даже для среднего уровня нет описаний использования подобных библиотек. Это я считаю серьёзным упущением. Разве что книги по QT относительно близки к этому, но обычно QT не считают программированием на чистом C++.
Если бы появились пара-тройка книг по C++, где будет описано проектирование/создание приложений с нуля с использованием вот таких библиотек (async, ORM), то язык может обрести второе дыхание (ага, школота набежит).
Кстати, никто не хочет написать такую книгу?
Здравствуйте, _Case, Вы писали:
_C>Здравствуйте, Sinclair, Вы писали:
S>>Здравствуйте, _Case, Вы писали:
_C>>>Entity Framework полезен когда нужно работать с локальной embedded базой (SqLite, SQL Compact) в desktop приложении, потому как писать inline SQL код (в C# классах) не очень удобно и чревато ошибками типа — забытой кавычки, а хранимых процедур в таких встроенных движках баз нет, а для WEB согласен, на хранимках сайт работает гораздо быстрее (при работе с MSSQL Server IMHO на порядки) S>>Facepalm. Вы просто не умеете готовить батчи. S>>Хранимки к перформансу не имеют почти никакого отношения.
_C>Ещё пример — сложные отчеты, по моему опыту через ORM такой отчет (хотя бы 7-8 джойнов) будет строиться неприлично большое количество времени..
Отчеты и ORM — вещи несовместимые. Так же несовместимы ORM и множества (соединения горизонтальных и вертикальных проекций). Еще ужаснее сочетание ORM + Bulk operations.
ORM хорошо рулят для обычного CRUD.
Вы же не забиваете шуруп молотком? Поэтому каждой технологии — свое применение
+ При использовании LINQ to EF, программист может забыть сто работает все-таки с б.д., а не просто LINQ, что может приводить к проблемам N + 1.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Сдаётся мне что требовалась не настолько "динамическая" проекция, а скорее "отложенная".
Да, всё правильно. Собственно говоря, я уже писал, что главным преимуществом этих динамических запросов является даже не сама динамика, а неизменность типа от ветвления.
EP>То есть в варианте #2 с доступом к полям не должно быть никаких проблем.
Да, я тоже не вижу никаких проблем для реализации подобного. Но в данной библиотечке такого вроде нет. Видимо автор посчитал что для "извращенцев" (которым надо динамически переключать проекции) хватит и того, что реализована та, максимальная динамика. )))
EP>А вот для варианта #1 аналогичные проблемы (статическая проверка доступности полей и т.п.) видимо начнутся и у C#
О, кстати... Я что-то с ходу не заметил этого. Сейчас спросим наших коллег, которые так хватались именно динамикой. )))
Здравствуйте, Sinclair, Вы писали:
S>Давайте измерять с вот такими значениями параметров: S>first_name = "'; delete Employees where 1=1; select * from Employees where first_name=''"; S>Остальные — пустые.
Очень смешно. )))
И кстати в случае sqlcpp11 инъекция не сработает. Ну а в случае sql строк, да, надо ещё вызвать sql_escape_string для параметров, но будем считать, что это уже сделано перед нашим примером. Потому как здесь мы совсем другие вещи проверяем.
Здравствуйте, gandjustas, Вы писали:
G>чтобы понимать бесполезность кеша запросов на стороне базы надо хотябы немного разбираться в архитктуре по. Фактически ни в одном из порулярных движков рсубд нету кеша запросов или он выключен (настолько «полезен», что по умолчанию не включают), только mysql отличился.
Что-то у тебя снова плохо с чтением. В Oracle этот кэш по умолчанию как раз включён. Только в режиме работы для отмеченных (в sql коде) запросов. Кстати, это на мой взгляд самый оптимальный режим для тонкой настройки оптимизации, только к сожалению непереносимый. И насчёт других СУБД у тебя тоже плохо с информацией. Например вот http://www.postgresql.org/about/news/1296, или тот же firebird насколько я помню работает тут как и myqsl. Так что похоже в реальности то как раз ситуация обратная — нет данной функции только у твоего сомнительного инструмента. )))
G>Если несколько процессов, то способов миллион. Зачастую распределенный кеш используется. И по быстродействию это на порядок лучше.
На порядок — это разве что по сравнению с вариантом, где доступ к БД через linq.))) Да, и счастливо тебе это всё программировать в своём приложение. А я просто возьму нормальную СУБД и получу автоматом такую же эффективности без единой строчки лишнего кода. )))
_>>Ты про аналог такого кода? _>>
2. select у тебя был в конце, да. А я его перенёс в начало для внешней симметрии с остальными двумя примерами. Можно и обратно в конец — какая разница то, если он у тебя не динамический (см. ниже)? )
G>Еще раз — без динамического конструирования и джоинов вообще нет смысла обсуждать что либо.
Эээ что? ) Какие ещё join?) Ты же сам предложил протестировать именно на этом своём примере:
Вперед, пиши. Пример на C# я привел, давай аналогичный на голом SQL и sqlpp11.
Еще раз напомню, что ты утверждал, что можно сделать быстрее без потери возможностей linq. Вот и делай пример.
А как только я привёл примеры, то сразу в кусты бежишь? )
Да, и кстати... Если ты хочешь настоящей динамики... Что у нас там скажет linq для такого случая (на sqlcpp11 или sql строках естественно без проблем реализуется):
var query = context.Employees;
if(!string.IsNullOrEmpty(lastName)) query = query.Where(e => e.LastName = lastName);
if(cond) query=query.Select(e => new {e.FirstName});
else query=query.Select(e => new {е.LastName});
Т.е. для твоих же любимых динамических запросов compiled query не работает? Т.е. получается, что с помощью linq у тебя вообще нет возможности сделать запрос без диких накладных расходов? )
_>>И снова у тебя плохо с чтением. Если ты потребуешь не то поле, то получишь ошибку. ) Я тебе об этом писал ещё в предыдущем сообщение. G>В случае простого запроса. А как быть с джоинами?
И с join'ми тоже без проблем. Показать пример ошибки? )
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, gandjustas, Вы писали:
G>>чтобы понимать бесполезность кеша запросов на стороне базы надо хотябы немного разбираться в архитктуре по. Фактически ни в одном из порулярных движков рсубд нету кеша запросов или он выключен (настолько «полезен», что по умолчанию не включают), только mysql отличился.
_>Что-то у тебя снова плохо с чтением. В Oracle этот кэш по умолчанию как раз включён. Только в режиме работы для отмеченных (в sql коде) запросов.
То есть выключен
_> Кстати, это на мой взгляд самый оптимальный режим для тонкой настройки оптимизации, только к сожалению непереносимый.
Интересно, сколько ты работал с СУБД Оракл или вообще со взрослыми базами, чтобы твой взгляд имел какой-либо смысл?
И в чем его непереносимость?
_>И насчёт других СУБД у тебя тоже плохо с информацией. Например вот http://www.postgresql.org/about/news/1296, или тот же firebird насколько я помню работает тут как и myqsl. Так что похоже в реальности то как раз ситуация обратная — нет данной функции только у твоего сомнительного инструмента. )))
Это не фича движка, а расширение. Расширяемость движка это хорошо, но чем больше точек расширения, тем ниже overall performance.
G>>Если несколько процессов, то способов миллион. Зачастую распределенный кеш используется. И по быстродействию это на порядок лучше.
_>На порядок — это разве что по сравнению с вариантом, где доступ к БД через linq.))) Да, и счастливо тебе это всё программировать в своём приложение. А я просто возьму нормальную СУБД и получу автоматом такую же эффективности без единой строчки лишнего кода. )))
Через linq обычно быстрее и безопаснее, чем рукопашные запросы. Ты сам это прекрасно показал.
_>>>Ты про аналог такого кода? _>>>
G>>Нет, в моем примере select в конце, да еще и джоин дополнительный делает. А ты опять привел к случаю статического запроса, который неинтересен.
_>1. join'ов у тебя в примере не было, не надо врать: http://rsdn.ru/forum/flame.comp/6013255
Да-да, я специально опустил что там внутри select, думал ты догадаешься. Но если сам не догадался еще раз напишу:
x => new { x.A, x.B.C }
Вторая часть проекции создает джоин. А операторы выше об этом не знают.
_>2. select у тебя был в конце, да. А я его перенёс в начало для внешней симметрии с остальными двумя примерами. Можно и обратно в конец — какая разница то, если он у тебя не динамический (см. ниже)? )
Разница огромная, select может добавить джоины, подзапросы и еще очень много чего.
G>>Еще раз — без динамического конструирования и джоинов вообще нет смысла обсуждать что либо.
_>Эээ что? ) Какие ещё join?) Ты же сам предложил протестировать именно на этом своём примере: _>
_>Вперед, пиши. Пример на C# я привел, давай аналогичный на голом SQL и sqlpp11.
_>Еще раз напомню, что ты утверждал, что можно сделать быстрее без потери возможностей linq. Вот и делай пример.
_>А как только я привёл примеры, то сразу в кусты бежишь? )
Ты привел пример статического запроса, который в одном месте задается на стадии компиляции. Еще раз повторю, что такой случай крайне неинтересный. Попробуй по разным модулм (dll) растащить куски запроса. В этом и суть динамической композиции. Потребитель IQueryable совершенно не обязан знать как запрос был построен и может навесить свои проекции и ограничения.
Если ты этого не понимаешь, то пожалуйста не продолжай. Не трать свое и мое время, почитай книжки лучше.
_>Да, и кстати... Если ты хочешь настоящей динамики... Что у нас там скажет linq для такого случая (на sqlcpp11 или sql строках естественно без проблем реализуется): _>
query.Select(e => new { Result = cond ? e.FirstName : e.LastName});
G>>С ифами нет смысла, компилировать можно только статический запрос. Можешь ручками выписать все 8-16 штук, сам же предлагал https://msdn.microsoft.com/ru-ru/library/Bb399335(v=vs.100).aspx. _>Т.е. для твоих же любимых динамических запросов compiled query не работает? Т.е. получается, что с помощью linq у тебя вообще нет возможности сделать запрос без диких накладных расходов? )
Дикие они только в твоей голове. В реальности тыих никогда не заметишь. Даже с профайлером. Более того, linq тупо сгенерирует более быстрый запрос, чем ты сможешь выписать руками, даже с использованием sqlpp11. Конечно в случае нетривиальных запросов.
_>>>И снова у тебя плохо с чтением. Если ты потребуешь не то поле, то получишь ошибку. ) Я тебе об этом писал ещё в предыдущем сообщение. G>>В случае простого запроса. А как быть с джоинами?
_>И с join'ми тоже без проблем. Показать пример ошибки? )
Покажи пример, когда у тебя джоин навешивается после предикатов и вообще в другой сборе (dll).
Здравствуйте, koodeer, Вы писали:
K>Я согласен, что пользоваться такими библиотеками вовсе не сложно. Однако, в книгах по C# для начинающих обязательно рассказывается про linq. В доброй половине книг рассказывается про EF. K>В то время как в книгах по С++ даже для среднего уровня нет описаний использования подобных библиотек. Это я считаю серьёзным упущением. Разве что книги по QT относительно близки к этому, но обычно QT не считают программированием на чистом C++. K>Если бы появились пара-тройка книг по C++, где будет описано проектирование/создание приложений с нуля с использованием вот таких библиотек (async, ORM), то язык может обрести второе дыхание (ага, школота набежит). K>Кстати, никто не хочет написать такую книгу?
Да, есть такое. У C/C++ вообще с библиотеками нетривиальная ситуация. С одной стороны их больше, чем во всех остальных языках (причём во многих языка реализацию своих библиотек делают в виде биндингов к известным C/C++ решениям). Можно найти вообще на любой вкус и практически под любую задачу. А с другой стороны там дикий зоопарк, нет ничего общепринятого и т.п. Я уже не говорю про наличие какого-либо глобального фреймворка. Соответственно при изучение именно языка не совсем понятно что именно рассказывать, кроме стандартной библиотеки языка (которая традиционно очень бедная).
Единственным исключением из этой ситуации является Boost. Этот сборник библиотек уже стал по сути стандартом де факто в мире C++. Причём состоит он в основном как раз из весьма сложных внутри и лёгких снаружи шаблонных библиотек. И кстати кажется как раз по нему уже кто-то выпустил книгу. ))) Хотя лично я не вижу в таком смысла — зачем они, если к каждой библиотеке есть хорошая документация? Да, но вот sql библиотечки в Boost'е пока нет. Хотя я бы добавил как раз sqlcpp11 — вполне укладывается по концепции. ) И GUI библиотек там тоже нет... Так что в любом случае серьёзный проект на C++ сейчас использует целую кучу сторонних инструментов. Причём у каждой команды этот набор будет свой. )))
Здравствуйте, alex_public, Вы писали:
_>Да, я тоже не вижу никаких проблем для реализации подобного. Но в данной библиотечке такого вроде нет. Видимо автор посчитал что для "извращенцев" (которым надо динамически переключать проекции) хватит и того, что реализована та, максимальная динамика. )))
Не палился бы ты так, что не умеешь работать с БД. Проекция — первое что нужно для оптимизации запросов. Если тул не умеет выбирать ровно те поля, что нужны, то это сразу огромный минус к быстродействию. Ибо без проекций не построишь покрывающие индексы и заранее пессимизируешь любой запрос. Естественно проекция должна работать и с джоинами, и с подзапросами, и с группировками.
Динамичность запроса означает, что модуль (класс\фукнция) может взять запрос и навесить на него предикаты, проекции, группировки на свое усмотрение и получить новый запрос, который может передать еще куда-то.
Если же тебе надо полностью запрос выписывать в одном месте (как в твоих примерах), то нужно выполнить обратное действие — собрать все параметры на всех слоях приложения и передать в функцию, генерирующую запрос (в том числе проекции). И если вдруг один параметр меняется — надо аккуратно поправить все функции, которые передают параметры, а если вдруг меняется проекция (добавляется поле), то надо еще и обратно прокинуть везде результаты. Поддерживать такой код — ад.
Фреймворк, который не позволяет в DAL сгенерировать запрос, потом в BL навесить предикаты, а в PL проекции и пейджинг, фактически не имеет никаких преимуществ перед хранимками.
Здравствуйте, gandjustas, Вы писали:
_>>"Можно" — это конечно хорошо звучит. Так покажи, как такой код будет выглядеть на практике. Вот опять же на базе того твоего примера с if'ми. ))) G>С ифами нет смысла, компилировать можно только статический запрос. Можешь ручками выписать все 8-16 штук, сам же предлагал
Во-первых, на C++ ручками 2**N запросов выписывать не нужно — это автоматически разруливается с помощью метапрограммирования. То есть задав N runtime условий, и N кусочков запросов (да и вообще любого кода), можно автоматом получить оптимизированный код для 2**N случаев.
Возможно ли что-то подобное в рамках C#? (по N условиям получить автоматом 2**N Compiled Queries)
И как это будет выглядеть в случае когда куски запроса в разных dll?
Во-вторых, на C# противоядием от дорогого runtime обхода expression tree является Compiled Queries, так?
По твоим словам, "сила" LINQ именно в динамической композиции, а "вся суть" в том что запрос можно по кускам собирать в разных местах, так?
Теперь же выясняется что с "с ифами компилировать нет смысла".
Вывод напрашивается сам собой — от runtime penalty привнесённой инструментом, трудно избавится (или даже невозможно в ряде случаев) — был бы инструмент построен на иных принципах, этих издержек (или по крайней мере существенной их части) можно было бы избежать — с чего собственно и началась вся ветка.
Здравствуйте, gandjustas, Вы писали:
G>И в чем его непереносимость?
В том, что другие БД ничего не знают про /*+ RESULT_CACHE */. Т.е. если мы делаем движок, который должен работать со многими СУБД, то мы не сможем рассчитывать на эту полезную функцию.
G>Это не фича движка, а расширение. Расширяемость движка это хорошо, но чем больше точек расширения, тем ниже overall performance.
Какая разница то расширение или нет, если само кэширование всё равно происходит в БД (против чего ты так возражаешь). Ну и в данном случае оно не снижает производительность, а наоборот намного повышает. )
G>Через linq обычно быстрее и безопаснее, чем рукопашные запросы. Ты сам это прекрасно показал.
Эээ, что? ) Мы пока что только собрались начать измерения, а ты уже сбежал. Но практических результатов пока нет ни у тебя, ни у меня. Хотя я в общем то не сомневаюсь в итоге (зная внутреннее устройство linq), но конкретные цифры надо ещё показать...
_>>1. join'ов у тебя в примере не было, не надо врать: http://rsdn.ru/forum/flame.comp/6013255
G>Да-да, я специально опустил что там внутри select, думал ты догадаешься. Но если сам не догадался еще раз напишу: G>
G> x => new { x.A, x.B.C }
G>
G>Вторая часть проекции создает джоин. А операторы выше об этом не знают.
Так всё равно непонятно. Какие-то A, B, C... Ты напиши в терминах того нашего примера, что за хитрую проекцию ты хочешь.
_>>2. select у тебя был в конце, да. А я его перенёс в начало для внешней симметрии с остальными двумя примерами. Можно и обратно в конец — какая разница то, если он у тебя не динамический (см. ниже)? ) G>Разница огромная, select может добавить джоины, подзапросы и еще очень много чего.
Там у тебя было написано просто "проекция". Если же ты хочешь какие-то join'ы, подзапросы и т.п. с использованием уже готового запроса, то в sqlcpp11 это прекрасно реализовано. Более того, это продемонстрировано в примере, на который я уже раз 10 давал ссылку https://github.com/rbock/sqlpp11/blob/master/examples/select.cpp#L98.
G>Ты привел пример статического запроса, который в одном месте задается на стадии компиляции. Еще раз повторю, что такой случай крайне неинтересный. Попробуй по разным модулм (dll) растащить куски запроса. В этом и суть динамической композиции. Потребитель IQueryable совершенно не обязан знать как запрос был построен и может навесить свои проекции и ограничения. G>Если ты этого не понимаешь, то пожалуйста не продолжай. Не трать свое и мое время, почитай книжки лучше.
О, я смотрю, что в процессе дискуссии у нас начало появляться новое определение для динамического запроса. В начале вроде всё было приемлемо: статические запросы зашиты на этапе компиляции, а динамические меняют свой вид в зависимости от рантайм параметра. Теперь оказывается что и меняющие свой вид в рантайме запросы — это статические. Потому что они оказывается не находятся в разных dll. Мда, я конечно разных оппонентов видел, но ты — это нечто. )))
Да, и самое забавно, что всё это время мы возимся с примером, который привёл опять же ты. )))
G>Правильно писать так: G>
G>query.Select(e => new { Result = cond ? e.FirstName : e.LastName});
G>
Ох, это я конечно подставился, кривовато сформулировав задачку. Ты конечно же наверняка всё понял, но увидел лазейку и тут же ею воспользовался. ))) Давай ка переформулирую по честному. И так, как нам написать реально динамическую проекцию на linq? T.e. что-то вроде:
var query = context.Employees;
if(cond) query=query.Select(e => new {e.FirstName});
else query=query.Select(e => new {e.EmploymentDate, e.LastName});
...
query=query.Where(...);
С помощью sqlcpp11 подобная реально динамическая проекция без проблем записывается.
G>Дикие они только в твоей голове. В реальности тыих никогда не заметишь. Даже с профайлером. Более того, linq тупо сгенерирует более быстрый запрос, чем ты сможешь выписать руками, даже с использованием sqlpp11. Конечно в случае нетривиальных запросов.
Ну так давай измерим, в чём проблема то? )))
G>Покажи пример, когда у тебя джоин навешивается после предикатов и вообще в другой сборе (dll).
Здравствуйте, gandjustas, Вы писали:
_>>Да, и кстати... Если ты хочешь настоящей динамики... Что у нас там скажет linq для такого случая (на sqlcpp11 или sql строках естественно без проблем реализуется): _>>
Здравствуйте, gandjustas, Вы писали:
G>Не палился бы ты так, что не умеешь работать с БД. Проекция — первое что нужно для оптимизации запросов. Если тул не умеет выбирать ровно те поля, что нужны, то это сразу огромный минус к быстродействию. Ибо без проекций не построишь покрывающие индексы и заранее пессимизируешь любой запрос. Естественно проекция должна работать и с джоинами, и с подзапросами, и с группировками.
Здесь ты всё верно написал. Но это легко реализуется во всех обсуждаемых инструментах.
G>Динамичность запроса означает, что модуль (класс\фукнция) может взять запрос и навесить на него предикаты, проекции, группировки на свое усмотрение и получить новый запрос, который может передать еще куда-то.
G>Если же тебе надо полностью запрос выписывать в одном месте (как в твоих примерах), то нужно выполнить обратное действие — собрать все параметры на всех слоях приложения и передать в функцию, генерирующую запрос (в том числе проекции). И если вдруг один параметр меняется — надо аккуратно поправить все функции, которые передают параметры, а если вдруг меняется проекция (добавляется поле), то надо еще и обратно прокинуть везде результаты. Поддерживать такой код — ад.
G>Фреймворк, который не позволяет в DAL сгенерировать запрос, потом в BL навесить предикаты, а в PL проекции и пейджинг, фактически не имеет никаких преимуществ перед хранимками.
Ужас какой. Т.е. в твоих приложениях работа с sql размазана по всем слоям? Да "специалиста" с такими подходами нельзя и на пушечный выстрел подпускать к проектированию архитектуры приложения! Во всех нормальных движках, работа с БД всегда абстрагирована в отдельный тонкий слой, с которым другие слои приложения взаимодействуют уже в терминах самого приложения (а не sql).
Здравствуйте, alex_public, Вы писали:
_> Во всех нормальных движках, работа с БД всегда абстрагирована в отдельный тонкий слой, с которым другие слои приложения взаимодействуют уже в терминах самого приложения (а не sql).
А не получится. Хоть убейся, а когда надо чего-то писать в журналы, транзакционно, то это выглядит как реляционка и является реляционкой, и язык для нее — SQL.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, gandjustas, Вы писали:
_>>>"Можно" — это конечно хорошо звучит. Так покажи, как такой код будет выглядеть на практике. Вот опять же на базе того твоего примера с if'ми. ))) G>>С ифами нет смысла, компилировать можно только статический запрос. Можешь ручками выписать все 8-16 штук, сам же предлагал
EP>Во-первых, на C++ ручками 2**N запросов выписывать не нужно — это автоматически разруливается с помощью метапрограммирования. То есть задав N runtime условий, и N кусочков запросов (да и вообще любого кода), можно автоматом получить оптимизированный код для 2**N случаев. EP>Возможно ли что-то подобное в рамках C#? (по N условиям получить автоматом 2**N Compiled Queries) EP>И как это будет выглядеть в случае когда куски запроса в разных dll?
Такой задачи никогда не ставилось. Да и смысла немного в этом. Пара миллисекунд и то если попадания в кеш не было.
EP>Во-вторых, на C# противоядием от дорогого runtime обхода expression tree является Compiled Queries, так? EP>По твоим словам, "сила" LINQ именно в динамической композиции, а "вся суть" в том что запрос можно по кускам собирать в разных местах, так? EP>Теперь же выясняется что с "с ифами компилировать нет смысла".
Ты читаешь через строку? Я же говорю, что когда запрос сильно динамический, то нет смысла заранее генерить все варианты. Они могут тупо не встретиться в программе, а время обхода Expression Tree вносит настолько малый вклад в скорость, что даже не заметишь под микроскопом.
EP>Вывод напрашивается сам собой — от runtime penalty привнесённой инструментом, трудно избавится (или даже невозможно в ряде случаев) — был бы инструмент построен на иных принципах, этих издержек (или по крайней мере существенной их части) можно было бы избежать — с чего собственно и началась вся ветка.
Каких например?
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, gandjustas, Вы писали:
G>>И в чем его непереносимость? _>В том, что другие БД ничего не знают про /*+ RESULT_CACHE */. Т.е. если мы делаем движок, который должен работать со многими СУБД, то мы не сможем рассчитывать на эту полезную функцию.
Значит функция не такая уж и полезная.
G>>Это не фича движка, а расширение. Расширяемость движка это хорошо, но чем больше точек расширения, тем ниже overall performance. _>Какая разница то расширение или нет, если само кэширование всё равно происходит в БД (против чего ты так возражаешь). Ну и в данном случае оно не снижает производительность, а наоборот намного повышает. )
Да, кешироваь в БД неправильно. Каждый мегабайт памяти кеша отбирает мегабайт памяти у пула страниц, что снижает общее быстродействие. Слава богу авторы многих движков СУБД догадалсь не включать эту фичу по умолчанию (ну кроме mysql, он настолько медленный что по другому никак видимо).
G>>Через linq обычно быстрее и безопаснее, чем рукопашные запросы. Ты сам это прекрасно показал. _>Эээ, что? ) Мы пока что только собрались начать измерения, а ты уже сбежал. Но практических результатов пока нет ни у тебя, ни у меня. Хотя я в общем то не сомневаюсь в итоге (зная внутреннее устройство linq), но конкретные цифры надо ещё показать...
У тебя уже есть прекрасный "практический результат" — SQL-инъекция в коде. Так сказать предварительный отбор не прошел, прежде чем мерить что-то.
Что касается использования либы, то ты банально не сделал джоина.
_>Так всё равно непонятно. Какие-то A, B, C... Ты напиши в терминах того нашего примера, что за хитрую проекцию ты хочешь.
Не прикидывайся дурачком, все прекрасно ты понял. А если нет, то ты вообще зря тут пишешь.
_>>>2. select у тебя был в конце, да. А я его перенёс в начало для внешней симметрии с остальными двумя примерами. Можно и обратно в конец — какая разница то, если он у тебя не динамический (см. ниже)? ) G>>Разница огромная, select может добавить джоины, подзапросы и еще очень много чего.
_>Там у тебя было написано просто "проекция". Если же ты хочешь какие-то join'ы, подзапросы и т.п. с использованием уже готового запроса, то в sqlcpp11 это прекрасно реализовано. Более того, это продемонстрировано в примере, на который я уже раз 10 давал ссылку https://github.com/rbock/sqlpp11/blob/master/examples/select.cpp#L98.
Чето он в #if 0 завернут, компилируется вообще?
G>>Ты привел пример статического запроса, который в одном месте задается на стадии компиляции. Еще раз повторю, что такой случай крайне неинтересный. Попробуй по разным модулм (dll) растащить куски запроса. В этом и суть динамической композиции. Потребитель IQueryable совершенно не обязан знать как запрос был построен и может навесить свои проекции и ограничения. G>>Если ты этого не понимаешь, то пожалуйста не продолжай. Не трать свое и мое время, почитай книжки лучше.
_>О, я смотрю, что в процессе дискуссии у нас начало появляться новое определение для динамического запроса.
А какой смысл динамического запроса, если он всегда приводит к одному и тому же SQL? Его банально можно заменить одной строкой. Интересно как раз тогда, когда одной строкой заменить нельзя или это приведет к потере быстродействия. Мой пример бы именно такой, а ты все пытаешься его свести к более простому случаю.
G>>Правильно писать так: G>>
G>>query.Select(e => new { Result = cond ? e.FirstName : e.LastName});
G>>
_>Ох, это я конечно подставился, кривовато сформулировав задачку. Ты конечно же наверняка всё понял, но увидел лазейку и тут же ею воспользовался. ))) Давай ка переформулирую по честному. И так, как нам написать реально динамическую проекцию на linq? T.e. что-то вроде: _>
_>var query = context.Employees;
_>if(cond) query=query.Select(e => new {e.FirstName});
_>else query=query.Select(e => new {e.EmploymentDate, e.LastName});
_>...
_>query=query.Where(...);
_>
_>С помощью sqlcpp11 подобная реально динамическая проекция без проблем записывается.
Да лекго:
var query = context.Employees;
if(cond) query=query.Select(e => new {e.FirstName, LastName=""});
else query=query.Select(e => new {e.EmploymentDate, e.LastName});
...
query=query.Where(...);
Но важно как ты это будешь обрабатывать.
G>>Дикие они только в твоей голове. В реальности тыих никогда не заметишь. Даже с профайлером. Более того, linq тупо сгенерирует более быстрый запрос, чем ты сможешь выписать руками, даже с использованием sqlpp11. Конечно в случае нетривиальных запросов. _>Ну так давай измерим, в чём проблема то? )))
Так ты еще ни одного нетривиального запроса не родил. А на тривиальном какой смысл мерить? Там разница будет настолько несущественная, что нет смысла обсуждать.
G>>Покажи пример, когда у тебя джоин навешивается после предикатов и вообще в другой сборе (dll).
_>Я так чувствую, что ещё раз 10-15 я кину эту https://github.com/rbock/sqlpp11/blob/master/examples/select.cpp#L98 ссылку и возможно до тебя наконец дойдёт реальность. ))) Или может обязательно требуется скопировать нужный кусок оттуда и вставить на форуме? )
Он же не компилируется Или автор его просто так в #if 0 обернул?
Здравствуйте, alex_public, Вы писали: G>>Фреймворк, который не позволяет в DAL сгенерировать запрос, потом в BL навесить предикаты, а в PL проекции и пейджинг, фактически не имеет никаких преимуществ перед хранимками.
_> Ужас какой. Т.е. в твоих приложениях работа с sql размазана по всем слоям? Да "специалиста" с такими подходами нельзя и на пушечный выстрел подпускать к проектированию архитектуры приложения! Во всех нормальных движках, работа с БД всегда абстрагирована в отдельный тонкий слой, с которым другие слои приложения взаимодействуют уже в терминах самого приложения (а не sql).
Ты откуда свалился? ОРМ тем и занимается что абстрагирует. Любая абстракция над ОРМ — почти всегда ошибка. Тебе никакой C++ не поможет если ты начнешь свои абстракции над IQueryable городить, тормозить будет так, что единственное нормальное решение — выкинуть нафиг эти абстракции. Чем я регулярно и занимаюсь когда меня приглашают починить быстродействие очередной enterprise системы.
Речь, конечно, о легковесных ОРМ, которые позволяют таки проекции писать, а не о говне типа Hibernate.
ЗЫ. И еще никто и никогда не жаловался на скорость построения SQL из Linq.
Здравствуйте, gandjustas, Вы писали:
EP>>Во-первых, на C++ ручками 2**N запросов выписывать не нужно — это автоматически разруливается с помощью метапрограммирования. То есть задав N runtime условий, и N кусочков запросов (да и вообще любого кода), можно автоматом получить оптимизированный код для 2**N случаев. EP>>Возможно ли что-то подобное в рамках C#? (по N условиям получить автоматом 2**N Compiled Queries) EP>>И как это будет выглядеть в случае когда куски запроса в разных dll? G>Такой задачи никогда не ставилось.
Пусть и не ставилась — мне интересно возможно ли это в рамках C# или нужны внешние инструменты.
G>Да и смысла немного в этом. Пара миллисекунд
Пара миллисекунд практически на ровном месте. Да это же 1/50 секунды современного железа! В 3D приложениях за столько целый кадр отрисовывается, да — там по большей части GPU, но всё же — одно ядро CPU за это время может выполнить около миллиарда операций с плавающей точкой, а из RAM можно загрузить сотни мегабайт.
Для десктопных GUI приложений это может и не существенно, но для остальных применений может очень сильно ударить, тем более если запрос не один в секунду. Не зря же появляются такие вещи как Massive, PetaPoco, Dapper — значит есть на них спрос.
G>и то если попадания в кеш не было.
Какой конкретно cache?
EP>>Во-вторых, на C# противоядием от дорогого runtime обхода expression tree является Compiled Queries, так? EP>>По твоим словам, "сила" LINQ именно в динамической композиции, а "вся суть" в том что запрос можно по кускам собирать в разных местах, так? EP>>Теперь же выясняется что с "с ифами компилировать нет смысла". G>Ты читаешь через строку? Я же говорю, что когда запрос сильно динамический, то нет смысла заранее генерить все варианты. Они могут тупо не встретиться в программе,
Можно ли в runtime закэшировать Compiled Query для используемых динамических запросов?
G>а время обхода Expression Tree вносит настолько малый вклад в скорость, что даже не заметишь под микроскопом.
Ты же сам говорил про миллисекунды — это же чрезмерно много.
EP>>Вывод напрашивается сам собой — от runtime penalty привнесённой инструментом, трудно избавится (или даже невозможно в ряде случаев) — был бы инструмент построен на иных принципах, этих издержек (или по крайней мере существенной их части) можно было бы избежать — с чего собственно и началась вся ветка. G>Каких например?