Re[25]: Entity Framework за! и против!
От: vdimas Россия  
Дата: 30.06.14 19:12
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>>>Ты не понимаешь что такое expression tree.

V>>У нас называлось SqlOrOp, SqlAddOp, SqlJoinOp. И переопределение операторов в рамках C# отчасти выручало.

НС>Ты не понимаешь что такое expression tree. Это не просто способ конструирования дерева (тем более что дерево там совсем не 1 в 1 срезультирующим сиквелом), это еще и полная проверка компилятором, и полноценная работа всех средств анализа, рефакторинга и навигации.


Ага, пошли ходить зигзагами. ))
Рефакторинг работал. Параметры мы описывали как типизированные структуры/объекты, под них — ф-ии контекста. Приходилось. Собсно, сейчас описание табличных ф-ий или хранимок в дата-контексте происходит аналогично. Классика жанра, не Linq это придумал, ес-но.

Всей разницы, что мы объекты проекций описывали явно, вместо автогенерирования компилятором анонимных типов.

Собсно, и там и там основное преобразование AST->SQL. Твоё "не понимаешь, что без expression tree" и далее по тексту, показало лишь отсутствие воображения. А как вообще порождать SQL из объектной модели запроса? )) Конечно, наколотили свой аналог expression tree над выражениями SQL. Поддержали в синтаксисе как смогли через переопределенные операторы. Примерно в том виде, как ты написал, и было. Только вместо встроенных в провайдер "типизированных хинтов" была возможность приписать к каждой таблице и каждому запросу текстовые опции.

.From(db.Products + "with some option")



V>>Временная таблица уровня транзакции тебе в помощь для декомпозиции.

НС>Во-во, вот после таких декомпозиций и начинаются жуткие тормоза. Иногда отдельные DBA так надекомпозируют, что выборка пары десятков строк занимает минуту.

Я такого не помню даже на Celeron-300 ))
Просто временные таблицы уровня транзакции, действительно, неплохо выручали именно для целей декомпозиции. Это же просто представления в памяти.

НС>>>Описываемые мной проблемы присутствовали без какой либо рекурсии.

V>>Без рекурсии не нужны были временные таблицы.
НС>Вот я и говорю что не нужны. А были. В количестве. DBA блин, ему ж все заинкапсулячить надо в хранимки, даже то что следует делать на клиенте.

Слушай, ну я такого от C# разработчиков насмотрелся за эти 10 лет, бо "порог вхождения низок", что... Ну офигенный аргумент, однако, стоит только начать обобщать среднего разработчика на сам инструмент.


НС>>>Вот только перформанс с их выкидыванием, напоминаю, улучшался в разы.

V>>Тут бы хотелось взглянуть на до и после.

НС>NDA.


Та ладно... Прям маленький кусочек нельзя показать?

Объясню. С временными таблицами работали много и плотно еще во времена вторых пней. Никаких ужасов не было замечено даже на приличных выборках и алгоритмах. А уж на нынешней технике ты вообще бы разницу не заметил, по сравнению с современной формой запросов, поддерживающих рекурсию, где надобность в явно-задаваемых промежуточных данных сейчас отсутствует. Там твой сложный запрос будет компилироваться и по нему строится план дольше. Поэтому и спрашиваю.

V>> Бо эксперименты с рекурсивными возможностями показали, что особой разницы с аналогичным кодом на курсорах нет.

НС>Нет там рекурсии. От слова "совсем".

Ну тогда пример нерелевантный, бо ты как аргумент решил привести чьи-то кривые руки. Нафига там были временные таблицы, тогда? А какой был уровень видимости у этих таблиц? — базы, соединения или транзакции? Тоже сильно влияет на издержки, из-за принципиально разной модели блокировок (или полного её отсутствия в последнем случае). А то мож все дело в этом, что ты банально избавился от блокировок? Чудес ведь не бывает, всему должна быть причина. Никакая пошаговая интерпретация никакой хранимой процедуры не даст тебе минуту.


НС>>>Код LINQ запросов неизмеримо лучше поддается декомпозиции, чем императивный код на T-SQL.

V>>На стороне клиента. База его компилит каждый раз.
НС>Нет, не каждый раз. Сиквел прекрасно умеет кешировать скомпилированные запросы. Причем уже давно. Это ж, блин, азы.

Так если он каждый раз разный у вас? В вашем контроле-гриде? ))
Re[16]: Entity Framework за! и против!
От: Ночной Смотрящий Россия  
Дата: 30.06.14 19:13
Оценка:
Здравствуйте, Alexander Polyakov, Вы писали:

AP>Теперь введем естественное усложнение тестовой задачи:


Эксепшен тоже предполагается в SQL генерить?

myEnum == case MyEnum.Item1
  ? x > c1 && x < c2
  : myEnum == case MyEnum.Item2
        ? x > c1 && x < c2*c1
        : myEnum == case MyEnum.Item3
            ? x > c1 && x < c2%c1*17
            : ???


AP>P.S. в моем подходе это реализуется естественным образом


Зато теряется в нем намного намного больше.
Re[25]: Entity Framework за! и против!
От: vdimas Россия  
Дата: 30.06.14 19:21
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>>>В BLT определенные оптимизации делаются.

V>>В рамках логических/арифметических выражений?

НС>Почему такое условие? И о каких конкретно выражениях речь (в сиквеле, в отличие от универсальных языков, 3 типа выражений, а не один)?


О логических/арифметических, допустимых как предикат или вычислимое поле.
И хоть сколько типов выражений — это рояли не играет. Как-то с Синклером подробно уже обсуждали эту тему. В целевой РА всё-равно два типа.


V>>>>, то бишь без "понимания" самого запроса

НС>>>Что такое "понимание запроса"?
V>>Логически дублированные/эквивалентные части вычленять, например.

