Помогите сделать запрос функции SQL из Исходного кода c возможностью записи результата и дальнейшим его использованием.
В sql запрос выглядит так :
Нравится
Функцию с одним возвращаемым значением можно запустить так:
string number = "12345"; using (DBExecutor dbExecutor = userConnection.EnsureDBConnection()) { UserDefinedFunction userDefinedFunction = new UserDefinedFunction(userConnection, "fn_GetPhoneNumberSearchForm") .WithParameter("SourcePhoneNumber", number) as UserDefinedFunction; userDefinedFunction.PackageName = userConnection.DBEngine.SystemPackageName; userDefinedFunction.ReturnType = UserDefinedFunctionReturnType.Scalar; searchNumber = userDefinedFunction.ExecuteScalar<string>(dbExecutor); }
Если же функция возвращает много значений:
using (DBExecutor dbExecutor = userConnection.EnsureDBConnection()) { UserDefinedFunction userDefinedFunction = new UserDefinedFunction(userConnection, "fn_GetFreeIntervalsForContactToDeadline") .WithParameter("ContactId", ContactId) .WithParameter("CalendarId", calendarId) .WithParameter("FromDate", userConnection.CurrentUser.GetCurrentDateTime()) .WithParameter("Deadline", deadLineDate.ToString("yyyy-MM-dd HH:mm:ss")) .WithParameter("Offset", userConnection.CurrentUser.TimeZone.BaseUtcOffset.TotalMinutes) as UserDefinedFunction; userDefinedFunction.PackageName = userConnection.DBEngine.SystemPackageName; userDefinedFunction.ReturnType = UserDefinedFunctionReturnType.Table; using(IDataReader dataReader = userDefinedFunction.ExecuteReader(dbExecutor)) { while (dataReader.Read()) { DateTime startDate = dataReader.GetColumnValue<DateTime>("StartDate"); DateTime endDate = dataReader.GetColumnValue<DateTime>("EndDate"); //... } } }
string datepart = "day"; var defValues3 = ""; var defValues5 = ""; using (DBExecutor dbExecutor = userConnection.EnsureDBConnection()) { UserDefinedFunction userDefinedFunction = new UserDefinedFunction(userConnection, "fn_dateadd_work ") .WithParameter("datepart", datepart) .WithParameter("number", 3) .WithParameter("date", defValues.StartDate)as UserDefinedFunction; userDefinedFunction.PackageName = userConnection.DBEngine.SystemPackageName; userDefinedFunction.ReturnType = UserDefinedFunctionReturnType.Scalar; defValues3 = userDefinedFunction.ExecuteScalar<string>(dbExecutor); }
Выдает ошибку:
Exception Message: Не удалось найти столбец "dbo", определяемую пользователем функцию или агрегатную функцию "dbo.fn_dateadd_work ". Также возможно, имя является неоднозначным.
Exception Type: System.Data.SqlClient.SqlException
Exception Source: .Net SqlClient Data Provider
Это значит, что такой функции у Вас в базе нет. Возможно, дело в пробеле в конце.
"Зверев Александр" написал:Это значит, что такой функции у Вас в базе нет. Возможно, дело в пробеле в конце.
string datepart = "day"; var defValues3 = ""; var defValues5 = ""; using (DBExecutor dbExecutor = userConnection.EnsureDBConnection()) { UserDefinedFunction userDefinedFunction = new UserDefinedFunction(userConnection, "fn_dateadd_work") .WithParameter("datepart", datepart) .WithParameter("number", 3) .WithParameter("date", defValues.StartDate)as UserDefinedFunction; userDefinedFunction.PackageName = userConnection.DBEngine.SystemPackageName; userDefinedFunction.ReturnType = UserDefinedFunctionReturnType.Scalar; defValues3 = userDefinedFunction.ExecuteScalar<string>(dbExecutor); }
Убарала пробел, ошибка та же. Функция в Бд есть.
В инете пишут, что возможно фн табличная и к ней так и нужно обращаться...
http://www.cyberforum.ru/sql-server/thread735489.html
Может, на функцию нет прав на запуск у пользователя, который прописан в конфиге ConnectionStrings.
Посмотрите в SQL-профайлере, какие запросы уходят и попробуйте запустить такие же вручную.
"Зверев Александр" написал:Может, на функцию нет прав на запуск у пользователя, который прописан в конфиге ConnectionStrings.
Посмотрите в SQL-профайлере, какие запросы уходят и попробуйте запустить такие же вручную.
Спасибо, проблема в правах.
А можно как то в исходном коде реализовать такой вызов функции:
select * from dbo.fn_SomeFunctionName() where someColumnName>2
Руслан, если через UserDefinedFunction такое нельзя, можно воспользоваться CustomQuery.
Хорошо, а через Select можно? Если да то как?
Пробовал так, не работает.
var select = (Select)new Select(_userConnection) .Column("Id") .From("fn_GetContactDescendantRoles") .Where("level") .IsEqual(Column.Parameter(1)); select.Parameters.Add("contact", new Guid("40445C52-FFD1-4DBA-94D8-127A7B29806A")); select.Parameters.Add("parentRole", new Guid("CFB6F756-B740-44AD-A221-80C5E441EBF4"));
Выходит ошибка:
System.Data.SqlClient.SqlException: "Параметры функции "dbo.fn_GetContactDescendantRoles" не были предоставлены."
Руслан, я имел в виду не Select, а CustomQuery.
В Вашей попытке через Select, вероятно, это получаются параметры не функции, а всего запроса (как при запросе на выборку из обычной таблицы). Проверить можно, сгенерировав перед выполнением текст запроса при помощи GetSqlText().
Зверев Александр,
Я понял что вы имели ввиду, но мне не хочется делать приложение жестко привязанным к базе данных MSSQL. Ожидаю что использование класса Select оставит приложение "кроссплатформенным". Поэтому предположил что можно использовать Select как при обычном обращении к таблице, т.к. функция возвращает таблицу.
Руслан, в справке написано:
Класс Terrasoft.Core.DB.Select предназначен для построения запросов выборки записей из таблиц базы данных.
Для работы с функциями используют другой класс, UserDefinedFunction . Для произвольных запросов — CustomQuery.
Зверев Александр,
Это все замечательно. Но в sql существует возможность выполнения такой конструкции:
select * from dbo.fn_SomeFunctionName() where someColumnName>2
Вы сказали что это не предусмотрено в классе UserDefinedFunction. Конечно моя функция не возвращает +100500 записей и можно было просто пробежаться по нужным в цикле, но я хочу задать условие на стороне сервера, чтобы он вернул только нужные по условию where. Вроде мелочь, но если количество запросов будет большое, то и сетевой трафик вырастет. В общем я за оптимизацию. Есть ли возможность в будущем расширить функционал данного класса?
В SQL можно и drop database выполнить. Возможность есть (но для этого нужно иметь доступ к исходникам ядра системы), больше вопрос, есть ли востребованность. Обходной вариант я предложил. Думаю, написать три строчки вызова для разных БД и получив ConnectionString (например, как тут в комментарии) выбрать нужную проще, чем вносить изменения в классы ядра.
Завёл идею, но не уверен, что до неё скоро доберутся.
Спасибо, Александр, за идею. Согласен что доберутся не скоро, но пусть будет, надеюсь это будет полезно еще кому-нибудь. По поводу получения строки соединения тоже спасибо, интересная возможность.