web-dev-qa-db-fra.com

Passer la table en tant que paramètre dans l'UDF du serveur SQL

Je voudrais passer une table en tant que paramètre dans un fichier UDF scaler. 

Je préférerais également limiter le paramètre aux tables avec une seule colonne. (optionnel)

Est-ce possible?

MODIFIER

Je ne veux pas passer un nom de table, j'aimerais passer à la table de données (en tant que référence je suppose)

MODIFIER

Je voudrais que mon fichier UDF Scaler utilise essentiellement un tableau de valeurs et renvoie une liste CSV des lignes.

IE

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

retournerais

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

Je voudrais cependant faire un peu de filtrage sur la table, IE en veillant à ce qu'il n'y ait pas de valeur NULL et en évitant les doublons. Je m'attendais à quelque chose du genre:

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

Malheureusement, il n'y a pas de moyen simple dans SQL Server 2005. La réponse de Lukasz est correcte pour SQL Server 2008 et la fonctionnalité est long en retard

Toute solution impliquerait des tables temporaires, ou passer en XML/CSV et analyser dans le fichier UDF. Exemple: changement en xml, analyse en udf

DECLARE @psuedotable xml

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

SELECT ... dbo.MyUDF (@psuedotable)

Que voulez-vous faire dans la grande image cependant? Il y a peut-être une autre façon de faire ça ...

Edit: Pourquoi ne pas transmettre la requête sous forme de chaîne et utiliser un proc stocké avec un paramètre de sortie

Remarque: il s’agit d’un bit de code non testé et vous devez penser à l’injection de code SQL, etc. Cependant, il répond également à votre exigence "une colonne" et devrait vous aider.

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

Vous pouvez cependant pas n'importe quelle table. De la documentation:

Pour les fonctions Transact-SQL, toutes les données types, y compris CLR défini par l'utilisateur types et types de table définis par l'utilisateur, sont autorisés à l'exception des données d'horodatage type.

Vous pouvez utiliser les types de table définis par l'utilisateur .

Exemple de type de table défini par l'utilisateur:

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

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

Donc, ce que vous pouvez faire est de définir votre type de table, par exemple TableType et de définir funcion qui prend le paramètre de ce type.Un exemple de fonction:

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

Le paramètre doit être READONLY. Et exemple d'utilisation:

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

SELECT dbo.Example(@myTable)

En fonction de vos objectifs, vous pouvez modifier ce code.

EDIT: Si vous avez des données dans une table, vous pouvez créer une variable:

DECLARE @myTable TableType

Et prendre des données de votre table à la variable

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

Étape 1 : Créer un type en tant que table avec le nom TableType qui acceptera une table ayant une colonne varchar

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

Étape 2 : Créez une fonction qui acceptera les types de table déclarés ci-dessus en tant que paramètre à valeur de table et la valeur de chaîne en tant que séparateur

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

Étape 3 : Passer la table avec une colonne varchar au type défini par l'utilisateur TableType et ',' en tant que séparateur dans la fonction

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

En coupant la ligne du bas, vous souhaitez qu'une requête telle que SELECT x FROM y soit transmise à une fonction qui renvoie les valeurs sous forme de chaîne séparée par des virgules.

Comme cela a déjà été expliqué, vous pouvez le faire en créant un type de table et en transmettant un UDT à la fonction, mais cela nécessite une instruction multiligne. 

Vous pouvez transmettre du XML sans déclarer une table typée, mais cela semble nécessiter une variable xml qui est toujours une instruction multiligne, par exemple i.e. 

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

Le "FOR XML RAW" permet au code SQL de vous donner son résultat défini sous forme de code xml.

Mais vous pouvez contourner la variable en utilisant Cast (... AS XML). Ensuite, il s’agit juste d’une question de XQuery et d’un petit truc de concaténation:

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

Tant que vous utilisez FOR XML RAW, vous devez simplement aliaser la colonne de votre choix en tant que valeur, car celle-ci est codée en dur dans la fonction.

2
Stephen Turner

J'ai eu à faire face à un problème très similaire et j'ai pu réaliser ce que je cherchais, même si j'utilise SQL Server 2000. Je sais que c'est une vieille question, mais je pense qu'il est valide d'afficher ici la solution. il devrait y avoir d'autres personnes comme moi qui utilisent les anciennes versions et ont encore besoin d'aide.

Voici l'astuce: SQL Server n'accepte pas de transmettre une table à un fichier UDF. Vous ne pouvez pas non plus transmettre une requête T-SQL afin que la fonction crée une table temporaire ou même appelle une procédure stockée pour le faire. Donc, à la place, j'ai créé une table réservée, que j'ai appelée xtList. Cela contiendra la liste des valeurs (1 colonne, si nécessaire) avec lesquelles travailler.

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

Ensuite, une procédure stockée pour remplir la liste. Ce n'est pas strictement nécessaire, mais je pense que c'est très utile et constitue la meilleure pratique.

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

Maintenant, traitez la liste comme vous le souhaitez, en utilisant la liste xtList. Vous pouvez utiliser dans une procédure (pour l'exécution de plusieurs commandes T-SQL) des fonctions scalaires (pour extraire plusieurs chaînes) ou des fonctions à plusieurs instructions de table (récupère les chaînes mais comme si elles se trouvaient dans une table, une chaîne par ligne). Pour cela, vous aurez besoin de curseurs:

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

L'action souhaitée serait la suivante, en fonction du type d'objet créé:

Procédures stockées

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

*/

Fonctions scalaires

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

*/

Fonctions de table multi-instructions

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

Ce qui suit vous permettra de supprimer rapidement les valeurs nulles en double et de ne renvoyer que les valeurs valides sous forme de liste.

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 sont valides dans SQL 2005, vous pouvez ensuite stocker les valeurs dans une table temporaire et l’utiliser avec votre fonction.

0
Raymond A

TABLEAU PASSANT COMME PARAMÈTRE DANS LA PROCÉDURE MÉMOIRE

Étape 1:

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

Étape 2:

CREATE TYPE EmpInsertType AS TABLE ( Id int, Nom nvarchar (50), Sexe nvarchar (10), Salary int )

Étape 3:

/ * Doit ajouter le mot clé READONLY à la fin de la variable * /

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

Étape 4:

DECLARE @EmployeeInsertType EmpInsertType

INSERT INTO @EmployeeInsertType VALUES (1, 'John', 'Homme', 50000) INSERER DANS @EmployeeInsertType VALUES (2, 'Praveen', 'Male', 60000) INSERT INTO @EmployeeInsertType 'Chitra', 'Femme', 45000) INSERER DANS @EmployeeInsertType VALUES (4, 'Mathy', 'Femme', 6600) INSERER DANS @EmployeeInsertType VALUES (5, 'Sam', 'Homme', 50000)

EXEC PRC_EmpInsertType @EmployeeInsertType

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

SELECT * FROM T_EMPLOYEES_DETAILS

SORTIE

1 John Male 50000

2 Praveen Homme 60000

3 Chitra Femme 45000

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

Pour obtenir le nombre de colonnes sur une table, utilisez ceci:

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

et pour passer une table à une fonction, essayez XML en tant que 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