НС>Это без проблем с expression tree делается. Есть ли в конкретном провайдере такая оптимизация и насколько она вообще нужна — этот вопрос надо изучать отдельно.


Я не говорю, что expression tree тут помеха. Я говорю об эквивалентных преобразованиях выражений exists/any/some/join/select_from_subquery_with_cross_level_conditions.


V>>А если используется view в запросе

НС>Ну вот и не надо использовать view, если не нужны индексы по нему. А если индексы нужны, то и сиквел у себя уже ничего заинлайнить не сможет.

Для сиквела вьюха "прозрачна", как шаблон С++ для компилятора. Т.е. он может выбрать использовать уже скомпилённый её вариант, либо просто использовать её тело как источник текстового выражения для подстановки (грубо говоря). Т.е. использование вьюхи никак не гарантирует (или ограничивает) порядок просмотра индексов и таблиц. Даже её материализация — это лишь разновидность "хинта" для построителя плана запроса, эдакий еще один индекс среди прочих в запросе.
Re[26]: Entity Framework за! и против!
От: Ночной Смотрящий Россия  
Дата: 30.06.14 19:23
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Ага, пошли ходить зигзагами. ))

V>Рефакторинг работал.

И как, можно было решарпером переименовать поле в БД?

V>Всей разницы, что мы объекты проекций описывали явно, вместо автогенерирования компилятором анонимных типов.


При чем тут анонимные типы и проекции? Банальное выражение фильтра foo.Bar > 4 ты как записывал?

V>Конечно, наколотили свой аналог expression tree над выражениями SQL


Да я уже давно все понял про твой трехколесный велосипед. И все проблемы этого велосипеда тоже прекрасно знаю.

НС>>Вот я и говорю что не нужны. А были. В количестве. DBA блин, ему ж все заинкапсулячить надо в хранимки, даже то что следует делать на клиенте.

V>Слушай, ну я такого от C# разработчиков насмотрелся за эти 10 лет, бо "порог вхождения низок", что... Ну офигенный аргумент, однако, стоит только начать обобщать среднего разработчика на сам инструмент.

А нафига мне твои теоретические идеальные DBA, если и я и IT в реальных ситуациях видим совсем другое?

НС>>NDA.

V>Та ладно... Прям маленький кусочек нельзя показать?

Там немаленький кусочек.

НС>>Нет там рекурсии. От слова "совсем".

V>Ну тогда пример нерелевантный, бо ты как аргумент решил привести чьи-то кривые руки. Нафига там были временные таблицы, тогда?

Так проще писать. Сложные запросы на много страниц на SQL плохо читаемы, со средствами декомпозиции бедулька, CTE больше засирает чем упрощает порой. Поэтому берут выборку, кладут во временную таблицу, и потом последовательно в несколько шагов ее ковыряют. А если там еще и пейджинг, да total к нему нужен ... Это ж до последней версии такого уродца надо было сочинять. А тут наклал все во времянку, а потом из нее нужную страничку и total выдернул.

НС>>Нет, не каждый раз. Сиквел прекрасно умеет кешировать скомпилированные запросы. Причем уже давно. Это ж, блин, азы.

V>Так если он каждый раз разный у вас? В вашем контроле-гриде? ))

Разный, да не совсем. Количество вариантов в реальности сильно ограничено. А если кто сочинил что то новое — ну будет при первом запросе компиляция, не смертельно.
Re[26]: Entity Framework за! и против!
От: Ночной Смотрящий Россия  
Дата: 30.06.14 19:28
Оценка:
Здравствуйте, vdimas, Вы писали:

НС>>Почему такое условие? И о каких конкретно выражениях речь (в сиквеле, в отличие от универсальных языков, 3 типа выражений, а не один)?

V>О логических/арифметических, допустимых как предикат или вычислимое поле.

Предикат и вычислимое поле — это разные типы, которые не во всех диалектах даже совместимы. И да, по предикатам оптимизации кое какие точно делаются.

НС>>Это без проблем с expression tree делается. Есть ли в конкретном провайдере такая оптимизация и насколько она вообще нужна — этот вопрос надо изучать отдельно.

V>Я не говорю, что expression tree тут помеха. Я говорю об эквивалентных преобразованиях выражений exists/any/some/join/select_from_subquery_with_cross_level_conditions.

Ну и в чем, по твоему, проблема?


НС>>Ну вот и не надо использовать view, если не нужны индексы по нему. А если индексы нужны, то и сиквел у себя уже ничего заинлайнить не сможет.

V>Для сиквела вьюха "прозрачна", как шаблон С++ для компилятора.

Ну так и для линка метод, возвыращающий IQueryable, тоже прозрачен, представляешь?

V> Т.е. он может выбрать использовать уже скомпилённый её вариант


Вот ты привязался к компиляции. Для современного железа, даже если закрыть глаза на кеширование, компиляция запросов особой нагрузки не создает. А с кешированием на это смело можно забить, о чем, кстати, прямо говорится в рекомендациях МС.
Re[21]: Entity Framework за! и против!
От: vdimas Россия  
Дата: 30.06.14 19:34
Оценка:
Здравствуйте, IT, Вы писали:

V>>В тех проектах, где я участвовал и был выделенный программист БД, кол-во хранимок+сохраненных запросов было порядка 2к на примерно полторы-две сотни таблиц.

IT>Поражаюсь твоему умению представлять худшие из преданных анафеме практик в полубожественном свете

А ну да, ну да. Я ж опять забыл, куда я влез, со своим немытым...

Странно только, что все банки и все биржи именно так и работают до сих пор, и никакого линка там быть не может в принципе. А уж банков и бирж сейчас — как собак не резанных.

