Insérer les résultats d'une procédure stockée dans une table temporaire

1579

Comment faire un SELECT * INTO [temp table] FROM [stored procedure]? Pas FROM [Table]et sans définir [temp table]?

Selecttoutes les données de BusinessLinedans tmpBusLinefonctionne bien.

select *
into tmpBusLine
from BusinessLine

J'essaye la même chose, mais en utilisant un stored procedurequi renvoie des données, ce n'est pas tout à fait la même chose.

select *
into tmpBusLine
from
exec getBusinessLineHistory '16 Mar 2009'

Message de sortie:

Msg 156, niveau 15, état 1, ligne 2 Syntaxe incorrecte près du mot clé 'exec'.

J'ai lu plusieurs exemples de création d'une table temporaire avec la même structure que la procédure stockée de sortie, ce qui fonctionne bien, mais ce serait bien de ne fournir aucune colonne.

Ferdeen
la source
22
Avec SELECT * INTO [TABLE NAME], vous connaissez les colonnes, car elles sont copiées à partir de la table d'origine. C'est exactement ce que je veux si je fais la même chose contre une procédure stockée.
Ferdeen
7
Je veux juste souligner que "sélectionner * dans tmpBusLine" crée une table permanente. Vous voulez probablement "sélectionner * dans #tmpBusLine". Je suis sûr que l'affiche originale l'a déjà découvert, mais cela pourrait aider d'autres personnes à trouver cet article, car il s'agit actuellement du meilleur résultat pour la recherche "sélectionner dans la table temporaire"
ktam33
2
Je ne sais pas si cela a été résolu ou non, mais la raison pour laquelle vous obtenez l'erreur est due au mot clé from.
Wes Palmer
9
Microsoft doit ajouter SELECT * INTO FROM EXEC! S'il vous plaît!
kjmerf

Réponses:

704

Vous pouvez utiliser OPENROWSET pour cela. Regarde. J'ai également inclus le code sp_configure pour activer les requêtes distribuées ad hoc, au cas où il ne serait pas déjà activé.

CREATE PROC getBusinessLineHistory
AS
BEGIN
    SELECT * FROM sys.databases
END
GO

sp_configure 'Show Advanced Options', 1
GO
RECONFIGURE
GO
sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO

SELECT * INTO #MyTempTable FROM OPENROWSET('SQLNCLI', 'Server=(local)\SQL2008;Trusted_Connection=yes;',
     'EXEC getBusinessLineHistory')

SELECT * FROM #MyTempTable
Aaron Alton
la source
28
C'est la bonne façon de procéder. OPENROWSET est à peu près le seul moyen de traiter les résultats d'une procédure stockée comme une expression de table.
Rob Farley
37
Cela semble un peu lourd à insérer dans une table. Beaucoup de configuration à faire. Également lorsque je l'ai essayé, j'ai obtenu "Msg 7357, niveau 16, état 2, ligne 1 ne peut pas traiter l'objet" EXEC GetPartyAnalysisData 146 ". Le fournisseur OLE DB" SQLNCLI "pour le serveur lié" (null) "indique que l'objet a aucune colonne ou l'utilisateur actuel n'a pas d'autorisations sur cet objet. " Vous devez donc définir un serveur lié ...
Ferdeen
10
Vous n'avez pas besoin d'un serveur lié, mais vous devez obtenir la bonne chaîne de connexion ... et également, spécifier le chemin d'accès complet à la procédure stockée, y compris le nom de la base de données et le propriétaire du sp.
MartW
18
eeeeew! une référence au même serveur? méchant. certainement plus un hack que d'avoir à créer manuellement la table temporaire
Tim Abell
23
Je conviens que c'est un hack et qu'il faut probablement l'éviter à moins que votre dos ne soit contre le mur. Changer le sp en fonction est probablement un meilleur angle à prendre. A MON HUMBLE AVIS.
Greg
624

Si vous souhaitez le faire sans d'abord déclarer la table temporaire, vous pouvez essayer de créer une fonction définie par l'utilisateur plutôt qu'une procédure stockée et faire en sorte que cette fonction définie par l'utilisateur renvoie une table. Alternativement, si vous souhaitez utiliser la procédure stockée, essayez quelque chose comme ceci:

CREATE TABLE #tmpBus
(
   COL1 INT,
   COL2 INT
)

INSERT INTO #tmpBus
Exec SpGetRecords 'Params'
Gavin
la source
171
Je pense que le but était de générer le schéma sans avoir à le déclarer explicitement.
Craig
5
Je serais intéressé de savoir quelle est la différence entre cela et la solution de @Aaron Alton ci-dessus. Celui-ci semble beaucoup plus simple, mais je ne suis pas sûr de toute autre implication.
funkymushroom
11
Cela fonctionnera mais si vous ajoutez des colonnes supplémentaires à la procédure stockée SpGetRecords, cela explosera.
Brady Holt
15
Vous obtenez seulement un INSERT INTO EXEC par pile d'appels. SpGetRecords et tout autre processus qu'il appelle ne peuvent pas utiliser cette stratégie dans leur propre code. Cela peut surprendre les responsables de SpGetRecords.
Matt Stephenson
33
Cela ne répond pas du tout à la question et je ne vois pas pourquoi c'est si voté? L'OP a explicitement déclaré "sans définir [table temporaire]" et votre toute première ligne a une instruction create table temporaire.
NickG
296

Dans SQL Server 2005, vous pouvez utiliser INSERT INTO ... EXECpour insérer le résultat d'une procédure stockée dans une table. À partir de la INSERTdocumentation de MSDN (pour SQL Server 2000, en fait):

