it-swarm.com.ru

Передать таблицу в качестве параметра в UDF SQL Server

Я хотел бы передать таблицу в качестве параметра в UDF масштабатора. 

Я также предпочел бы ограничить параметр таблицами с одним столбцом. (необязательный)

Это возможно?

ПРАВКА

Я не хочу передавать имя таблицы, я хотел бы передать таблицу данных (в качестве ссылки я полагаю)

ПРАВКА

Я хотел бы, чтобы мой Scaler UDF в основном брал таблицу значений и возвращал список строк CSV.

IE

col1  
"My First Value"  
"My Second Value"
...
"My nth Value"

вернется

"My First Value, My Second Value,... My nth Value"

Я хотел бы выполнить некоторую фильтрацию таблицы, IE, чтобы убедиться, что нет нулей, и чтобы не было дубликатов. Я ожидал что-то вроде:

SELECT dbo.MyFunction(SELECT DISTINCT myDate FROM myTable WHERE myDate IS NOT NULL)
37
Nathan Koop

К сожалению, в SQL Server 2005 нет простого способа. Ответ Лукаша верен для SQL Server 2008, хотя эта функция длинная просрочена

Любое решение будет включать временные таблицы или передачу xml/CSV и анализ в UDF. Пример: изменить на xml, разобрать в udf

DECLARE @psuedotable xml

SELECT
    @psuedotable = ...
FROM
    ...
FOR XML ...

SELECT ... dbo.MyUDF (@psuedotable)

Что вы хотите сделать в целом? Может быть другой способ сделать это ...

Правка: Почему бы не передать запрос в виде строки и использовать сохраненный процесс с выходным параметром

Примечание: это непроверенный фрагмент кода, и вам нужно подумать о внедрении SQL и т.д. Однако он также удовлетворяет вашему требованию «одного столбца» и должен помочь вам в этом.

CREATE PROC dbo.ToCSV (
    @MyQuery varchar(2000),
    @CSVOut varchar(max)
)
AS
SET NOCOUNT ON

CREATE TABLE #foo (bar varchar(max))

INSERT #foo
EXEC (@MyQuery)

SELECT
    @CSVOut = SUBSTRING(buzz, 2, 2000000000)
FROM
    (
    SELECT 
        bar -- maybe CAST(bar AS varchar(max))??
    FROM 
        #foo
    FOR XML PATH (',')
    ) fizz(buzz)
GO
15
gbn

Вы можете, однако, не любой стол. Из документации:

Для функций Transact-SQL все данные типы, включая определяемые пользователем CLR типы и определяемые пользователем типы таблиц, разрешены, за исключением данных временной метки тип.

Вы можете использовать определяемые пользователем типы таблиц .

Пример пользовательского типа таблицы:

CREATE TYPE TableType 
AS TABLE (LocationName VARCHAR(50))
GO 

DECLARE @myTable TableType
INSERT INTO @myTable(LocationName) VALUES('aaa')
SELECT * FROM @myTable

Итак, вы можете определить тип таблицы, например, TableType и определить функцию, которая принимает параметр этого типа. Пример функции:

CREATE FUNCTION Example( @TableName TableType READONLY)
RETURNS VARCHAR(50)
AS
BEGIN
    DECLARE @name VARCHAR(50)

    SELECT TOP 1 @name = LocationName FROM @TableName
    RETURN @name
END

Параметр должен быть ЧТЕН. И пример использования:

DECLARE @myTable TableType
INSERT INTO @myTable(LocationName) VALUES('aaa')
SELECT * FROM @myTable

SELECT dbo.Example(@myTable)

В зависимости от того, чего вы хотите достичь, вы можете изменить этот код.

EDIT: Если у вас есть данные в таблице, вы можете создать переменную:

DECLARE @myTable TableType

И взять данные из вашей таблицы в переменную

INSERT INTO @myTable(field_name)
SELECT field_name_2 FROm my_other_table
65
Lukasz Lysik

Шаг 1 : Создать Type as Table с именем TableType, который будет принимать таблицу с одним столбцом varchar

create type TableType
as table ([value] varchar(100) null)