"Полубожественный" доставило. Но это следовало понимать не как божественный, а как "бинго!" для оппонента, от которого за версту несет "поколением next". У меня возникла догадка, она подтвердилась. Всего-то и делов. Другой мир, прям. Других слов нет.
Re[17]: Entity Framework за! и против!
От: Alexander Polyakov  
Дата: 30.06.14 19:53
Оценка:
НС>
НС>myEnum == case MyEnum.Item1
НС>  ? x > c1 && x < c2
НС>  : myEnum == case MyEnum.Item2
НС>        ? x > c1 && x < c2*c1
НС>        : myEnum == case MyEnum.Item3
НС>            ? x > c1 && x < c2%c1*17
НС>            : ???
НС>

О чем я и писал два дня назад, средства декомпозиции не развитые, а довольно скудные. Они приводят к неестественным извращениям над кодом. Человек воспринимает код через текст. Поэтому естественным способом декомпозиции является декомпозиция близкая к нарезке фрагментов текста. Фрагмент текста можно окружить условным оператором, выделить в метод и т.д. Это естественная нарезка текста, и именно такое предлагают большинство языков при работе с обычным кодом. В примере это продемонстрировано на IEnumerable<T>. Такое свойство хочется иметь и в коде по работе с БД. Текстовые запросы дают такое свойство. А проблемы, связанные с текстовыми запросами, оказалось можно решить без потери этого фундаментального свойства.

НС>Эксепшен тоже предполагается в SQL генерить?

Не в том направлении мыслишь, условия для генерации эксепшена не зависят от серверных данных, поэтому эти условия можно вычислять на клиенте. В этом направлении и надо было ресёчить команде, которая делала LINQ к БД. Компилятор должен специальным образом обрабатывать смесь кода, часть которого выполняется на клиенте, а часть на сервере. Довольно интересно насколько далеко можно продвинуться в этом направлении. Но они почему-то остановились в самом начале пути. И оставили нас с куцыми экспрешенами.

НС>Зато теряется в нем намного намного больше.

Ты сильно переоценивает реальные потери.
Re[27]: Entity Framework за! и против!
От: vdimas Россия  
Дата: 30.06.14 19:59
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>>>Почему такое условие? И о каких конкретно выражениях речь (в сиквеле, в отличие от универсальных языков, 3 типа выражений, а не один)?

V>>О логических/арифметических, допустимых как предикат или вычислимое поле.

НС>Предикат и вычислимое поле — это разные типы, которые не во всех диалектах даже совместимы. И да, по предикатам оптимизации кое какие точно делаются.


Nullable булевское поле имеет тот же тип, что и предикат. И вообще, я плохо понимаю причины твоих уточнений именно этого момента.
Предикат может выглядеть как (арифметическое_выражение > @X). Левая часть пригодна для упрощений. Тот же самый механизм пойдет и на вычислимое поле, по которому идет join, например.


V>>Я не говорю, что expression tree тут помеха. Я говорю об эквивалентных преобразованиях выражений exists/any/some/join/select_from_subquery_with_cross_level_conditions.


НС>Ну и в чем, по твоему, проблема?


Я пока всё еще уточнял вопрос. Такие оптимизации делаются?
Тем более, что ты заговорил о диалектах, то бишь о ситуациях, когда не поддерживаемые в целевом диалекте SQL конструкции надо выражать через другие, так?
Просто в сиквеле-сервере дублирующие подзапросы могут схлопываться аж бегом, если эквивалентное выражено разными с-вами языка. А в движке Access или каком еще — дудки.



НС>>>Ну вот и не надо использовать view, если не нужны индексы по нему. А если индексы нужны, то и сиквел у себя уже ничего заинлайнить не сможет.

V>>Для сиквела вьюха "прозрачна", как шаблон С++ для компилятора.

НС>Ну так и для линка метод, возвыращающий IQueryable, тоже прозрачен, представляешь?


Нет, не прозрачен, если содержит маппинг на вьюху. Тело этой вьюхи не прозрачно для клиентской стороны и оптимизациям не подлежит. См исходный умозрительный пример.
Re[10]: Entity Framework за! и против!
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 30.06.14 20:05
Оценка: -1
Здравствуйте, IB, Вы писали:

IB>Здравствуйте, gandjustas, Вы писали:



G>>Покажи пример где EF последней версии генерит неэффективный запрос.



IB>
IB>     Order[] orders = context.Set<Order>().Include( o => o.Products).ToArray();
IB>

IB>Генерится SQL, который в два раза менее эффективен, чем генерил в свое время linq2SQL. И это один из наиболее частых сценариев, то есть это приговор инструменту...
А ты что с чем сравниваешь? Мапить many-to-many никто кроме ef не умеет, поэтому даже сравнить не с чем...
Перепиши с промежуточной таблицей\классом проблем не будет, делает красивые inner join.


IB>А знаешь почему запрос такой тормозной и они так и не поправили его за пять лет? Потому что для корректной работы маппинга EF через внутреннюю модель, нужно сгенерить дополнительный столбец, по которому не построишь индекс, а потом еще и отсортировать по этому столбцу.

IB>То есть нельзя просто взять и поправить генерацию запросов для сценария потомок-родитель — нужно переделывать всю архитектуру генерации запросов. А эта архитектура, в свою очередь, явилась следствием дурацкой идеологии.
IB>Как видишь, иногда полезно все-таки понимать изначальную идею решения и оценивать ее адекватность, а не бросаться сразу осваивать все что предложили.
По странному стечению обстоятельств именно эта кривая архитектура\идеология позволяет мапить many-to-many
Re[22]: Entity Framework за! и против!
От: IT Россия linq2db.com
Дата: 30.06.14 20:06
Оценка: +4 :))
Здравствуйте, vdimas, Вы писали:

V>>>В тех проектах, где я участвовал и был выделенный программист БД, кол-во хранимок+сохраненных запросов было порядка 2к на примерно полторы-две сотни таблиц.

IT>>Поражаюсь твоему умению представлять худшие из преданных анафеме практик в полубожественном свете

