J'essaie de créer une procédure stockée simple qui interroge une table sys.tables.
CREATE PROCEDURE dbo.test
@dbname NVARCHAR(255),
@col NVARCHAR(255)
AS
SET NOCOUNT ON
SET XACT_ABORT ON
USE @dbname
SELECT TOP 100 *
FROM sys.tables
WHERE name = @col
GO
Cela ne semble pas fonctionner car je devrais mettre GO après USE @dbname mais cela met fin à la création de cette procédure? Comment est-ce que je peux mettre cette sélection de base de données dans cette procédure afin qu'un utilisateur puisse donner un nom de base de données en tant que paramètre pour ce proc?
Il y a au moins deux façons de le faire:
Utilisez une instruction case/switch (ou, dans mon exemple, un bloc naïf if..else
) pour comparer le paramètre à une liste de bases de données et exécutez une instruction using basée sur celle-ci. Cela présente l'avantage de limiter les bases de données auxquelles le proc peut accéder à un ensemble connu, plutôt que de permettre l'accès à tout ce dont le compte d'utilisateur dispose de droits.
declare @dbname nvarchar(255);
set @dbname = 'db1';
if @dbname = 'db1'
use db1;
else if @dbname = 'db2'
use db2;
SQL dynamique. Je déteste SQL dynamique. C'est un énorme trou de sécurité et presque jamais nécessaire. (pour mettre cela en perspective: en 17 ans de développement professionnel, je n’ai jamais eu à déployer de système de production utilisant du SQL dynamique). Si vous décidez de suivre cette voie, limitez le code appelé/créé dynamiquement à une instruction using et appelez un autre processus stocké pour effectuer le travail réel. Vous ne pouvez pas simplement exécuter dynamiquement l'instruction using
par elle-même en raison de règles d'étendue.
declare @sql nvarchar(255);
set @sql = 'using '+@dbname+'; exec mydatabase..do_work_proc;';
bien sûr, dans votre exemple, vous pouvez simplement faire
set @sql='select * from '+@dbname+'.sys.tables';
l'opérateur de résolution .<schema_name>.
vous permet d'interroger des objets d'une autre base de données sans utiliser d'instruction use
.
Dans de très rares circonstances, il peut être souhaitable d'autoriser un sproc à utiliser une base de données arbitraire. À mon avis, la seule utilisation acceptable est un générateur de code, ou une sorte d’outil d’analyse de base de données qui ne peut pas connaître les informations requises à l’avance.
Update Il s'avère que vous ne pouvez pas use
dans une procédure stockée, le SQL dynamique restant la seule méthode évidente. Pourtant, je envisagerais d'utiliser
select top 100 * from db_name.dbo.table_name
plutôt que use
.
Si vous utilisez EXEC @Var
(sans crochets - c'est-à-dire not EXEC (@Var)
), SQL Server recherche une procédure stockée correspondant au nom transmis dans @Var
. Vous pouvez utiliser le nommage en trois parties pour cela.
Si sys.sp_executesql
est appelé avec un nom en trois parties, le contexte est défini sur la base de données dans laquelle il est appelé.
Ainsi, vous pouvez le faire avec zéro risque d'injection SQL comme ci-dessous.
CREATE PROCEDURE dbo.test @dbname SYSNAME,
@col SYSNAME
AS
SET NOCOUNT, XACT_ABORT ON;
DECLARE @db_sp_executesql NVARCHAR(300) = QUOTENAME(@dbname) + '.sys.sp_executesql'
EXEC @db_sp_executesql N'
SELECT TOP 100 *
FROM sys.columns
WHERE name = @col',
N'@col sysname',
@col = @col
Même si ce qui précède n’était pas possible, je dirais toujours qu’il est parfaitement possible d’utiliser le code SQL dynamique de manière sûre, comme ici.
CREATE PROCEDURE dbo.test
@dbname SYSNAME, /*Use Correct Datatypes for identifiers*/
@col SYSNAME
AS
SET NOCOUNT ON
SET XACT_ABORT ON
IF DB_ID(@dbname) IS NULL /*Validate the database name exists*/
BEGIN
RAISERROR('Invalid Database Name passed',16,1)
RETURN
END
DECLARE @dynsql nvarchar(max)
/*Use QUOTENAME to correctly escape any special characters*/
SET @dynsql = N'USE '+ QUOTENAME(@dbname) + N'
SELECT TOP 100 *
FROM sys.tables
WHERE name = @col'
/*Use sp_executesql to leave the WHERE clause parameterised*/
EXEC sp_executesql @dynsql, N'@col sysname', @col = @col
La seule façon de faire est d'utiliser SQL dynamique , qui est puissant mais dangereux.
Une méthode différente pour atteindre le même objectif consiste à utiliser une procédure stockée système.
Voir Procédure (s) stockée (s) SQL - Exécution à partir de plusieurs bases de données .
Si le nom de la procédure commence par "sp_", se trouve dans la base de données principale et est marqué avec sys.sp_MS_MarkSystemObject, elle peut être appelée comme ceci:
Exec somedb.dbo.Test;
Exec anotherdb.dbo.Test;
Ou comme ceci:
Declare @Proc_Name sysname;
Set @Proc_Name = 'somedb.dbo.Test';
Exec @Proc_Name;
Les paramètres peuvent être utilisés aussi.
L'utilisation de cette technique nécessite d'utiliser le préfixe 'sp_' et de placer du code dans une base de données système. C'est votre choix si les compensations n'utilisent pas le SQL dynamique.