Шаг 2 : Создать функцию, которая будет принимать выше объявленный TableType в качестве параметра с табличным значением и строковое значение в качестве разделителя

create function dbo.fn_get_string_with_delimeter (@table TableType readonly,@Separator varchar(5))
returns varchar(500)
As
begin

    declare @return varchar(500)

    set @return = stuff((select @Separator + value from @table for xml path('')),1,1,'')

    return @return

end

Шаг 3 : передать таблицу с одним столбцом varchar пользовательскому типу TableType и ',' в качестве разделителя в функции

select dbo.fn_get_string_with_delimeter(@tab, ',')
4
Rahul Srivastava

Переходя к нижней строке, вы хотите, чтобы запрос, подобный SELECT x FROM y, был передан в функцию, которая возвращает значения в виде строки, разделенной запятой.

Как уже было объяснено, вы можете сделать это путем создания типа таблицы и передачи UDT в функцию, но для этого нужен многострочный оператор. 

Вы можете передавать XML, не объявляя типизированную таблицу, но для этого требуется переменная xml, которая по-прежнему является многострочным оператором, т.е. 

DECLARE @MyXML XML = (SELECT x FROM y FOR XML RAW);
SELECT Dbo.CreateCSV(@MyXml);

«FOR XML RAW» заставляет SQL выдавать результат в виде некоторого xml.

Но вы можете обойти переменную используя Cast (... AS XML). Тогда это всего лишь вопрос некоторого XQuery и небольшого трюка с конкатенацией:

CREATE FUNCTION CreateCSV (@MyXML XML) 
RETURNS VARCHAR(MAX)
BEGIN
    DECLARE @listStr VARCHAR(MAX);
    SELECT 
            @listStr = 
                COALESCE(@listStr+',' ,'') + 
                c.value('@Value[1]','nvarchar(max)') 
        FROM @myxml.nodes('/row') as T(c)
    RETURN @listStr
END
GO

-- And you call it like this:
SELECT Dbo.CreateCSV(CAST((    SELECT x FROM y    FOR XML RAW) AS XML));

-- Or a working example
SELECT Dbo.CreateCSV(CAST((
        SELECT DISTINCT number AS Value 
        FROM master..spt_values 
        WHERE type = 'P' 
            AND number <= 20
    FOR XML RAW) AS XML));

Пока вы используете FOR XML RAW, все, что вам нужно, это создать псевдоним нужного столбца в качестве значения, поскольку это жестко задано в функции.

2
Stephen Turner

Я имел дело с очень похожей проблемой и смог достичь того, что искал, хотя я использую SQL Server 2000. Я знаю, что это старый вопрос, но думаю, что правильно размещать здесь решение, так как должны быть такие, как я, которые используют старые версии и все еще нуждаются в помощи.

Вот хитрость: SQL Server не будет принимать передачу таблицы в UDF, а также вы не можете передать запрос T-SQL, поэтому функция создает временную таблицу или даже вызывает хранимую процедуру для этого. Вместо этого я создал зарезервированную таблицу, которую я назвал xtList. Это будет содержать список значений (1 столбец, если необходимо) для работы.

CREATE TABLE [dbo].[xtList](
    [List] [varchar](1000) NULL
) ON [PRIMARY]

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

-- =============================================
-- Author:      Zark Khullah
-- Create date: 20/06/2014
-- =============================================
CREATE PROCEDURE [dbo].[xpCreateList]
    @ListQuery varchar(2000)
AS
BEGIN
    SET NOCOUNT ON;

  DELETE FROM xtList

  INSERT INTO xtList
    EXEC(@ListQuery)
END

Теперь просто разберитесь со списком так, как хотите, используя xtList. Вы можете использовать в процедуре (для выполнения нескольких команд T-SQL) скалярные функции (для извлечения нескольких строк) или табличные функции с несколькими операторами (извлекает строки, но, как это было в таблице, 1 строка на строку). Для этого вам понадобятся курсоры:

DECLARE @Item varchar(100)
DECLARE cList CURSOR DYNAMIC
  FOR (SELECT * FROM xtList WHERE List is not NULL)
  OPEN cList