V>А ну да, ну да. Я ж опять забыл, куда я влез, со своим немытым...


Ну так помой. А то нимб с немытым не очень сочетается

V>Странно только, что все банки и все биржи именно так и работают до сих пор, и никакого линка там быть не может в принципе. А уж банков и бирж сейчас — как собак не резанных.


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

V>"Полубожественный" доставило. Но это следовало понимать не как божественный, а как "бинго!" для оппонента, от которого за версту несет "поколением next". У меня возникла догадка, она подтвердилась. Всего-то и делов. Другой мир, прям. Других слов нет.


Ты считаешь, что лучше, когда от тебя за версту несёт нафталином и гниловатым запахом разлагающихся технологий? Прямо некрофилизм какой-то. Фу. Ужас.
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: Entity Framework за! и против!
От: Dair Россия  
Дата: 30.06.14 20:11
Оценка:
Я, увы, не настолько хорошо знаю C# и совсем не знаю EF/NHibernate, чтобы понять, что делает этот код.
Re[4]: Entity Framework за! и против!
От: Alexander Polyakov  
Дата: 30.06.14 20:24
Оценка:
D>Я, увы, не настолько хорошо знаю C# и совсем не знаю EF/NHibernate, чтобы понять, что делает этот код.
Делает он то же самое, что вот этот old-school вариант:
var messageInfo = Query<IMessageInfo>.New(new {id}, @"
SELECT  f.ShortName ForumShortName, m.CreatedOn, m.Subject, a.Origin
FROM    Message m
        JOIN Forum f ON f.Id = m.ForumId
        OUTER JOIN Account a ON a.Id = m.AccountId
WHERE   m.Id = @id AND f.ReadLevel <= @Account.AccessLevel
").SingleOrDefault();
return messageInfo == null
           ? ErrorMessageBox("Сообщение не найдено") 
           : View(new MessageModel(id, messageInfo, FormatMessageBody(id, messageInfo.Origin)));
Re[28]: Entity Framework за! и против!
От: Ночной Смотрящий Россия  
Дата: 30.06.14 21:15
Оценка: +1
Здравствуйте, vdimas, Вы писали:

V>Nullable булевское поле имеет тот же тип, что и предикат.


Нет. "WHERE tbl.BoolField" в большинстве диалектов написать нельзя, только "WHERE tbl.BoolField = 1".

V> И вообще, я плохо понимаю причины твоих уточнений именно этого момента.


Потому что обрабатываются разные типы обычно существенно по разному.

V>>>Я не говорю, что expression tree тут помеха. Я говорю об эквивалентных преобразованиях выражений exists/any/some/join/select_from_subquery_with_cross_level_conditions.

НС>>Ну и в чем, по твоему, проблема?
V>Я пока всё еще уточнял вопрос. Такие оптимизации делаются?

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

НС>>Ну так и для линка метод, возвыращающий IQueryable, тоже прозрачен, представляешь?

V>Нет, не прозрачен, если содержит маппинг на вьюху.

Цитирую сам себя: "Ну вот и не надо использовать view".
Re[18]: Entity Framework за! и против!
От: Ночной Смотрящий Россия  
Дата: 30.06.14 21:15
Оценка:
Здравствуйте, Alexander Polyakov, Вы писали:

AP>О чем я и писал два дня назад, средства декомпозиции не развитые, а довольно скудные.


По сравнению с SQL на три головы выше.

AP> Они приводят к неестественным извращениям над кодом.


Что в приведенном мной коде неестественного?

AP>Поэтому естественным способом декомпозиции является декомпозиция близкая к нарезке фрагментов текста.


Ложное утверждение. Особенно когда куски приходится параметризовать. И в случае таких нарезок твой статический чекер идет лесом, так как запрос собирается динамически.

AP> Фрагмент текста можно окружить условным оператором, выделить в метод и т.д. Это естественная нарезка текста


Текста — может быть. А декомпозированный SQL запрос, на практике, в 99% случаев собирается склеиваиванием датасетов и дописыванием в предикаты по AND. Для всего этого возможностей выражений в C# за глаза.
Ты же пытаешься вытащить какие то экзотические случаи, которые не в кажом проекте вообще бывают, и на основании этого заявить, что это все хуже чем фатальнейший прощелк с девэкспириенсом в твоей библиотечке, вылазящий строго на каждом запросе строго при каждой его модификации.
А обоснование я увидел ровно одно — личне тебе некомфортно не видеть SQL. Браво, чо.

НС>>Эксепшен тоже предполагается в SQL генерить?

AP>Не в том направлении мыслишь



AP>, условия для генерации эксепшена не зависят от серверных данных, поэтому эти условия можно вычислять на клиенте.


А это неважно. Наложение эксепшена на ленивое функциональное выражение — жестяная жесть. Такой код должен приводить только к ошибке при компиляции запроса.

НС>>Зато теряется в нем намного намного больше.

AP>Ты сильно переоценивает реальные потери.

Или ты их недооцениваешь. Хотя фик тебя знает, может у тебя в качестве IDE какой нибудь Far или древняя студия без решарпера. Тогда тебя можно понять.
Re[4]: Entity Framework за! и против!
От: Ночной Смотрящий Россия  
Дата: 30.06.14 21:31
Оценка: 2 (1)
Здравствуйте, Dair, Вы писали:

D>Я, увы, не настолько хорошо знаю C# и совсем не знаю EF/NHibernate, чтобы понять, что делает этот код.


SQL, надеюсь, понимаешь?
SELECT TOP 1
  f.ForumShortName, m.CreatedOn, m.Subject, a.Origin, mt.Text
FROM Messages m
  JOIN Forums f ON f.ID = m.ForumID
  OUTER JOIN Accounts a ON a.ID = m.AccountID
  JOIN MessageTexts mt ON mt.ID = m.ID
WHERE m.ID = @id AND f.ReadLevel <= @accessLevel

Приведенный код открывает коннект к БД, выполняет этот sql с передачей значений двух параметров, читает результат. Если записей нет — возвращает null, если есть — создает и заполняет данными экземпляр класса MessageModel (при этом вызывается функция FormatMessageBody с частью полей результата) и возвращает этот экземпляр. При этом все типы в запросе, его параметрах и результате контролируются комспилятором.
Теперь понятно?
Re[19]: Entity Framework за! и против!
От: Alexander Polyakov  
Дата: 30.06.14 21:59
Оценка: :)
AP>> Они приводят к неестественным извращениям над кодом.
НС>Что в приведенном мной коде неестественного?
Запись switch через оператор '?'. Зачем придумали switch он не нужен, ага?

