T-SQL Dynamic SQL and Temp Tables
il semble que les # temptables créés en utilisant le SQL dynamique via la méthode EXECUTE string ont une portée différente et ne peuvent pas être référencés par des SQLs "fixes" dans la même procédure stockée. Cependant, je peux faire référence à une table temp créée par une déclaration SQL dynamique dans un SQL dynamique subséquent mais il semble qu'une procédure stockée ne renvoie pas un résultat de requête à un client appelant à moins que le SQL ne soit fixe.
un scénario simple à 2 tables: J'ai 2 tables. Appelons-les Commandes et les Éléments. Order a une clé primaire de OrderId et Items a une clé primaire de ItemId. Article.OrderId est la clé étrangère pour identifier l'ordre parent. Une commande peut contenir de 1 à N.
je veux être capable de fournir une interface de type "query builder" très flexible pour permettre à l'utilisateur de sélectionner les éléments qu'il veut voir. Les critères de filtrage peuvent être basés sur les champs de la table Items et/ou de la table commande parent. Si un article satisfait à la condition de filtre y compris et condition sur le ordre parent s'il en existe un, l'élément doit être retourné dans la requête ainsi que l'ordre parent.
Habituellement, je suppose, la plupart des gens construisent une jointure entre la table D'Item et les tables d'ordre parent. Je voudrais effectuer 2 requêtes distinctes à la place. Un pour retourner tous les articles admissibles et l'autre pour retourner toutes les commandes mères distinctes. La raison est double et vous pouvez ou non être d'accord.
la première raison est que je dois interroger tous les colonnes dans la table d'ordre de parent et si je faisais une seule requête pour joindre la table D'ordres à la table D'Articles, je serais repoeating l'information D'Ordre plusieurs fois. Comme il y a généralement un grand nombre d'articles par commande, j'aimerais éviter cela parce que cela entraînerait beaucoup plus de données étant transférées à un client gros. Au lieu de cela, comme mentionné, je voudrais retourner les deux tables individuellement dans un ensemble de données et d'utiliser les deux tables à l'intérieur pour peupler une commande personnalisée et les enfants articles client objet. (Je ne connais pas encore assez LINQ ou Entity Framework. Je construis mes objets à la main). La deuxième raison pour laquelle je voudrais retourner deux tables au lieu d'une est parce que j'ai déjà une autre procédure qui retourne tous les articles pour un OrderId donné avec L'ordre parent et je voudrais utiliser la même approche de 2 tables afin que je puisse réutiliser le code client pour peupler mon ordre personnalisé et les objets Client à partir des 2 datatables retournés.
Ce que j'espérais, à faire était de ceci:
construisez une chaîne SQL dynamique sur le Client qui relie la table orders à la table Items et filtres appropriés sur chaque table comme spécifié par le filtre personnalisé créé sur L'application Winform fat-client. La construction SQL sur le client aurait ressemblé à quelque chose comme ceci:
TempSQL = "
INSERT INTO #ItemsToQuery
OrderId, ItemsId
FROM
Orders, Items
WHERE
Orders.OrderID = Items.OrderId AND
/* Some unpredictable Order filters go here */
AND
/* Some unpredictable Items filters go here */
"
Ensuite, je voudrais appeler une procédure stockée,
CREATE PROCEDURE GetItemsAndOrders(@tempSql as text)
Execute (@tempSQL) --to create the #ItemsToQuery table
SELECT * FROM Items WHERE Items.ItemId IN (SELECT ItemId FROM #ItemsToQuery)
SELECT * FROM Orders WHERE Orders.OrderId IN (SELECT DISTINCT OrderId FROM #ItemsToQuery)
le problème avec cette approche est que la table # ItemsToQuery, puisqu'elle a été créée par dynamic SQL, est inaccessible à partir des 2 SQL statiques suivants et si je change les SQL statiques en dynamique, aucun résultat n'est transmis au client fat.
3 autour de vous viennent à l'esprit mais je suis de regarder pour une meilleure:
1) le premier SQL peut être exécuté en exécutant le SQL dynamiquement construit à partir du client. Les résultats pourraient ensuite être transmis sous forme de tableau à une version modifiée de la procédure stockée ci-dessus. Je suis familier avec la transmission de données de table comme XML. Si je l'ai fait, le proc stocké pourrait alors insérer les données dans une table temporaire en utilisant un SQL statique qui, parce qu'il a été créé par SQL dynamique, pourrait alors être interrogé sans problème. (Je pourrais aussi étudier en passant le nouveau type de Table param au lieu de XML.) Cependant, je voudrais éviter de passer des listes potentiellement importantes à une procédure stockée.
2) je pourrais effectuer toutes les requêtes du client.
La première serait quelque chose comme ceci:
SELECT Items.* FROM Orders, Items WHERE Order.OrderId = Items.OrderId AND (dynamic filter)
SELECT Orders.* FROM Orders, Items WHERE Order.OrderId = Items.OrderId AND (dynamic filter)
ceci me fournit toujours avec la possibilité de réutiliser mon code objet-population côté client parce que les commandes et les articles continuent d'être retournés dans deux tableaux différents.
j'ai un sentiment que je pourrais avoir quelques options à l'aide d'un Tableau de type de données dans ma procédure stockée, mais qui est aussi nouveau pour moi et j'apprécierais un peu d'alimentation à la cuillère.
si vous avez même scanné si loin dans ce que j'ai écrit, je suis surpris, mais si c'est le cas, j'apprécierais n'importe quelle de vos pensées sur la façon de accomplissez ce mieux.
5 réponses
vous devez d'abord créer votre table d'abord puis elle sera disponible dans le SQL dynamique.
Ceci fonctionne:
CREATE TABLE #temp3 (id INT)
EXEC ('insert #temp3 values(1)')
SELECT *
FROM #temp3
Cela ne fonctionne pas:
EXEC (
'create table #temp2 (id int)
insert #temp2 values(1)'
)
SELECT *
FROM #temp2
En d'autres termes:
- Créer une table temporaire
- Exécuter proc
- Sélectionner à partir de la table temporaire
voici un exemple complet:
CREATE PROC prTest2 @var VARCHAR(100)
AS
EXEC (@var)
GO
CREATE TABLE #temp (id INT)
EXEC prTest2 'insert #temp values(1)'
SELECT *
FROM #temp
1ère Méthode - de Joindre plusieurs instructions dans la même Dynamique Appel SQL:
DECLARE @DynamicQuery NVARCHAR(MAX)
SET @DynamicQuery = 'Select * into #temp from (select * from tablename) alias
select * from #temp
drop table #temp'
EXEC sp_executesql @DynamicQuery
2ème Méthode - Utiliser la Table temporaire:
(Attention, vous devez prendre des précautions supplémentaires de variable globale.)
IF OBJECT_ID('tempdb..##temp2') IS NULL
BEGIN
EXEC (
'create table ##temp2 (id int)
insert ##temp2 values(1)'
)
SELECT *
FROM ##temp2
END
N'oubliez pas de supprimer l'objet ##temp2 manuellement une fois que vous l'aurez fait:
IF (OBJECT_ID('tempdb..##temp2') IS NOT NULL)
BEGIN
DROP Table ##temp2
END
Note: N'utilisez pas cette méthode 2 Si vous ne connaissez pas la structure complète de la base de données.
je suggère fortement que vous faites une lecture http://www.sommarskog.se/arrays-in-sql-2005.html
personnellement j'aime l'approche de passer une liste de texte délimitée par une virgule, puis de l'analyser avec une fonction Texte à table et de s'y joindre. L'approche de la table temporaire peut fonctionner si vous la Créez en premier dans la connexion. Mais il se sent un peu messier.
les ensembles de résultats de dynamic SQL sont retournés au client. J'ai fait beaucoup de choses.
vous avez raison sur les problèmes de partage de données à travers des tables et variables temporaires et des choses comme ça entre le SQL et le SQL dynamique qu'il génère.
je pense qu'en essayant de faire fonctionner votre table d'Intérim, vous avez probablement des choses confuses, parce que vous pouvez certainement obtenir des données d'un SP qui exécute la dynamique SQL:
USE SandBox
GO
CREATE PROCEDURE usp_DynTest(@table_type AS VARCHAR(255))
AS
BEGIN
DECLARE @sql AS VARCHAR(MAX) = 'SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = ''' + @table_type + ''''
EXEC (@sql)
END
GO
EXEC usp_DynTest 'BASE TABLE'
GO
EXEC usp_DynTest 'VIEW'
GO
DROP PROCEDURE usp_DynTest
GO
Aussi:
USE SandBox
GO
CREATE PROCEDURE usp_DynTest(@table_type AS VARCHAR(255))
AS
BEGIN
DECLARE @sql AS VARCHAR(MAX) = 'SELECT * INTO #temp FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = ''' + @table_type + '''; SELECT * FROM #temp;'
EXEC (@sql)
END
GO
EXEC usp_DynTest 'BASE TABLE'
GO
EXEC usp_DynTest 'VIEW'
GO
DROP PROCEDURE usp_DynTest
GO
j'ai eu le même problème que @Muflix a mentionné. Quand vous ne savez pas que les colonnes sont retournées, ou qu'elles sont générées dynamiquement, ce que j'ai fait est de créer une table globale avec un id unique, puis de la supprimer quand j'en ai fini avec elle, cela ressemble à quelque chose comme ce qui est montré ci-dessous:
DECLARE @DynamicSQL NVARCHAR(MAX)
DECLARE @DynamicTable VARCHAR(255) = 'DynamicTempTable_' + CONVERT(VARCHAR(36), NEWID())
DECLARE @DynamicColumns NVARCHAR(MAX)
--Get "@DynamicColumns", example: SET @DynamicColumns = '[Column1], [Column2]'
SET @DynamicSQL = 'SELECT ' + @DynamicColumns + ' INTO [##' + @DynamicTable + ']' +
' FROM [dbo].[TableXYZ]'
EXEC sp_executesql @DynamicSQL
SET @DynamicSQL = 'IF OBJECT_ID(''tempdb..##' + @DynamicTable + ''' , ''U'') IS NOT NULL ' +
' BEGIN DROP TABLE [##' + @DynamicTable + '] END'
EXEC sp_executesql @DynamicSQL
Certainement pas la meilleure solution, mais cela semble fonctionner pour moi.