--INSERT...EXECUTE procedure example
INSERT author_sales EXECUTE get_author_sales
Matt Hamilton
la source
122
Cela nécessite que les author_sales soient définis à l'avance. J'essaye d'éviter cela. Merci.
Ferdeen
5
C'est ce que je pensais. Si utile Insertion dans les tables tmp à la volée, mais pas si utile si vous avez besoin de connaître la structure de l'ensemble de données renvoyée par un proc stocké. Merci pour ton aide.
Ferdeen
3
Il y a un bon article ici msdn.microsoft.com/en-us/library/aa175921.aspx
Rich Andrews
4
Pour utiliser le même schéma, vous pouvez faire une copie comme suit: sélectionnez top 0 * dans tempTable depuis realTable ( stackoverflow.com/a/9206463/73794 )
Even Mien
@EvenMien J'ai été momentanément excité quand j'ai vu votre commentaire ... mais malheureusement cela ne fonctionne que si les résultats de votre proc reflètent réellement une vraie table :(
BVernon
193

Ceci est une réponse à une version légèrement modifiée de votre question. Si vous pouvez abandonner l'utilisation d'une procédure stockée pour une fonction définie par l'utilisateur, vous pouvez utiliser une fonction définie par l'utilisateur de table en ligne. Il s'agit essentiellement d'une procédure stockée (qui prendra des paramètres) qui renvoie une table en tant qu'ensemble de résultats; et donc se placera bien avec une instruction INTO.

Voici un bon article rapide à ce sujet et d'autres fonctions définies par l'utilisateur. Si vous avez toujours besoin de conduire pour une procédure stockée, vous pouvez envelopper la fonction définie par l'utilisateur de table en ligne avec une procédure stockée. La procédure stockée transmet simplement les paramètres lorsqu'elle appelle select * à partir de la fonction définie par l'utilisateur de la table en ligne.

Ainsi, par exemple, vous auriez une fonction définie par l'utilisateur de table en ligne pour obtenir une liste de clients pour une région particulière:

CREATE FUNCTION CustomersByRegion 
(  
    @RegionID int  
)
RETURNS TABLE 
AS
RETURN 
  SELECT *
  FROM customers
  WHERE RegionID = @RegionID
GO

Vous pouvez ensuite appeler cette fonction pour obtenir un résultat tel que:

SELECT * FROM CustomersbyRegion(1)

Ou pour faire un SELECT INTO:

SELECT * INTO CustList FROM CustomersbyRegion(1)

Si vous avez toujours besoin d'une procédure stockée, enveloppez la fonction comme telle:

CREATE PROCEDURE uspCustomersByRegion 
(  
    @regionID int  
)
AS
BEGIN
     SELECT * FROM CustomersbyRegion(@regionID);
END
GO

Je pense que c'est la méthode la plus «sans hack» pour obtenir les résultats souhaités. Il utilise les fonctionnalités existantes car elles étaient destinées à être utilisées sans complications supplémentaires. En imbriquant la fonction définie par l'utilisateur de table en ligne dans la procédure stockée, vous avez accès à la fonctionnalité de deux manières. Plus! Vous n'avez qu'un seul point de maintenance pour le code SQL réel.

L'utilisation de OPENROWSET a été suggérée, mais ce n'est pas pour cela que la fonction OPENROWSET était destinée à être utilisée (à partir de Books Online):

Comprend toutes les informations de connexion requises pour accéder aux données distantes à partir d'une source de données OLE DB. Cette méthode est une alternative à l'accès aux tables dans un serveur lié et est une méthode ponctuelle ponctuelle de connexion et d'accès aux données distantes à l'aide d'OLE DB. Pour des références plus fréquentes aux sources de données OLE DB, utilisez plutôt des serveurs liés.

L'utilisation de OPENROWSET fera le travail, mais cela entraînera des frais supplémentaires pour l'ouverture des connexions locales et le rassemblement des données. Il peut également ne pas être une option dans tous les cas car il nécessite une autorisation de requête ad hoc qui pose un risque pour la sécurité et peut donc ne pas être souhaitée. En outre, l'approche OPENROWSET empêchera l'utilisation de procédures stockées renvoyant plus d'un jeu de résultats. L'encapsulation de plusieurs fonctions définies par l'utilisateur de valeur de table en ligne dans une seule procédure stockée peut y parvenir.

Christian Loris
la source
4
+1 Une fonction table est une solution appropriée. Nous devons noter les inconvénients mineurs: la fonction table est un objet de base de données supplémentaire, et il peut être nécessaire de lui accorder des privilèges.
spencer7593
2
J'adore la solution. Un hic mineur que j'ai rencontré, c'est que ma table ne peut pas avoir l'ordre dans lequel elle pourrait l'être dans la procédure stockée. Eh bien, je vais régler ça
mrwaim
5
Un hic de plus - "Impossible d'accéder aux tables temporaires à partir d'une fonction"
mrwaim
7
La question initiale est de savoir comment créer une table temporaire avec les résultats de la sp. C'est un bon modèle, mais ne répond pas à cette question
Greg
16
greg, la première ligne de ma réponse indique "Ceci est une réponse à une version légèrement modifiée de votre question." Votre commentaire est redondant.
Christian Loris
131
EXEC sp_serveroption 'YOURSERVERNAME', 'DATA ACCESS', TRUE

SELECT  *
INTO    #tmpTable
FROM    OPENQUERY(YOURSERVERNAME, 'EXEC db.schema.sproc 1')
Quassnoi
la source
2
Obtenez un "Msg 208, Niveau 16, État 1, Ligne 1 Nom d'objet non valide 'tmpBusLine' (probablement car il n'est pas défini à l'avance).
Ferdeen
1
@Ferds: désolé, je n'ai pas compris votre demande au début. Mis à jour avec une autre solution.
Quassnoi
26
Excellente solution. Une mise en garde, vous devrez activer 'DATA ACCESS' sur votre serveur: EXEC sp_serveroption 'TheServerName', 'DATA ACCESS', TRUE
jcollum
8
Vous devrez également autoriser l'accès à distance au serveur. Cela aura des ramifications de sécurité.
BraveNewMath
7
Cela ne fonctionnera pas si la procédure stockée cible utilise des tables temporaires
Sal
125

Solution la plus simple:

CREATE TABLE #temp (...);

INSERT INTO #temp
EXEC [sproc];

Si vous ne connaissez pas le schéma, vous pouvez procéder comme suit. Veuillez noter que cette méthode comporte de graves risques pour la sécurité.

SELECT * 
INTO #temp
FROM OPENROWSET('SQLNCLI', 
                'Server=localhost;Trusted_Connection=yes;', 
                'EXEC [db].[schema].[sproc]')
Tigerjz32
la source
si je ne connais pas la colonne du jeu de résultats renvoyé alors ??? je veux dire que la colonne peut varier. alors comment insérer le résultat dans la table temporaire ???
SHEKHAR SHETE
Vous pouvez utiliser OPENQUERY mais ce n'est pas recommandé car il comporte des failles de sécurité.
Tigerjz32
1
"si je ne connais pas la colonne du jeu de résultats renvoyé alors" alors vous ne pouvez pas l'utiliser dans votre logique. Comment utiliserez-vous les données si vous ne savez pas de quoi il s'agit?
Adriaan Davel
@AdriaanDavel Je suis d'accord avec vous que vous devez toujours connaître vos données (meilleure pratique), mais ce qu'il pourrait dire, c'est qu'il y a des moments où le sproc renvoie des colonnes dynamiques et vous ne savez pas toujours à quoi ressemblera le schéma. Dans ce cas, vous pouvez utiliser OPENROWSET pour insérer et créer une table à la volée. Cependant, il y a des risques de sécurité évidents à faire cela ...
Tigerjz32
1
@nurettin parfois vous ne savez pas ce que la procédure stockée va retourner. Que se passe-t-il dans ce cas? Comment pourriez-vous créer une table temporaire (lorsque vous ne savez pas ce que la procédure stockée renverra) et y insérer à partir d'une procédure stockée?
Tigerjz32
106

Lorsque la procédure stockée renvoie un grand nombre de colonnes et que vous ne souhaitez pas "créer" manuellement une table temporaire pour contenir le résultat, j'ai trouvé que le moyen le plus simple consiste à accéder à la procédure stockée et à ajouter une clause "into" sur le dernière instruction select et ajoutez 1 = 0 à la clause where.

Exécutez la procédure stockée une fois et revenez en arrière et supprimez le code SQL que vous venez d'ajouter. Maintenant, vous aurez une table vide correspondant au résultat de la procédure stockée. Vous pouvez soit "table de script en tant que créer" pour une table temporaire ou simplement insérer directement dans cette table.

dotjoe
la source
9
+1, excellente suggestion. Vous pouvez même ajouter une variable facultative rapide au sproc appelée @TableCreate ou quelque chose de similaire qui, quand il n'est pas nul, suivez les étapes ci-dessus. Ne nécessite pas de changer le sproc puis une fois qu'il est installé.
Ian Roke
1
@dotjoe Faites-vous un SELECT INTO table temporaire et table de script à partir de la table temporaire? Les tables temporaires apparaissent dans tempdbmais je ne suis pas en mesure de faire un clic droit et de créer un script. Toute aide est appréciée.
DotnetDude
2
@DotNetDude vous pouvez select ... into new_tablecréer implicitement une table réelle.
dotjoe
Récupérez ensuite la définition approximative des colonnes dans le schéma de table vide; remplacer "..." à la fin par légitime TABLE_NAME:declare @s varchar(max)='';select @s=@s+','+COLUMN_NAME+' '+DATA_TYPE+isnull('('+case CHARACTER_MAXIMUM_LENGTH when -1 then 'max' else cast(CHARACTER_MAXIMUM_LENGTH as varchar(10))end+')','')from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='...';select @s
user423430
C'est la meilleure solution!
Lucas925
66
declare @temp table
(
    name varchar(255),
    field varchar(255),
    filename varchar(255),
    filegroup varchar(255),
    size varchar(255),
    maxsize varchar(255),
    growth varchar(255),
    usage varchar(255)
);
INSERT @temp  Exec sp_helpfile;
select * from @temp;
nitine
la source
3
ne répond pas à la question d'origine OP, faisant l'insertion sans définir d'abord la table temporaire.
t.durden
48

Votre procédure stockée récupère-t-elle uniquement les données ou les modifie-t-elle également? Si elle est utilisée uniquement pour la récupération, vous pouvez convertir la procédure stockée en fonction et utiliser les expressions de table communes (CTE) sans avoir à la déclarer, comme suit:

with temp as (
    select * from dbo.fnFunctionName(10, 20)
)
select col1, col2 from temp

Cependant, tout ce qui doit être récupéré du CTE doit être utilisé dans une seule instruction. Vous ne pouvez pas fairewith temp as ... et essayer de l'utiliser après quelques lignes de SQL. Vous pouvez avoir plusieurs CTE dans une seule instruction pour des requêtes plus complexes.

Par exemple,

with temp1020 as (
    select id from dbo.fnFunctionName(10, 20)
),
temp2030 as (
    select id from dbo.fnFunctionName(20, 30)
)
select * from temp1020 
where id not in (select id from temp2030)
Utilisateur SO
la source
1
Ce ne sont pas des tables temporaires, ce sont des CTE. technet.microsoft.com/en-us/library/…
yucer
5
Merci @yucer ... Je crois que je ne savais pas qu'ils s'appelaient CTE à l'époque :)
Utilisateur SO
48

Si la table de résultats de votre proc stocké est trop compliquée pour taper manuellement l'instruction "create table" et que vous ne pouvez pas utiliser OPENQUERY OU OPENROWSET, vous pouvez utiliser sp_help pour générer la liste des colonnes et des types de données pour vous. Une fois que vous avez la liste des colonnes, il suffit de la formater selon vos besoins.

Étape 1: Ajoutez "dans #temp" à la requête de sortie (par exemple "sélectionnez [...] dans #temp dans [...]").

Le moyen le plus simple consiste à modifier directement la requête de sortie dans le proc. si vous ne pouvez pas modifier le proc stocké, vous pouvez copier le contenu dans une nouvelle fenêtre de requête et y modifier la requête.

Étape 2: exécutez sp_help sur la table temporaire. (par exemple, "exec tempdb..sp_help #temp")

Après avoir créé la table temporaire, exécutez sp_help sur la table temporaire pour obtenir une liste des colonnes et des types de données, y compris la taille des champs varchar.

Étape 3: copiez les colonnes et types de données dans une instruction create table

J'ai une feuille Excel que j'utilise pour formater la sortie de sp_help dans une instruction "create table". Vous n'avez besoin de rien d'extraordinaire, copiez et collez simplement dans votre éditeur SQL. Utilisez les noms, tailles et types de colonne pour créer une instruction "Créer une table #x [...]" ou "déclarer @x table [...]" que vous pouvez utiliser pour INSÉRER les résultats de la procédure stockée.

Étape 4: insérer dans la table nouvellement créée

Vous allez maintenant avoir une requête qui ressemble aux autres solutions décrites dans ce fil.

DECLARE @t TABLE 
(
   --these columns were copied from sp_help
   COL1 INT,
   COL2 INT   
)

INSERT INTO @t 
Exec spMyProc 

Cette technique peut également être utilisée pour convertir une table temporaire ( #temp) en une variable de table ( @temp). Bien que cela puisse être plus d'étapes que de simplement écrire l' create tableinstruction vous-même, cela empêche les erreurs manuelles telles que les fautes de frappe et les incompatibilités de types de données dans les processus volumineux. Le débogage d'une faute de frappe peut prendre plus de temps que l'écriture de la requête en premier lieu.

FistOfFury
la source
37

Si l'OPENROWSET vous pose des problèmes, il existe un autre moyen à partir de 2012; utiliser sys.dm_exec_describe_first_result_set_for_object, comme mentionné ici: récupérer les noms de colonne et les types d'une procédure stockée?

Créez d'abord cette procédure stockée pour générer le SQL de la table temporaire:

CREATE PROCEDURE dbo.usp_GetStoredProcTableDefinition(
    @ProcedureName  nvarchar(128),
    @TableName      nvarchar(128),
    @SQL            nvarchar(max) OUTPUT
)
AS
SET @SQL = 'CREATE TABLE ' + @tableName + ' ('

SELECT @SQL = @SQL + '['+name +'] '+ system_type_name +''  + ','
        FROM sys.dm_exec_describe_first_result_set_for_object
        (
          OBJECT_ID(@ProcedureName), 
          NULL
        );

--Remove trailing comma
SET @SQL = SUBSTRING(@SQL,0,LEN(@SQL))    
SET @SQL =  @SQL +')'

Pour utiliser la procédure, appelez-la de la manière suivante:

DECLARE     @SQL    NVARCHAR(MAX)

exec dbo.usp_GetStoredProcTableDefinition
    @ProcedureName='dbo.usp_YourProcedure',
    @TableName='##YourGlobalTempTable',@SQL = @SQL OUTPUT

INSERT INTO ##YourGlobalTempTable
EXEC    [dbo].usp_YourProcedure

select * from ##YourGlobalTempTable

Notez que j'utilise une table temporaire globale. En effet, l'utilisation d'EXEC pour exécuter le SQL dynamique crée sa propre session, de sorte qu'une table temporaire ordinaire serait hors de portée de tout code ultérieur. Si une table temporaire globale pose problème, vous pouvez utiliser une table temporaire ordinaire, mais tout SQL subséquent devra être dynamique, c'est-à-dire également exécuté par l'instruction EXEC.

StuartQ
la source
4
Vous avez oublié de créer la table à partir de @SQL.
Trisped
32

Quassnoi m'a mis la plupart du chemin là-bas, mais une chose manquait:

**** J'avais besoin d'utiliser des paramètres dans la procédure stockée. ****

Et OPENQUERY ne permet pas que cela se produise:

J'ai donc trouvé un moyen de travailler le système et de ne pas avoir à rendre la définition de table si rigide, et à la redéfinir dans une autre procédure stockée (et bien sûr, prendre le risque qu'elle se casse)!

Oui, vous pouvez créer dynamiquement la définition de table renvoyée par la procédure stockée en utilisant l'instruction OPENQUERY avec de fausses variables (tant que NO RESULT SET renvoie le même nombre de champs et au même emplacement qu'un ensemble de données contenant de bonnes données).