AP>>Поэтому естественным способом декомпозиции является декомпозиция близкая к нарезке фрагментов текста.

НС>Ложное утверждение. Особенно когда куски приходится параметризовать.
Ты прочитал невнимательно, слово "близкая" пропустил.

НС>И в случае таких нарезок твой статический чекер идет лесом, так как запрос собирается динамически.

Это ложь! Просто нет слов! После нескольких десятков постов выясняется, что ты ничего не понял. Просто ничего, ни капли. Даже не пытался понять.
Re[22]: Entity Framework за! и против!
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 30.06.14 21:59
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, gandjustas, Вы писали:


G>>Даже банальная математика говорит что при 2к хранимок и 200 таблиц это минимум 10 хранимок на таблицу, а скорее всего в два раза больше. Любая правка в исходной таблице — нужна синхронная правка около 20 хранимок. Команда из 3-4 программистов будет поля править раз в день, DBA от такого сдохнет.


V>Во-во, и после этого ты еще поучаешь коллег.

После чего?

V>Все запросы всей базы верифицируются после изменений.

Кем? и зачем?

G>>При этом Linq позволяет генерировать больше и более специализированные запросы, чем выделенный DBA.


V>Ты плохо представляешь себе работу DBA. В 90% случаев от "слепит" тебе результат из уже готовых view, табличных ф-ий или уже готовых SP.

Это ты плохо представляешь.
Типичная проблема. Надо сделать функцию с параметром. Если параметр null, то надо вернуть все записи, у которых поле — null. Если поле не null, то нужно использовать предикат.
DBA может пойти двумя путями:
1) Сделать две функции, одну для null, другую для не-null и выбирать на строне приложения
2) Написать универсальную функцию, примерно так:

CREATE FUNCTION [dbo].[Function]
(
    @shipDate datetime
)
RETURNS TABLE AS RETURN
(
    SELECT * from Transactions t
    where t.ShippedDate = @shipdate or (@shipDate is null and t.ShippedDate is null)
)


Использовать IF или клеить строки на стороне базы не рассматриваем, ибо контекст теряется запроса.

Естественно 99% DBA выберут второй варинат, ибо они — люди, и чем меньше им поддерживать, тем лучше.
Программисты потом напишут один вызов:

select ... from Function(@p)

План для этого запроса закешируется и дальше происходят неприятные вещи.
Оказывается что null селективность низкая и SQL использует scan. А для конкретного значения селективность высокая и стоит использовать seek. Но план запроса кешируется при первом вызове и если первый был вызов был с параметром null, то SQL всегда будет делать scan. Это называется Parameter Sniffing Problem (PSP).


Чтобы такого не было вешают хинт — OPTION(RECOMPILE) и SQL начинает перекомпилировать план на каждый запрос.
Если такой запрос выполняется часто, то SQL большую часть времени начинает компилировать запросы.
Тогда DBA делает хранимку, которая внутри делает склейку строк с учетом проекций, постраничного разбиения и подобных вещей, а потом мучительно её поддерживает, потому что нет способов валидировать динамические запросы внутри SQL.

А если мы используем Linq, то получается так:

private static IQueryable<Transaction> GetValues(IQueryable<Transaction> query, DateTime? dateTime)
{
    if (dateTime.HasValue)
    {
        return query.Where(t => t.ShippedDate == dateTime.Value);
    }
    else
    {
        return query.Where(t => t.ShippedDate == null);
    }
}

В этом случае Linq генерит разные запросы когда параметр null и когда не-null. Это значит что PSP больше не наступает.


V>У грамотного разработчика большое повторное использование кода. Соответственно его вариант будет намного-намного проще твоего, построенного на Linq с 0-ля.

Внезапно в Linq гораздо проще делать декомпозицию. См выше.


V>Я говорил про вид запросов. Одно и тоже сложное выражение можно вертеть многими способами. На Linq это банально неудобно, т.к. не очевидно. Например, очередность и взаимная вложенность подзапросов запросто может зависеть от конкретного провайдера, порождающего код SQL.

SQL сам (пере)упорядочивает inner join и эквивалентные ему подзапросы.
В случае внешних джоинов от порядка зависит результат и все известные мне linq-провайдеры делают джоины ровно в том порядке, в котором укажешь в коде.


V>А рассуждения из цикла "MS SQL умный и сам составит нужный план" — и есть детсад. Это только на относительно несложных запросах.

Это на любых запросах. Механизм работы оптимизатора не отличается.

V>Вложи 3 раза exists/any друг в друга и получишь, что вертя внешней формой одного и того же запроса будешь менять конечный план.

Это ни о чем. Приводи запрос и план, будем смотреть.

V>С любыми запросами на групповое обновление — беда. А их бывает дохрена в учетных системах. Linq тебе поможет в обновлении только пообъектно, то бишь построчно. Хаха, как грится.

https://github.com/loresoft/EntityFramework.Extended#batch-update-and-delete
https://github.com/linq2db/linq2db/blob/master/Source/LinqExtensions.cs (см методы Delete и Update)


