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