it-swarm.dev

Übergeben Sie die Tabelle als Parameter an die SQL Server-UDF

Ich möchte eine Tabelle als Parameter in eine UDF des Scalers übergeben. 

Ich würde es auch vorziehen, den Parameter auf Tabellen mit nur einer Spalte zu beschränken. (wahlweise)

Ist das möglich?

EDIT

Ich möchte keinen Tabellennamen übergeben, ich möchte die Datentabelle weitergeben (als eine Referenz, die ich vermute)

EDIT

Ich möchte, dass meine Scaler-UDF grundsätzlich eine Wertetabelle verwendet und eine CSV-Liste der Zeilen zurückgibt.

IE

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

würden zurückkehren

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

Ich möchte jedoch einige Filter für die Tabelle durchführen, IE um sicherzustellen, dass keine Nullen vorhanden sind, und um sicherzustellen, dass keine Duplikate vorhanden sind. Ich erwartete etwas in der Art von:

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

Leider gibt es keine einfache Möglichkeit in SQL Server 2005. Die Antwort von Lukasz ist jedoch für SQL Server 2008 korrekt und die Funktion ist long overdue

Jede Lösung würde temporäre Tabellen oder das Übergeben von XML/CSV und das Analysieren in der UDF umfassen. Beispiel: zu xml wechseln, in udf analysieren

DECLARE @psuedotable xml

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

SELECT ... dbo.MyUDF (@psuedotable)

Was möchten Sie jedoch im Großen und Ganzen tun? Es kann einen anderen Weg geben, dies zu tun ...

Bearbeiten: Warum übergeben Sie die Abfrage nicht als String und verwenden Sie eine gespeicherte Prozedur mit Ausgabeparameter

Hinweis: Dies ist ein ungeprüfter Code, und Sie müssen über SQL-Injection usw. nachdenken. Sie erfüllt jedoch auch Ihre "einspaltige" Anforderung und sollte Ihnen helfen

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

Sie können jedoch keine Tabelle. Aus der Dokumentation:

Für Transact-SQL-Funktionen alle Daten Typen, einschließlich CLR benutzerdefiniert Typen und benutzerdefinierte Tabellentypen sind zulässig, mit Ausnahme der Zeitstempeldaten Art.

Sie können benutzerdefinierte Tabellentypen verwenden.

Beispiel für einen benutzerdefinierten Tabellentyp:

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

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

Sie können also Ihren Tabellentyp definieren, beispielsweise TableType, und eine Funktion definieren, die den Parameter dieses Typs verwendet. Eine Beispielfunktion:

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

Der Parameter muss READONLY sein. Und beispiel verwendung:

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

SELECT dbo.Example(@myTable)

Je nachdem, was Sie erreichen möchten, können Sie diesen Code ändern.

EDIT: Wenn Sie Daten in einer Tabelle haben, können Sie eine Variable erstellen:

DECLARE @myTable TableType

Und bringen Sie Daten aus Ihrer Tabelle in die Variable

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

Schritt 1 : Erstellen Sie einen Typ als Tabelle mit dem Namen TableType, der eine Tabelle mit einer Varchar-Spalte akzeptiert

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

Schritt 2 : Erstellen Sie eine Funktion, die den oben angegebenen TableType als Tabellenwertparameter und den Zeichenfolgenwert als Trennzeichen akzeptiert

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

Schritt 3 : Übergibt eine Tabelle mit einer varchar-Spalte an den benutzerdefinierten Typ TableType und ',' als Trennzeichen in der Funktion

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

Bis auf die unterste Zeile möchten Sie, dass eine Abfrage wie SELECT x FROM y an eine Funktion übergeben wird, die die Werte als durch Kommas getrennte Zeichenfolge zurückgibt.

Wie bereits erläutert, können Sie dies tun, indem Sie einen Tabellentyp erstellen und eine UDT an die Funktion übergeben. Dies erfordert jedoch eine mehrzeilige Anweisung. 