G>>Что ты считаешь "плохим" SQL и почему Linq его генерит сам?

V>Потому что программист борется за лучшую читабельность, вестимо. Это же на уровне мозжечка, на то он и программист. А по закону подлости, наилучшим образом читаемый Linq-запрос не всегда самый лучший.
Еще раз: что ты считаешь плохим SQL?

V>>>Куда тянется? Если это внутренний подзапрос, то ничего никуда не тянется.

G>>С чего ты взял? От проекции в запросе (неважно подзапрос это или нет) зависит какой план построит SQL, а это напрямую влияет на быстродействие. Конечно кроме случаев, когда ты используешь подзапрос в exists выражении.

V>Да любой подзапрос, если его поля используются только для построения условий на других уровнях — там вовсе не надо указывать никакие проекции, "унутре" всегда читается строка в ОП целиком (кроме тех самых блобов и text), бо MS SQL хранит построчно.

Ты про индексы в курсе? SQL может не читать данные таблицы вообще, только индекс.
А еще есть columnstore индексы...


V>Да. Серьезные приложухи на стороне базы — это ад для современных программистов. Это и так известно. Но выхода не было, по тем ресурсам аппаратуры, что были еще до времен MS SQL 2005. Сейчас то же самое с той лишь разницей, что эта граница отодвинулась примерно в 5 раз. Но она не исчезла. Просто да, многие нагрузочные раньше приложения стали простыми с т.з. требуемой мощности. Но далеко не все. Серьезные по размерам базы всё так же обслуживаются DBA по-старинке, я знаю это их первых рук, как грится.

Во первых это не значит что это хорошо. Во вторых причины скорее исторические, чем объективная необходимость.

V>>>Опять же. Есть такой фактор — владение проблемой. Когда программист SQL пишет запросы, он "видит" происходящую картинку. Именно так. Нет эдакого "разрыва" кода и базы. Просто индексы, статистику, материализцию view и прочее — такие решения принимает человек, который в этом "варится". А программист C# в этом не варится нифига, что бы кто не говорил.

G>>Проблема в том, что "картинка" которую он "видит" может с реальным потребностями и проблемами расходиться. Именно так часто и получается.
V>Бла-бла-бла. Реальную картинку даешь не ты, мега-программист, а характер самих данных и конечно статистика. Но её тоже надо уметь читать и понимать.
Наиболее реальной картиной обладает тот, кто делает UI или Public API системы. Если дать ему формировать запросы с помощью linq и научить как правильно это делать, то DBA много нового узнает о системе. Видел это вживую.


V>Клинические они по самому сценарию. Вот тебе идёт обновление одной и той же немалой таблицы тысячи раз в секунду и десятки тысяч раз — запросы из неё. Хоть ужом извернись, но без ручного ковыряния хинтами ты нормально ничего не сделаешь. Часто таблицу разбивают на 2, а то и 3: актуальные (наиболее меняющиеся) данные, реже меняющиеся, исторические. Иногда делят домены на поддомены и т.д. и т.п.. И периодически перемещают данные м/у таблицами. И для разных операций разные уровни блокировки.

Чушь какая-то. Если одновременно много записей и чтений нужно:
1) Сделать для чтения покрывающие индексы, чтобы весь запрос отдавался через index seek
2) Сделать stagingtable, без индексов, куда будут все писать, желательно с низким уровнем изоляции
3) Повесить на sqlagent задачу раз в интервал переносить данные stagingtable в основную таблицу одним insert select
4) Пересчитывать статистику по индексам когда нагрузка минимальная
Конечно если запросы делаются через хранимки, то так не получится.

G>>EF кстати генрит базы с READ_COMMITED_SNAPSHOT, так что блокировки, скорее всего, не станут проблемой.


V>Для батча с построчным изменением нескольких объектов? Ню-ню...

Да, для него тоже.

V>Ты хоть знаешь, что еще лет 10 назад за подобное любой вменяемый DBA тебя просто расстрелял бы? ))

10 лет назад в SQL Server не было SNAPSHOT, а в Oracle только он и есть с первых версий.

V>При небольшом кол-ве построчных изменений, всё можно вложить в один запрос обновления, а не в батч, где каждый раз идет скан по таблице. Один скан на 3 ключа дешевле, чем три скана на один ключ. Хотя итоговая "математическая" сложность эквивалентна. Но вот подиж ты.

Какой скан, ты о чем? Мы все еще о SQL Server?
Что такое скан на три ключа?


V>>>Ну и "длинные транзакции" всё-равно на стороне базы исполняются на многие порядки быстрее (с тащтельно подобранными уровнями блокировки), чем последовательность вызовов Linq с клиента базы в рамках одной транзакции. От этого ты вообще никак не убежишь.

G>>"Длинные транзакции" это что?
V>Это начало транзакции, более одной операции, конец транзакции.

G>>Если у тебя один селект работает полчаса это длинная транзакция?

V>Это идиотизм даже по меркам 10-тилетней давности. А уж на нынешних мощщах за это кастрировать надо. Чтобы не портить общечеловеческий генофонд. Я тут ему про актуальные/исторические данные распинаюсь, про поддомены, а он мне селект на пол-часа.

так это является "длинной транзакцией"?

G>>А 1000 инсертов за секунду, отправленных одним батчем? И то и другое вполне может быть сделано как Linq, так и написано вручную.

V>Тупые 1000 инсертов за секунду позволял сервак на Pentium-III в своё время.
А это является "длинной транзакцией"?

V>>>Триггера те же и т.д.

V>>>Понимаешь, все эти триггера, длинные транзакции и прочее используют какую-то кодовую базу SQL, которая УЖЕ есть на стороне базы. Смысл её дублировать в Linq?
G>>А кто говорит что надо дублировать? Типичное распределение чтения\записи в веб-приложении — 98\2. Об оптимизации записи речи в этом плане не идет.