Une fois la table créée, vous pouvez utiliser la procédure stockée exec dans la table temporaire toute la journée.


Et pour noter (comme indiqué ci-dessus), vous devez activer l'accès aux données,

EXEC sp_serveroption 'MYSERVERNAME', 'DATA ACCESS', TRUE

Code:

declare @locCompanyId varchar(8)
declare @locDateOne datetime
declare @locDateTwo datetime

set @locDateOne = '2/11/2010'
set @locDateTwo = getdate()

--Build temporary table (based on bogus variable values)
--because we just want the table definition and
--since openquery does not allow variable definitions...
--I am going to use bogus variables to get the table defintion.

select * into #tempCoAttendanceRpt20100211
FROM OPENQUERY(DBASESERVER,
  'EXEC DATABASE.dbo.Proc_MyStoredProc 1,"2/1/2010","2/15/2010 3:00 pm"')

set @locCompanyId = '7753231'

insert into #tempCoAttendanceRpt20100211
EXEC DATABASE.dbo.Proc_MyStoredProc @locCompanyId,@locDateOne,@locDateTwo

set @locCompanyId = '9872231'

insert into #tempCoAttendanceRpt20100211
EXEC DATABASE.dbo.Proc_MyStoredProc @locCompanyId,@locDateOne,@locDateTwo

