web-dev-qa-db-ja.com

テーブルをパラメーターとしてSQLサーバーUDFに渡す

テーブルをパラメーターとしてスケーラーUDFに渡したいです。

また、パラメーターを1列のみのテーブルに制限することも好みます。 (オプション)

これは可能ですか?

[〜#〜] edit [〜#〜]

テーブル名を渡したくありません。データのテーブルを渡したいと思います(参照として推測します)

[〜#〜] edit [〜#〜]

Scaler UDFで基本的に値のテーブルを取得し、行のCSVリストを返すようにします。

IE

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

戻ります

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

ただし、テーブルでフィルタリングを行いたい場合は、IE nullがないことを確認し、重複がないことを確認します。

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

残念ながら、SQL Server 2005には簡単な方法はありません。Lukaszの答えはSQL Server 2008に対して正しいのですが、機能はlong期限切れです

すべてのソリューションには、一時テーブルが含まれるか、xml/CSVを渡してUDFで解析されます。例:XMLへの変更、UDFでの解析

DECLARE @psuedotable xml

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

SELECT ... dbo.MyUDF (@psuedotable)

しかし、全体像で何をしたいですか?これを行う別の方法があるかもしれません...

編集:クエリを文字列として渡し、出力パラメータでストアドプロシージャを使用しない理由

注:これはテストされていないコードであり、SQLインジェクションなどについて考える必要があります。ただし、「1列」の要件も満たしているため、役立つはずです。

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
16
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

パラメーターはREADONLYでなければなりません。そして使用例:

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
69
Lukasz Lysik

手順1:1つのvarchar列を持つテーブルを受け入れるTableTypeという名前のテーブルとしてタイプを作成します

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

ステップ:1つのvarchar列を持つテーブルをユーザー定義型TableTypeに渡し、関数の区切り文字として「、」を渡す

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

要するに、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を使用する限り、必要なのは、関数にハードコードされているため、Valueとして必要な列のエイリアスのみです。

2
Stephen Turner

SQL Server 2000を使用している場合でも、非常に似た問題に対処しており、探していたものを達成できました。これは古い質問ですが、ここに解決策を投稿するのは有効だと思います私のような古いバージョンを使用していて、まだ助けが必要な人がいるはずです。

ここに秘trickがあります。SQLServerは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行につき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

ストアドプロシージャのパラメーターとしての受け渡しテーブル

ステップ1:

CREATE TABLE [DBO] .T_EMPLOYEES_DETAILS(Id int、Name nvarchar(50)、Gender nvarchar(10)、Salary int)

ステップ2:

CREATE TYPE EmpInsertType AS TABLE(Id int、Name nvarchar(50)、Gender nvarchar(10)、Salary int)

ステップ3:

/ *変数の最後にREADONLYキーワードを追加する必要があります* /

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

ステップ4:

DECLARE @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

[〜#〜] output [〜#〜]

1ジョン・マレ50000

2 Praveen Male 60000

3チトラ女性45000

4 Mathy Female 6600

5サムマレ50000

1
Jamal

以下を使用すると、重複したnull値をすばやく削除し、有効な値のみをリストとして返すことができます。

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
    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')

テーブルを関数に渡すには、show here のようにXMLを試してください。

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