V>>>Есть мильон служебных хелперов, и? Эти хелперы связаны с состоянием базы. Ты чуть подмандил свой запрос, и твой вызванный ранее plan guide стал бесполезен. Или ты каждый раз перед целевым запросом будешь настраивать plan guide?

G>>План-гайд или хинты для каждого запроса — индикатор неграмотного DBA. В базе много средств построения оптимальных планов без хинтов. Увы не все это знают. linq в этом плане помогает, ибо не дает писать хинты.

V>Ясно. Этот вопрос ты решил тщательно замылить.

V>Ты мне конкретный сценарий дай. Вот у меня идёт постоянная и одновременно частое обновление на таблице — регистре остатков. Частоту я тебе уже приводил. Без хинтов блокировок там ловить нечего. Просто поверь. Не работает.

1) Поставь READ_COMMITED_SNAPSHOT.
2) Сделай материализованное представление для остатков.
3) Сделай хорошие проекции.
4) сделай покрывающие индексы.

Хинты блокировок это от незнания.


V>Я одно не понимаю! Какое нафиг отношение к нашему обсуждению имеют ГЛОБАЛЬНЫЕ настройки базы (типа твоего read_commited_snapshot), когда речь о тюнинге конкретных запросов? Этих глобальных настроек многие десятки и что с того?

А ты попробуй почитать что означает read_commited_snapshot.

V>Я не увидел удобного сценария, как тюнить хинтами запросы в Linq, кароч.

План-гайды


V>>>Про impendance mismatch я вообще молчу. С грамотным DBA я наблюдал ровно противоположное — у всех всё выспросит, заставить оформить требования, ни байта лишнего из базы не отдаст.

G>>Теоретически такое может быть, но сильно удлиняет цикл разработки и повышает нагрузку на этого самого DBA.
V>Дык, у него ЗП не меньше, чем у программиста. Каждый пусть занимается своим делом.
А какой смысл в этом?


V>>>Про количество хранимок и вьюшек я написал выше. Я сразу в твоих аргументах ощутил специфический опыт, когда разработка на стороне базы фактически отсутствовала. ОК! В условиях объективной невозможности вести разработку на стороне базы, Linq тебе в помощь.

G>>С чего ты взял что отсутствовала?

V>Ну или опыт был печален. Блин, с вами линоководами смешные разговоры выходят. Как послушаешь вас, так до Linq ни одного приложения на БД толком не взлетело и это идёт как основной аргумент. Ребят, это у ВАС не взлетело. А весь мир давно успешно пользуется базами без Linq. Например, ни в один банк ты со своим Linq не сунешься, кроме как для маргинальной неспешной аналитики под прихоть и ответственность какого-нить старшего менеджера. В рабочий процесс ЭТО не пустят на сегодня. Таковы реалии.

С чего ты взял? Взлетало и без linq, но больше времени на это уходило.


V>>>Правда, и до Linq был тот же Hibernate/NHibernate со своим построителем запросов. ))

G>>там постритель запросов с трудом осиливает джоины и предикаты. Подзапросы и проекции — жопа.
V>Нормально он осиливает. Если пользоваться в синтаксисе "объектного построителя" как мне тут рядом давали для демонстрации прикрепления хинтов, то точно так же всё.
Ну покажи как подзапрос сделать в CriteriaAPI.


V>Да не в этом дело. А в том, что когда вся база легко садится в ОП сервака, то наш разговор теряет смысл сам собой. И вообще становится дичью, бо надобность реляционки в таком сценарии сильно под вопросом.

Гарантировать что БД всегда влезает в память довольно сложно.


G>>>>Лет 7-8 назад такое было повсеместно, потом появился Linq и больше C# программистов стало понимать что происходит в базе и как сделать оптимальной работу с СУБД.

G>>>>Но тебя это, видимо не коснулось. Не беспокойся, еще не поздно изучить.

V>>>Что ИМЕННО надо изучить?

G>>Как работает SQL.

V>Давай конкретней. Особенно про момент, когда программист Linq волшебным образом начинает БОЛЬШЕ ПОНИМАТЬ, что происходит в базе.



V>>>И КАК C#-программист начнет понимать происходящее в базе, особенно, если разные программисты разрабатывают разные абстрактные предикаты, из которых собирается целевой запрос "кем-то еще"? Даже по неизвестной заранее таблице? Как преодолеть ту пропасть между мышлением "типами" у программиста и "доменами" у DBA? ))

G>>Изучать и SQL, и C#.
V>Это не ответ на выделенное. Ты хоть вопрос понял?
Это именно ответ на выделенный вопрос.



V>>>Нет, не динамические. Подготавливается заранее view, скрывающий всю сложность низлежащего запроса, а уже на результат можно накладывать сортировку и фильтрацию. View делается DBA, конкретный тупой SQL-запрос к этому view пишет программист. Но он же тривиальный, много ума не надо, бо по специально заточенному view идёт тупой "select * from ... where ... order by ...".

G>>так where и orderby каждый раз будут разными, это все равно не динамический запрос?
V>ОК, динамический. Но недостойный ни внимания, ни обсуждения.
Почему?

V>Просто, действительно динамически в моей практике строили запросы только с изменяемой структурой. А описанный сценарий требует лишь однажды написанной вьюшки на стороне базы. Пусть эту вьюшка будет унутре сколь угодно сложной, но прилепить условия и сортировку к ней можно задешево без всяких Linq.

Так что такое "динамический запрос"?


G>>>>Что ты имеешь ввиду под динамическими запросами?


V>>>С динамической структурой, затрагивающей подзапросы. Т.е. где в условиях идут еще выборки или групповые операции над ними, типа exists, any и т.д. из этой оперы. С перекрестными связями по-условию м/у подуровнями — прикольнее всего. ))