FETCH FIRST FROM cList INTO @Item
WHILE @@FETCH_STATUS = 0 BEGIN

  << desired action with values >>

FETCH NEXT FROM cList INTO @Item
END
CLOSE cList
DEALLOCATE cList

Желаемое действие будет следующим, в зависимости от того, какой тип объекта создан:

Хранимые процедуры

-- =============================================
-- Author:      Zark Khullah
-- Create date: 20/06/2014
-- =============================================
CREATE PROCEDURE [dbo].[xpProcreateExec]
(
    @Cmd varchar(8000),
    @ReplaceWith varchar(1000)
)
AS
BEGIN
  DECLARE @Query varchar(8000)

  << cursor start >>
    SET @Query = REPLACE(@Cmd,@ReplaceWith,@Item)
    EXEC(@Query)
  << cursor end >>
END

/* EXAMPLES

  (List A,B,C)

  Query = 'SELECT x FROM table'
    with EXEC xpProcreateExec(Query,'x') turns into
  SELECT A FROM table
  SELECT B FROM table
  SELECT C FROM table

  Cmd = 'EXEC procedure ''arg''' --whatchout for wrong quotes, since it executes as dynamic SQL
    with EXEC xpProcreateExec(Cmd,'arg') turns into
  EXEC procedure 'A'
  EXEC procedure 'B'
  EXEC procedure 'C'

*/

Скалярные функции

-- =============================================
-- Author:      Zark Khullah
-- Create date: 20/06/2014
-- =============================================
CREATE FUNCTION [dbo].[xfProcreateStr]
(
    @OriginalText varchar(8000),
    @ReplaceWith varchar(1000)
)
RETURNS varchar(8000)
AS
BEGIN
    DECLARE @Result varchar(8000)

  SET @Result = ''
  << cursor start >>
    SET @Result = @Result + REPLACE(@OriginalText,@ReplaceWith,@Item) + char(13) + char(10)
  << cursor end >>

    RETURN @Result
END

/* EXAMPLE

  (List A,B,C)

  Text = 'Access provided for user x'
    with "SELECT dbo.xfProcreateStr(Text,'x')" turns into
  'Access provided for user A
  Access provided for user B
  Access provided for user C'

*/

Многозначные табличные функции

-- =============================================
-- Author:      Zark Khullah
-- Create date: 20/06/2014
-- =============================================
CREATE FUNCTION [dbo].[xfProcreateInRows]
(
    @OriginalText varchar(8000),
    @ReplaceWith varchar(1000)
)
RETURNS 
@Texts TABLE 
(
    Text varchar(2000)
)
AS
BEGIN
  << cursor start >>
      INSERT INTO @Texts VALUES(REPLACE(@OriginalText,@ReplaceWith,@Item))
  << cursor end >>
END

/* EXAMPLE

  (List A,B,C)

  Text = 'Access provided for user x'
    with "SELECT * FROM dbo.xfProcreateInRow(Text,'x')" returns rows
  'Access provided for user A'
  'Access provided for user B'
  'Access provided for user C'

*/
1
Z. Khullah

Следующее позволит вам быстро удалить дубликаты, нулевые значения и вернуть только действительный список.

CREATE TABLE DuplicateTable (Col1 INT)
INSERT INTO DuplicateTable
SELECT 8
UNION ALL
SELECT 1--duplicate
UNION ALL
SELECT 2 --duplicate
UNION ALL
SELECT 1
UNION ALL
SELECT 3
UNION ALL
SELECT 4
UNION ALL
SELECT 5
UNION 
SELECT NULL
GO

WITH CTE (COl1,DuplicateCount)
AS
(
SELECT COl1,
ROW_NUMBER() OVER(PARTITION BY COl1 ORDER BY Col1) AS DuplicateCount
FROM DuplicateTable
WHERE (col1 IS NOT NULL) 
)
SELECT COl1
FROM CTE
WHERE DuplicateCount =1
GO

CTE действительны в SQL 2005, затем вы можете сохранить значения во временной таблице и использовать их со своей функцией.

