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)
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
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
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, ',')
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.
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'
*/
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.
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
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
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
*/