select * from #tempCoAttendanceRpt20100211
drop table #tempCoAttendanceRpt20100211

Merci pour les informations qui ont été fournies à l'origine ... Oui, enfin, je n'ai pas à créer toutes ces définitions de table fausses (strictes) lorsque vous utilisez des données d'une autre procédure stockée ou base de données, et oui, vous pouvez également utiliser des paramètres.

Rechercher des balises de référence:

  • Procédure stockée SQL 2005 dans la table temporaire

  • openquery avec procédure stockée et variables 2005

  • openquery avec des variables

  • exécuter la procédure stockée dans la table temporaire

Mise à jour: cela ne fonctionnera pas avec les tables temporaires , j'ai donc dû recourir à la création manuelle de la table temporaire.

Avis décevant : cela ne fonctionnera pas avec les tables temporaires , http://www.sommarskog.se/share_data.html#OPENQUERY

Référence: La prochaine chose est de définir LOCALSERVER. Il peut ressembler à un mot-clé dans l'exemple, mais ce n'est en fait qu'un nom. Voici comment procéder:

sp_addlinkedserver @server = 'LOCALSERVER',  @srvproduct = '',
                   @provider = 'SQLOLEDB', @datasrc = @@servername

Pour créer un serveur lié, vous devez avoir l'autorisation ALTER ANY SERVER, ou être membre de l'un des rôles de serveur fixes sysadmin ou setupadmin.

OPENQUERY ouvre une nouvelle connexion à SQL Server. Cela a quelques implications:

La procédure que vous appelez avec OPENQUERY ne peut pas référencer des tables temporaires créées dans la connexion actuelle.

La nouvelle connexion a sa propre base de données par défaut (définie avec sp_addlinkedserver, la valeur par défaut est master), donc toute spécification d'objet doit inclure un nom de base de données.

Si vous avez une transaction ouverte et que vous maintenez des verrous lorsque vous appelez OPENQUERY, la procédure appelée ne peut pas accéder à ce que vous verrouillez. Autrement dit, si vous ne faites pas attention, vous vous bloquerez.

La connexion n'est pas gratuite, il y a donc une pénalité de performance.

Doug Lubey de Louisiane
la source
1
Si vous ne connaissez pas le nom de votre serveur, utilisez SELECT @@SERVERNAME. Vous pouvez également utiliserEXEC sp_serveroption @@SERVERNAME, 'DATA ACCESS', TRUE
Contango
24

Si vous avez la chance d'avoir SQL 2012 ou supérieur, vous pouvez utiliser dm_exec_describe_first_result_set_for_object

Je viens de modifier le sql fourni par gotqn. Merci gotqn.

Cela crée une table temporaire globale avec un nom identique à celui de la procédure. La table temporaire peut ensuite être utilisée selon les besoins. N'oubliez pas de le supprimer avant de le réexécuter.

    declare @procname nvarchar(255) = 'myProcedure',
            @sql nvarchar(max) 

    set @sql = 'create table ##' + @procname + ' ('
    begin
            select      @sql = @sql + '[' + r.name + '] ' +  r.system_type_name + ','
            from        sys.procedures AS p
            cross apply sys.dm_exec_describe_first_result_set_for_object(p.object_id, 0) AS r
            where       p.name = @procname

            set @sql = substring(@sql,1,len(@sql)-1) + ')'
            execute (@sql)
            execute('insert ##' + @procname + ' exec ' + @procname)
    end
Sandeep Gaadhe
la source
1
Excellent! Une seule remarque: utilisez sys.all_objectsplutôt que sys.proceduressi vous voulez le faire pour les procédures stockées intégrées.
Gert Arnold
2
Cela échouera également si le SP utilise des tables temporaires. (mais c'est assez pratique pour l'avoir comme proc dans votre arsenal)
Trubs
23

Ce proc stocké fait le travail:

CREATE PROCEDURE [dbo].[ExecIntoTable]
(
    @tableName          NVARCHAR(256),
    @storedProcWithParameters   NVARCHAR(MAX)
)
AS
BEGIN
    DECLARE @driver         VARCHAR(10)
    DECLARE @connectionString   NVARCHAR(600)
    DECLARE @sql            NVARCHAR(MAX)
    DECLARE @rowsetSql      NVARCHAR(MAX)

    SET @driver = '''SQLNCLI'''

    SET @connectionString = 
        '''server=' + 
            CAST(SERVERPROPERTY('ServerName') AS NVARCHAR(256)) + 
            COALESCE('\' + CAST(SERVERPROPERTY('InstanceName') AS NVARCHAR(256)), '') + 
        ';trusted_connection=yes'''

    SET @rowsetSql = '''EXEC ' + REPLACE(@storedProcWithParameters, '''', '''''') + ''''

    SET @sql = '
SELECT
    *
INTO 
    ' + @tableName + ' 
FROM
    OPENROWSET(' + @driver + ',' + @connectionString + ',' + @rowsetSql + ')'

    EXEC (@sql)
END
GO

C'est un léger remaniement de ceci: Insérez les résultats des procédures stockées dans le tableau afin que cela fonctionne réellement.

Si vous voulez qu'il fonctionne avec une table temporaire, vous devrez utiliser une ##GLOBALtable et la déposer ensuite.

satnhak
la source
17

Pour insérer le premier jeu d'enregistrements d'une procédure stockée dans une table temporaire, vous devez connaître les éléments suivants:

  1. seul le premier ensemble de lignes de la procédure stockée peut être inséré dans une table temporaire
  2. la procédure stockée ne doit pas exécuter d'instruction T-SQL dynamique (sp_executesql )
  3. vous devez d'abord définir la structure de la table temporaire

Ce qui précède peut sembler être une limitation, mais à mon humble avis, il est parfaitement logique - si vous utilisez, sp_executesqlvous pouvez retourner une fois deux colonnes et une fois dix, et si vous avez plusieurs jeux de résultats, vous ne pouvez pas les insérer dans plusieurs tableaux également - vous pouvez insérer un maximum dans deux tables dans une instruction T-SQL (en utilisantOUTPUT clause et aucun déclencheur).

Ainsi, le problème est principalement de savoir comment définir la structure de table temporaire avant d'exécuter l' EXEC ... INTO ...instruction.

Le premier fonctionne avec OBJECT_IDtandis que le second et le troisième fonctionnent également avec les requêtes ad hoc. Je préfère utiliser le DMV au lieu du sp car vous pouvez utiliser CROSS APPLYet créer les définitions de table temporaires pour plusieurs procédures en même temps.

SELECT p.name, r.* 
FROM sys.procedures AS p
CROSS APPLY sys.dm_exec_describe_first_result_set_for_object(p.object_id, 0) AS r;

Faites également attention au system_type_namedomaine car il peut être très utile. Il stocke la définition complète de la colonne. Par exemple:

smalldatetime
nvarchar(max)
uniqueidentifier
nvarchar(1000)
real
smalldatetime
decimal(18,2)

et vous pouvez l'utiliser directement dans la plupart des cas pour créer la définition de table.

Donc, je pense que dans la plupart des cas (si la procédure stockée correspond à certains critères), vous pouvez facilement créer des instructions dynamiques pour résoudre ces problèmes (créer la table temporaire, insérer le résultat de la procédure stockée, faire ce dont vous avez besoin avec les données) .


Notez que les objets ci-dessus ne parviennent pas à définir les premières données du jeu de résultats dans certains cas, comme lorsque des instructions T-SQL dynamiques sont exécutées ou que des tables temporaires sont utilisées dans la procédure stockée.

