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

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

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

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

 

Нравится

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

Функцию с одним возвращаемым значением можно запустить так:

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 (например, как тут в комментарии) выбрать нужную проще, чем вносить изменения в классы ядра.

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

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

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