G>>А чем такие запросы в принципе отличаются от "select * from .. where ..." где предикаты формируются в UI (с точки зрения SQL)?
V>Чем сложнее тело вьюшки, тем более больше отличий, вестимо.
V>Ты всерьез спрашиваешь или как? ))
Серьезно спрашиваю, ибо алгоритмы SQL не отличаются в этом случае.


V>>>Это список полей статичный, а запрос к базе может быть очень даже динамичным в том смысле, что я написал выше.

G>>Увы, не могу понять твое определение динамичности. Для SQL это такой же запрос.


G>>>>Давай по порядку: что ты называешь динамическими запросами?


V>>>Ну я заведомо исключил из них те, где меняются простые параметры, бо для ТАКИХ сценариев вообще Linq не облокотился и обсуждать сразу нечего.

V>>>Я рассматриваю те сценарии, где Linq еще хоть зачем-то нужен, например, если ты заранее не знаешь, какой будет конечный join.
G>>Что значит "заранее не знаешь" ?

V>Ну вот так. Добавил сравнение со статистикой — вот тебе лишняя операция реляционной алгебры (всё-равно все подзапросы, а так же exists/any/all/some и т.д. идут унутре через разновидности джоинов). Добавил сравнение в другим элементом, встречающимся в другом поддомене — вот тебе дополнительный джоин на другую таблицу.

И что? Для SQL какая разница? Он по одному и тому же алгоритму план строит, независимо от количества джоинов.


G>>Чем такая штука принципиально отличается от грида с настраиваемыми сортировками? (опять же с точки зрения SQL).

V>Тем, что для грида можно написать низлежащую вьюшку лишь однажды.
И как это отразится на работе SQL Server? Он планы по-другому делать будет?
Re[22]: Entity Framework за! и против!
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 30.06.14 22:13
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, gandjustas, Вы писали:


V>>>Т.е. ты сам себе ПМ, аналитик, архитектор и тот самый Вася, если тебе через 10 мин (!!!) потребовалось третье поле. ))

G>>Как это связано? Чтобы добавить поле нужен аналитик, архитектор и еще кто-то?

V>Ха-ха три раза.

V>Сначала код, так?
V>Например, для среднеразмерной складской программы?

Почему бы и нет? У тебя с этим проблемы?



G>>Вот и получается, что DBA с посредственными навыками получают слишком большой кредит доверия.

G>>Кстати по этой же причине последние годы появилось повальное увлечение NoSQL базами. Они банально проще и работа с ними более прямолинейна.

V>И кто тут косностью страдает?

V>NoSql хорош не по этому, а потому что аппаратура позволяет отказаться от реляционки. В т.ч. быстродействующие внешние носители, а не только размер ОП. Сдаётся мне, ты фундаментально не понимаешь происходящих процессов.

При чем тут внешние ностели? В большинстве NoSQL баз производительность падает до неприличных значений когда БД перестает влезать в ОП.
А SQL умудряется вполне достойно работать на базах объемами в сотни раз превышающими ОП на том же железе.

Но для большинства программистов разница SQL или NoSQL как раз в том как с базой работать. NoSQL предлагает самый прямолинейный подход — сериализация объектов целиком, даже думать не надо, просто вызвал Save и все. Проблемы начинаются когда данные оказываются сильно связанными, присутствуют циклы в связях. Тогда NoSQL предлагает такой же прямолинейный подход — тяни все объекты и делай join руками или делай индекс и напрямую к индексу обращайся. SQL все это делает внутри, далеко не очевидным образом, что и вызывает проблемы у программистов.
Re[24]: Entity Framework за! и против!
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 30.06.14 22:38
Оценка:
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, gandjustas, Вы писали:


IT>За громоздкую и никому не нужную архитектуру. За тормоза. За кривой SQL.

Какой-то неуловимый джо...

IT>За кривой маппинг, вспомним маппинг энумераторов.

А конкретнее?

IT>За бестолковые решения, делающие некоторые фичи совершенно бесполезными.

Кому-то это мешает, кроме самих разработчиков ef?

IT>Возьмём тут же студию или WPF, которым стало можно пользоваться лишь после того, как его стали применять в студии.

Логично, пользователей новых технологий MS как-то совсем мало. Хайп гораздо больше, чем реальных применений.
EF только последний год начал применяться и то не за счет мапинга или Linq, а за счет задрачивания под конкретные сценарии.

IT>
IT>public DataConnection(IDataProvider dataProvider, string connectionString)
IT>

IT>либо, для конкретно SqlServer

IT>
IT>LinqToDB.DataProvider.SqlServer.SqlServerTools.CreateDataConnection(...)
IT>

IT>Ещё можно сначала зарегистрировать конфигурацию, а потом её использовать.

IT>
IT>DataConnection.AddConfiguration(...);
IT>


А пример кода можно, ибо непонятно куда писать.

Кстати, как я понял, identity map нету. Загрузить объект и связанные как-то можно?

Вообще примеров не хватает.
Re[23]: Entity Framework за! и против!
От: IT Россия linq2db.com
Дата: 30.06.14 23:47
Оценка: +2
Здравствуйте, gandjustas, Вы писали:

G>А если мы используем Linq, то получается так:


G>
G>private static IQueryable<Transaction> GetValues(IQueryable<Transaction> query, DateTime? dateTime)
G>{
G>    if (dateTime.HasValue)
G>    {
G>        return query.Where(t => t.ShippedDate == dateTime.Value);
G>    }
G>    else
G>    {
G>        return query.Where(t => t.ShippedDate == null);
G>    }
G>}
G>

G>В этом случае Linq генерит разные запросы когда параметр null и когда не-null. Это значит что PSP больше не наступает.

Должно быть достаточно так

private static IQueryable<Transaction> GetValues(IQueryable<Transaction> query, DateTime? dateTime)
{
    return query.Where(t => t.ShippedDate == dateTime);
}

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