функции
Технические вопросы
5.x

Вызов функции SQL

Помогите сделать запрос функции SQL из Исходного кода c возможностью записи результата и дальнейшим его использованием.

В sql запрос выглядит так :

SELECT  dbo.fn_dateadd_work ('minute', 20, defValues)

 

Нравится

16 комментариев

Функцию с одним возвращаемым значением можно запустить так:
[csharp]
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(dbExecutor);
}
[/csharp]
Если же функция возвращает много значений:
[csharp]
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("StartDate");
DateTime endDate = dataReader.GetColumnValue("EndDate");
//...
}
}
}
[/csharp]

[csharp]
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(dbExecutor);
}

[/csharp]

Выдает ошибку:
Exception Message: Не удалось найти столбец "dbo", определяемую пользователем функцию или агрегатную функцию "dbo.fn_dateadd_work ". Также возможно, имя является неоднозначным.
Exception Type: System.Data.SqlClient.SqlException
Exception Source: .Net SqlClient Data Provider

Функция Скалярная

Это значит, что такой функции у Вас в базе нет. Возможно, дело в пробеле в конце.

"Зверев Александр" написал:

Это значит, что такой функции у Вас в базе нет. Возможно, дело в пробеле в конце.


[csharp]
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(dbExecutor);
}
[/csharp]
Убарала пробел, ошибка та же. Функция в Бд есть.
В инете пишут, что возможно фн табличная и к ней так и нужно обращаться...
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 (например, как тут в комментарии) выбрать нужную проще, чем вносить изменения в классы ядра.

Завёл идею, но не уверен, что до неё скоро доберутся.

Спасибо, Александр, за идею. Согласен что доберутся не скоро, но пусть будет, надеюсь это будет полезно еще кому-нибудь. По поводу получения строки соединения тоже спасибо, интересная возможность. 

Показать все комментарии