Sie können XML weitergeben, ohne eine typisierte Tabelle zu deklarieren. Dies scheint jedoch eine XML-Variable zu erfordern, die immer noch eine mehrzeilige Anweisung ist, d. H. 

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

Mit "FOR XML RAW" erhalten Sie von SQL die Ergebnismenge als XML.

Sie können die Variable jedoch mit Cast (... AS XML) umgehen. Dann ist es nur eine Frage von XQuery und einem kleinen Verkettungstrick:

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

Solange Sie FOR XML RAW verwenden, brauchen Sie nur die Spalte als Wert zu aliasieren, da diese in der Funktion hart codiert ist.

2
Stephen Turner

Ich habe es mit einem sehr ähnlichen Problem zu tun und konnte erreichen, wonach ich gesucht habe, obwohl ich SQL Server 2000 verwende. Ich weiß, dass es eine alte Frage ist, aber ich denke, es ist gültig, die Lösung hier zu veröffentlichen Es sollte andere wie mich geben, die alte Versionen verwenden und immer noch Hilfe benötigen.

Hier ist der Trick: SQL Server akzeptiert weder die Übergabe einer Tabelle an eine UDF, noch können Sie eine T-SQL-Abfrage übergeben, sodass die Funktion eine temporäre Tabelle erstellt oder sogar eine gespeicherte Prozedur aufruft. Stattdessen habe ich eine reservierte Tabelle erstellt, die ich xtList nannte. Dies enthält die Liste der Werte (je nach Bedarf 1 Spalte), mit der gearbeitet werden kann.

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

Dann eine gespeicherte Prozedur zum Auffüllen der Liste. Dies ist nicht unbedingt notwendig, aber ich halte es für sehr nützlich und empfehlenswert.

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

Behandeln Sie die Liste nun auf beliebige Weise mit der xtList. Sie können in einer Prozedur (zum Ausführen mehrerer T-SQL-Befehle) Skalarfunktionen (zum Abrufen mehrerer Zeichenfolgen) oder Tabellenwertfunktionen mit mehreren Anweisungen verwenden (ruft die Zeichenfolgen ab, befindet sich jedoch wie in einer Tabelle (1 Zeile pro Zeile). Für all das benötigen Sie Cursor:

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

Die gewünschte Aktion würde je nach dem erstellten Objekttyp wie folgt aussehen:

Gespeicherte Prozeduren

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

*/

Skalarfunktionen

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

*/

Tabellenwertfunktionen mit mehreren Anweisungen

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

Im Folgenden können Sie die doppelten Nullwerte schnell entfernen und nur den gültigen Wert als Liste zurückgeben.

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 sind in SQL 2005 gültig. Sie können die Werte dann in einer temporären Tabelle speichern und mit Ihrer Funktion verwenden.

0
Raymond A

TABELLE ALS PARAMETER IM GESPEICHERTEN VERFAHREN

Schritt 1:

CREATE TABLE [DBO] .T_EMPLOYEES_DETAILS ( Id int, Name nvarchar (50), Geschlecht nvarchar (10), Gehalt int )

Schritt 2:

CREATE TYPE EmpInsertType AS TABLE ( Id int, Name nvarchar (50), Geschlecht nvarchar (10), Gehalt int )

Schritt 3:

/ * Muss das READONLY-Schlüsselwort am Ende der Variablen hinzufügen * /

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

Schritt 4:

DECLARE @EmployeeInsertType EmpInsertType

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

EXEC PRC_EmpInsertType @EmployeeInsertType

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

SELECT * FROM T_EMPLOYEES_DETAILS

AUSGABE

1 John Male 50000

2 Praveen Männlich 60000

3 Chitra Female 45000

4 Mathy Female 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

So ermitteln Sie die Spaltenanzahl für eine Tabelle:

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

um eine Tabelle an eine Funktion zu übergeben, versuchen Sie XML als show here :

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