gotqn
la source
observation pratique sur les limitations: si vous devez insérer la sortie de certains sp (appelons-la SP_LEVEL_0) dans la table temporaire créée dynamiquement en utilisant l'approche ci-dessus dans un autre sp (permet de l'appeler SP_LEVEL_1), vous ne pouvez pas faire la même astuce pour la sortie de ce SP_LEVEL_1 vers une autre table temporaire dans SP_LEVEL_2
nahab
17
  1. Je crée une table avec le schéma et les données suivants.
  2. Créez une procédure stockée.
  3. Maintenant, je sais quel est le résultat de ma procédure, donc j'exécute la requête suivante.

    CREATE TABLE [dbo].[tblTestingTree](
        [Id] [int] IDENTITY(1,1) NOT NULL,
        [ParentId] [int] NULL,
        [IsLeft] [bit] NULL,
        [IsRight] [bit] NULL,
    CONSTRAINT [PK_tblTestingTree] PRIMARY KEY CLUSTERED
    (
        [Id] ASC
    ) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    GO
    SET IDENTITY_INSERT [dbo].[tblTestingTree] ON
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (1, NULL, NULL, NULL)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (2, 1, 1, NULL)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (3, 1, NULL, 1)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (4, 2, 1, NULL)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (5, 2, NULL, 1)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (6, 3, 1, NULL)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (7, 3, NULL, 1)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (8, 4, 1, NULL)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (9, 4, NULL, 1)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (10, 5, 1, NULL)
    
    SET IDENTITY_INSERT [dbo].[tblTestingTree] OFF

    VALEURS (10, 5, 1, NULL) SET IDENTITY_INSERT [dbo]. [TblTestingTree] On

    create procedure GetDate
    as
    begin
        select Id,ParentId from tblTestingTree
    end
    
    create table tbltemp
    (
        id int,
        ParentId int
    )
    insert into tbltemp
    exec GetDate
    
    select * from tbltemp;
Devansh
la source
15

Si la requête ne contient pas de paramètre, utilisez OpenQueryelse useOpenRowset .

La chose de base serait de créer un schéma selon la procédure stockée et de l'insérer dans cette table. par exemple:

DECLARE @abc TABLE(
                  RequisitionTypeSourceTypeID INT
                , RequisitionTypeID INT
                , RequisitionSourcingTypeID INT
                , AutoDistOverride INT
                , AllowManagerToWithdrawDistributedReq INT
                , ResumeRequired INT
                , WarnSupplierOnDNRReqSubmission  INT
                , MSPApprovalReqd INT
                , EnableMSPSupplierCounterOffer INT
                , RequireVendorToAcceptOffer INT
                , UseCertification INT
                , UseCompetency INT
                , RequireRequisitionTemplate INT
                , CreatedByID INT
                , CreatedDate DATE
                , ModifiedByID INT
                , ModifiedDate DATE
                , UseCandidateScheduledHours INT
                , WeekEndingDayOfWeekID INT
                , AllowAutoEnroll INT
                )
INSERT INTO @abc
EXEC [dbo].[usp_MySp] 726,3
SELECT * FROM @abc
Un résolveur de problèmes
la source
13

Code

CREATE TABLE #T1
(
    col1 INT NOT NULL,
    col2 NCHAR(50) NOT NULL,
    col3 TEXT NOT NULL,
    col4 DATETIME NULL,
    col5 NCHAR(50) NULL,
    col6 CHAR(2) NULL,
    col6 NCHAR(100) NULL,
    col7 INT NULL,
    col8 NCHAR(50) NULL,
    col9 DATETIME NULL,
    col10 DATETIME NULL
)

DECLARE @Para1 int
DECLARE @Para2 varchar(32)
DECLARE @Para3 varchar(100)
DECLARE @Para4 varchar(15)
DECLARE @Para5 varchar (12)
DECLARE @Para6 varchar(1)
DECLARE @Para7 varchar(1)


SET @Para1 = 1025
SET @Para2 = N'6as54fsd56f46sd4f65sd'
SET @Para3 = N'XXXX\UserName'
SET @Para4 = N'127.0.0.1'
SET @Para5 = N'XXXXXXX'
SET @Para6 = N'X'
SET @Para7 = N'X'

INSERT INTO #T1
(
    col1,
    col2,
    col3,
    col4,
    col5,
    col6,
    col6,
    col7,
    col8,
    col9,
    col10,
)
EXEC [dbo].[usp_ProcedureName] @Para1, @Para2, @Para3, @Para4, @Para5, @Para6, @Para6

J'espère que ça aide. Veuillez qualifier le cas échéant.

SoftwareARM
la source
11

J'ai trouvé Passing Arrays / DataTables dans des procédures stockées qui pourraient vous donner une autre idée de la façon dont vous pourriez résoudre votre problème.

Le lien suggère d'utiliser un paramètre de type Image pour passer à la procédure stockée. Ensuite, dans la procédure stockée, l'image est transformée en une variable de table contenant les données d'origine.

Il y a peut-être un moyen de l'utiliser avec une table temporaire.

kevchadders
la source
4
Cela n'est plus requis dans les versions Sql2008 et ultérieures avec l'introduction des paramètres de valeur de table . Maintenant, vous pouvez directement passer un ensemble de données .net ou un objet datatable à une procédure stockée SQL avec avoir à faire la conversion en octets comme mentionné dans le lien ci-dessus
EndlessSpace
10

J'ai rencontré le même problème et voici ce que j'ai fait pour cela à partir de la suggestion de Paul . La partie principale est ici d'utiliser NEWID()pour éviter que plusieurs utilisateurs exécutent les procédures / scripts de magasin en même temps, ce qui est pénible pour la table temporaire globale.

DECLARE @sql varchar(max) = '', 
@tmp_global_table varchar(255) = '##global_tmp_' + CONVERT(varchar(36), NEWID())
SET @sql = @sql + 'select * into [' + @tmp_global_table + '] from YOURTABLE'
EXEC(@sql)

EXEC('SELECT * FROM [' + @tmp_global_table + ']')
zhongxiao37
la source
9

Une autre méthode consiste à créer un type et à utiliser PIPELINED pour ensuite renvoyer votre objet. Cela se limite toutefois à connaître les colonnes. Mais il a l'avantage de pouvoir faire:

SELECT * 
FROM TABLE(CAST(f$my_functions('8028767') AS my_tab_type))
pierre
la source
Qu'est-ce que c'est? Il ne semble pas y avoir quoi que ce soit à voir avec SQL Server sur lequel porte cette question
Martin Smith
8

C'est un processus simple en 2 étapes: - créer une table temporaire - Insérer dans la table temporaire.

Code pour effectuer la même chose:

CREATE TABLE #tempTable (Column1 int, Column2 varchar(max));
INSERT INTO #tempTable 
EXEC [app].[Sproc_name]
@param1 = 1,
@param2 =2;
S Krishna
la source
Downvoted; très similaire aux réponses existantes.
iokevins
6

Après avoir cherché, j'ai trouvé un moyen de créer dynamiquement une table temporaire pour toute procédure stockée sans utiliser OPENROWSETou OPENQUERYutiliser un schéma générique de la définition de résultat de la procédure stockée, surtout lorsque vous n'êtes pas administrateur de base de données.

Le serveur SQL a un proc buit-in sp_describe_first_result_setqui peut vous fournir le schéma de n'importe quel jeu de résultats de procédures. J'ai créé une table de schéma à partir des résultats de cette procédure et défini manuellement tout le champ sur NULLABLE.

declare @procname varchar(100) = 'PROCEDURENAME' -- your procedure name
declare @param varchar(max) = '''2019-06-06''' -- your parameters 
declare @execstr nvarchar(max) = N'exec ' + @procname
declare @qry nvarchar(max)

-- Schema table to store the result from sp_describe_first_result_set.
create table #d
(is_hidden  bit  NULL, column_ordinal   int  NULL, name sysname NULL, is_nullable   bit  NULL, system_type_id   int  NULL, system_type_name nvarchar(256) NULL,
max_length  smallint  NULL, precision   tinyint  NULL,  scale   tinyint  NULL,  collation_name  sysname NULL, user_type_id  int NULL, user_type_database    sysname NULL,
user_type_schema    sysname NULL,user_type_name sysname NULL,assembly_qualified_type_name   nvarchar(4000),xml_collection_id    int NULL,xml_collection_database    sysname NULL,
xml_collection_schema   sysname NULL,xml_collection_name    sysname NULL,is_xml_document    bit  NULL,is_case_sensitive bit  NULL,is_fixed_length_clr_type  bit  NULL,
source_server   sysname NULL,source_database    sysname NULL,source_schema  sysname NULL,source_table   sysname NULL,source_column  sysname NULL,is_identity_column bit NULL,
is_part_of_unique_key   bit NULL,is_updateable  bit NULL,is_computed_column bit NULL,is_sparse_column_set   bit NULL,ordinal_in_order_by_list   smallint NULL,
order_by_list_length    smallint NULL,order_by_is_descending    smallint NULL,tds_type_id   int  NULL,tds_length    int  NULL,tds_collation_id  int NULL,
tds_collation_sort_id   tinyint NULL)


-- Get result set definition of your procedure
insert into #d
EXEC sp_describe_first_result_set @exestr, NULL, 0

-- Create a query to generate and populate a global temp table from above results
select 
@qry = 'Create table ##t(' +
stuff(  
    (select ',' + name + ' '+ system_type_name + ' NULL'
    from #d d For XML Path, TYPE)
    .value(N'.[1]', N'nvarchar(max)')
, 1,1,'')
+ ')

insert into ##t 
Exec '+@procname+' ' + @param

Exec sp_executesql @qry

-- Use below global temp table to query the data as you may
select * from ##t

-- **WARNING** Don't forget to drop the global temp table ##t.
--drop table ##t
drop table #d 

Développé et testé sur la version Sql Server - Microsoft SQL Server 2016 (RTM) - 13.0.1601.5 (Build 17134 :)

Vous pouvez modifier le schéma de votre version de serveur SQL que vous utilisez (si nécessaire).

vendettamit
la source
4

Si vous connaissez les paramètres transmis et si vous n'avez pas accès à make sp_configure, modifiez la procédure stockée avec ces paramètres et les mêmes peuvent être stockés dans une table globale ##.

lakshmivisalij
la source
3

Cela peut être fait dans SQL Server 2014+ à condition que la procédure stockée ne renvoie qu'une seule table. Si quelqu'un trouve un moyen de le faire pour plusieurs tables, j'aimerais en savoir plus.

DECLARE @storedProcname NVARCHAR(MAX) = ''
SET @storedProcname = 'myStoredProc'

DECLARE @strSQL AS VARCHAR(MAX) = 'CREATE TABLE myTableName '

SELECT @strSQL = @strSQL+STUFF((
SELECT ',' +name+' ' + system_type_name 
FROM sys.dm_exec_describe_first_result_set_for_object (OBJECT_ID(@storedProcname),0)
FOR XML PATH('')
),1,1,'(') + ')'

EXEC (@strSQL)

INSERT INTO myTableName

EXEC ('myStoredProc @param1=1, @param2=2')

SELECT * FROM myTableName

DROP TABLE myTableName

Cela extrait la définition de la table renvoyée des tables système et l'utilise pour créer la table temporaire pour vous. Vous pouvez ensuite le remplir à partir de la procédure stockée comme indiqué précédemment.

Il existe également des variantes de cela qui fonctionnent également avec Dynamic SQL.

Matthew Baker
la source
2

Quelques années de retard à la question, mais j'avais besoin de quelque chose comme ça pour une génération de code rapide et sale. Je pense que comme d'autres l'ont déclaré, il est simplement plus facile de définir la table temporaire à l'avance, mais cette méthode devrait fonctionner pour les requêtes de procédures stockées simples ou les instructions SQL.

Ce sera un peu compliqué, mais il emprunte aux contributeurs ici ainsi qu'à la solution de Paul White de DBA Stack Exchange Obtenez les types de colonnes de résultats de procédure stockée . Encore une fois, pour réitérer cette approche et cet exemple, il n'est pas conçu pour les processus dans un environnement multi-utilisateurs. Dans ce cas, la définition de la table est définie pendant une courte période dans une table temporaire globale pour référence par un processus de modèle de génération de code.

Je n'ai pas entièrement testé cela, il peut donc y avoir des mises en garde, vous pouvez donc aller sur le lien MSDN dans la réponse de Paul White. Cela s'applique à SQL 2012 et supérieur.

Utilisez d'abord la procédure stockée sp_describe_first_result_set qui ressemble à la description d'Oracle.

Cela évaluera la première ligne du premier jeu de résultats, donc si votre procédure stockée ou votre instruction renvoie plusieurs requêtes, elle ne décrira que le premier résultat.

J'ai créé un proc stocké pour décomposer les tâches qui renvoie un seul champ à sélectionner pour créer la définition de la table temporaire.

CREATE OR ALTER PROCEDURE [dbo].[sp_GetTableDefinitionFromSqlBatch_DescribeFirstResultSet]
(
     @sql NVARCHAR(4000)
    ,@table_name VARCHAR(100)
    ,@TableDefinition NVARCHAR(MAX) OUTPUT
)
AS
BEGIN
    SET NOCOUNT ON
    DECLARE @TempTableDefinition NVARCHAR(MAX)
    DECLARE @NewLine NVARCHAR(4) = CHAR(13)+CHAR(10)

    DECLARE @ResultDefinition TABLE (  --The View Definition per MSDN
      is_hidden         bit NOT NULL
    , column_ordinal    int NOT NULL
    , [name]            sysname NULL
    , is_nullable       bit NOT NULL
    , system_type_id    int NOT NULL
    , system_type_name  nvarchar(256) NULL
    , max_length        smallint NOT NULL
    , [precision]       tinyint NOT NULL
    , scale             tinyint NOT NULL
    , collation_name    sysname NULL    
    , user_type_id      int NULL
    , user_type_database    sysname NULL    
    , user_type_schema  sysname NULL
    , user_type_name    sysname NULL    
    , assembly_qualified_type_name      nvarchar(4000)  
    , xml_collection_id         int NULL
    , xml_collection_database   sysname NULL    
    , xml_collection_schema     sysname NULL    
    , xml_collection_name       sysname NULL
    , is_xml_document           bit NOT NULL            
    , is_case_sensitive         bit NOT NULL            
    , is_fixed_length_clr_type  bit NOT NULL    
    , source_server             sysname NULL            
    , source_database           sysname NULL
    , source_schema             sysname NULL
    , source_table              sysname NULL
    , source_column             sysname NULL
    , is_identity_column        bit NULL
    , is_part_of_unique_key     bit NULL
    , is_updateable             bit NULL
    , is_computed_column        bit NULL
    , is_sparse_column_set      bit NULL
    , ordinal_in_order_by_list  smallint NULL   
    , order_by_is_descending    smallint NULL   
    , order_by_list_length      smallint NULL
    , tds_type_id               int NOT NULL
    , tds_length                int NOT NULL
    , tds_collation_id          int NULL
    , tds_collation_sort_id     tinyint NULL
    )

    --Insert the description into table variable    
    INSERT @ResultDefinition
    EXEC sp_describe_first_result_set @sql

    --Now Build the string to create the table via union select statement
    ;WITH STMT AS (
        SELECT N'CREATE TABLE ' + @table_name + N' (' AS TextVal
        UNION ALL

        SELECT 
         CONCAT(
                CASE column_ordinal
                    WHEN 1 THEN '     ' ELSE '   , ' END  --Determines if comma should precede
                , QUOTENAME([name]) , '   ', system_type_name  -- Column Name and SQL TYPE
                ,CASE is_nullable 
                    WHEN 0 THEN '   NOT NULL' ELSE '   NULL' END --NULLABLE CONSTRAINT          
               ) AS TextVal
        FROM @ResultDefinition WHERE is_hidden = 0  -- May not be needed
        UNION ALL

        SELECT N');' + @NewLine
    ) 

    --Now Combine the rows to a single String
    SELECT @TempTableDefinition = COALESCE (@TempTableDefinition + @NewLine + TextVal, TextVal) FROM STMT

    SELECT @TableDefinition = @TempTableDefinition
END

L'énigme est que vous devez utiliser une table globale, mais vous devez la rendre suffisamment unique pour pouvoir la supprimer et la créer fréquemment sans vous soucier d'une collision.
Dans l'exemple, j'ai utilisé un Guid (FE264BF5_9C32_438F_8462_8A5DC8DEE49E) pour la variable globale remplaçant les tirets par un trait de soulignement

DECLARE @sql NVARCHAR(4000) = N'SELECT @@SERVERNAME as ServerName, GETDATE() AS Today;'
DECLARE @GlobalTempTable VARCHAR(100) = N'##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable'

--@sql can be a stored procedure name like dbo.foo without parameters

DECLARE @TableDef NVARCHAR(MAX)

DROP TABLE IF EXISTS #MyTempTable
DROP TABLE IF EXISTS ##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable

EXEC [dbo].[sp_GetTableDefinitionFromSqlBatch_DescribeFirstResultSet] 
    @sql, @GlobalTempTable, @TableDef OUTPUT

--Creates the global table ##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable
EXEC sp_executesql @TableDef 

--Now Call the stored procedure, SQL Statement with Params etc.
INSERT ##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable
    EXEC sp_executesql @sql 

--Select the results into your undefined Temp Table from the Global Table
SELECT * 
INTO #MyTempTable
FROM ##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable

SELECT * FROM #MyTempTable

DROP TABLE IF EXISTS #MyTempTable
DROP TABLE IF EXISTS ##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable

Encore une fois, je ne l'ai testé qu'avec des requêtes de procédure stockée simples et des requêtes simples, de sorte que votre kilométrage peut varier. J'espère que cela aide quelqu'un.

Charles Byrne
la source
1

Eh bien, vous devez créer une table temporaire, mais elle n'a pas besoin d'avoir le bon schéma .... J'ai créé une procédure stockée qui modifie une table temporaire existante afin qu'elle ait les colonnes requises avec les bonnes données type et ordre (suppression de toutes les colonnes existantes, ajout de nouvelles colonnes):

GO
create procedure #TempTableForSP(@tableId int, @procedureId int)  
as   
begin  
    declare @tableName varchar(max) =  (select name  
                                        from tempdb.sys.tables 
                                        where object_id = @tableId
                                        );    
    declare @tsql nvarchar(max);    
    declare @tempId nvarchar(max) = newid();      
    set @tsql = '    
    declare @drop nvarchar(max) = (select  ''alter table tempdb.dbo.' + @tableName 
            +  ' drop column ''  + quotename(c.name) + '';''+ char(10)  
                                   from tempdb.sys.columns c   
                                   where c.object_id =  ' + 
                                         cast(@tableId as varchar(max)) + '  
                                   for xml path('''')  
                                  )    
    alter table tempdb.dbo.' + @tableName + ' add ' + QUOTENAME(@tempId) + ' int;
    exec sp_executeSQL @drop;    
    declare @add nvarchar(max) = (    
                                select ''alter table ' + @tableName 
                                      + ' add '' + name 
                                      + '' '' + system_type_name 
                           + case when d.is_nullable=1 then '' null '' else '''' end 
                                      + char(10)   
                              from sys.dm_exec_describe_first_result_set_for_object(' 
                               + cast(@procedureId as varchar(max)) + ', 0) d  
                                order by column_ordinal  
                                for xml path(''''))    

    execute sp_executeSQL  @add;    
    alter table '  + @tableName + ' drop column ' + quotename(@tempId) + '  ';      
    execute sp_executeSQL @tsql;  
end         
GO

create table #exampleTable (pk int);

declare @tableId int = object_Id('tempdb..#exampleTable')
declare @procedureId int = object_id('examplestoredProcedure')

exec #TempTableForSP @tableId, @procedureId;

insert into #exampleTable
exec examplestoredProcedure

Notez que cela ne fonctionnera pas si sys.dm_exec_describe_first_result_set_for_object ne peut pas déterminer les résultats de la procédure stockée (par exemple s'il utilise une table temporaire).

jmoreno
la source
0

Si vous laissez SQL dynamique créer une table temporaire, cette table appartient à la connexion Dynamic SQL, par opposition à la connexion à partir de laquelle votre procédure stockée est appelée.

DECLARE @COMMA_SEPARATED_KEYS varchar(MAX);
DROP TABLE IF EXISTS KV;
CREATE TABLE KV (id_person int, mykey varchar(30), myvalue int);
INSERT INTO KV VALUES
(1, 'age', 16),
(1, 'weight', 63),
(1, 'height', 175),
(2, 'age', 26),
(2, 'weight', 83),
(2, 'height', 185);
WITH cte(mykey) AS (
    SELECT DISTINCT mykey FROM KV
) 
SELECT @COMMA_SEPARATED_KEYS=STRING_AGG(mykey,',') FROM cte;
SELECT @COMMA_SEPARATED_KEYS AS keys;

entrez la description de l'image ici

DECLARE @ExecuteExpression varchar(MAX);

DROP TABLE IF EXISTS #Pivoted;

SET @ExecuteExpression = N'
SELECT * 
INTO #Pivoted
FROM
(
    SELECT
        mykey,
        myvalue,
        id_person
    FROM KV
) AS t
PIVOT(
    MAX(t.myvalue) 
    FOR mykey IN (COMMA_SEPARATED_KEYS)
) AS pivot_table;
';

SET @ExecuteExpression = REPLACE(@ExecuteExpression, 'COMMA_SEPARATED_KEYS', @COMMA_SEPARATED_KEYS);

EXEC(@ExecuteExpression);

SELECT * FROM #Pivoted;

Msg 208, niveau 16, état 0 Nom d'objet non valide '#Pivoted'. En effet, #Pivoted appartient à la connexion SQL dynamique. Donc, la dernière instruction

SELECT * FROM #Pivoted

échoue.

Une façon de ne pas faire face à ce problème est de vous assurer que toutes les références à #Pivoted sont faites à l'intérieur de la requête dynamique elle-même:

DECLARE @COMMA_SEPARATED_KEYS varchar(MAX);
DROP TABLE IF EXISTS KV;
CREATE TABLE KV (id_person int, mykey varchar(30), myvalue int);
INSERT INTO KV VALUES
(1, 'age', 16),
(1, 'weight', 63),
(1, 'height', 175),
(2, 'age', 26),
(2, 'weight', 83),
(2, 'height', 185);
WITH cte(mykey) AS (
    SELECT DISTINCT mykey FROM KV
) 
SELECT @COMMA_SEPARATED_KEYS=STRING_AGG(mykey,',') FROM cte;
SELECT @COMMA_SEPARATED_KEYS AS keys;


DECLARE @ExecuteExpression varchar(MAX);

DROP TABLE IF EXISTS #Pivoted;

SET @ExecuteExpression = N'
SELECT * 
INTO #Pivoted
FROM
(
    SELECT
        mykey,
        myvalue,
        id_person
    FROM KV
) AS t
PIVOT(
    MAX(t.myvalue) 
    FOR mykey IN (COMMA_SEPARATED_KEYS)
) AS pivot_table;
SELECT * FROM #Pivoted;
';

SET @ExecuteExpression = REPLACE(@ExecuteExpression, 'COMMA_SEPARATED_KEYS', @COMMA_SEPARATED_KEYS);

EXEC(@ExecuteExpression);

entrez la description de l'image ici

Ludovic Aubert
la source
-5

Je ferais ce qui suit

  1. Créez (convertissez SP en) un UDF (valeur de table UDF).

  2. select * into #tmpBusLine from dbo.UDF_getBusinessLineHistory '16 Mar 2009'

Hlin
la source
2
Il pourrait y avoir des obstacles à faire votre premier pas. Par exemple, si le SP d'origine utilise des tables temporaires. Les FDU ne peuvent pas utiliser de tables temporaires.
yucer