0
Raymond A

ТАБЛИЦА ПЕРЕДАЧИ КАК ПАРАМЕТР ХРАНИЛИЩЕ

Шаг 1:

CREATE TABLE [DBO] .T_EMPLOYEES_DETAILS ( Id int, Имя nvarchar (50), Пол nvarchar (10), Зарплата int )

Шаг 2:

СОЗДАТЬ ТИП EmpInsertType AS TABLE (... Id int, Имя nvarchar (50), Пол nvarchar (10), Зарплата int )

Шаг 3:

/ * Необходимо добавить ключевое слово READONLY в конец переменной * /

CREATE PROC PRC_EmpInsertType @ EmployeeInsertType EmpInsertType READONLY AS BEGIN INSERT INTO [DBO] .T_EMPLOYEES_DETAILS SELECT * FROM @EmployeeInsertType END

Шаг 4:

ОБЪЯВИТЬ @EmployeeInsertType EmpInsertType

INSERT INTO @EmployeeInsertType VALUES (1, 'John', 'Male', 50000) INSERT INTO @EmployeeInsertType VALUES (2, 'Praveen', 'Male', 60000) INSERT INTO @EmployeeInsertType VALUES (3, 'Chitra', 'Female', 45000) INSERT INTO @EmployeeInsertType VALUES (4, 'Mathy', 'Female', 6600) INSERT INTO @EmployeeInsertType VALUES (5, 'Sam', 'Male', 50000)

EXEC PRC_EmpInsertType @EmployeeInsertType

=======================================

SELECT * FROM T_EMPLOYEES_DETAILS

РЕЗУЛЬТАТ

1 Джон Мале 50000

2 Правин Мужской 60000

3 Читра Женский 45000

4 Mathy Женский 6600

5 Sam Male 50000

0
Jamal
    create table Project (ProjectId int, Description varchar(50));
    insert into Project values (1, 'Chase tail, change directions');
    insert into Project values (2, 'ping-pong ball in clothes dryer');

    create table ProjectResource (ProjectId int, ResourceId int, Name varchar(15));
    insert into ProjectResource values (1, 1, 'Adam');
    insert into ProjectResource values (1, 2, 'Kerry');
    insert into ProjectResource values (1, 3, 'Tom');
    insert into ProjectResource values (2, 4, 'David');
    insert into ProjectResource values (2, 5, 'Jeff');


    SELECT *, 
      (SELECT Name + ' ' AS [text()] 
       FROM ProjectResource pr 
       WHERE pr.ProjectId = p.ProjectId 
       FOR XML PATH ('')) 
    AS ResourceList 
    FROM Project p

-- ProjectId    Description                        ResourceList
-- 1            Chase tail, change directions      Adam Kerry Tom 
-- 2            ping-pong ball in clothes dryer    David Jeff 
0
D. Kermott

Чтобы получить количество столбцов в таблице, используйте это:

select count(id) from syscolumns where id = object_id('tablename')

и чтобы передать таблицу функции, попробуйте XML как show здесь :

create function dbo.ReadXml (@xmlMatrix xml)
returns table
as
return
( select
t.value('./@Salary', 'integer') as Salary,
t.value('./@Age', 'integer') as Age
from @xmlMatrix.nodes('//row') x(t)
)
go

declare @source table
( Salary integer,
age tinyint
)
insert into @source
select 10000, 25 union all
select 15000, 27 union all
select 12000, 18 union all
select 15000, 36 union all
select 16000, 57 union all
select 17000, 44 union all
select 18000, 32 union all
select 19000, 56 union all
select 25000, 34 union all
select 7500, 29
--select * from @source

declare @functionArgument xml

select @functionArgument =
( select
Salary as [row/@Salary],
Age as [row/@Age]
from @source
for xml path('')
)
--select @functionArgument as [@functionArgument]

select * from readXml(@functionArgument)

/* -------- Sample Output: --------
Salary Age
----------- -----------
10000 25
15000 27
12000 18
15000 36
16000 57
17000 44
18000 32
19000 56
25000 34
7500 29
*/
0
D3vtr0n