Is there a way to specify table name as a string?

2020-02-12 05:50发布

问题:

Let us say I have a query like this:

SELECT * FROM 
(
  SELECT * FROM 
  (
    SELECT * FROM DB.dbo.Table
  )
  INNER JOIN DB.dbo.Table ON ...

I am running this query multiple times with different tables by manually changing the string everywhere. I tried declaring the following:

DECLARE @tablename AS VARCHAR(255)
SET @tablename = 'DB.dbo.Table'

But this does not seem to work as it throws me an error saying that I need to declare @tablename as a table variable before I can use it. How do I templatize my table name and if that is possible, will Intellisense still work?

回答1:

You can wrap it in an EXEC statement like this:

declare @my_tablename nvarchar(100) = 'mytable';
exec('
SELECT * FROM 
(
  SELECT * FROM 
  (
    SELECT * FROM ' + @my_tablename + '
  )
  INNER JOIN ' + @my_tablename + ' ON ...'
);

But no, intellisense will not work in that scenario.

If you know what your output will look like in advance, then you can declare a temp table to hold the results, and then you can access that without EXEC. You will have intellisense on the temp table.

For example:

  --this must match whatever your SELECT is going to return
  CREATE TABLE #results(
    FIELD1 INT
   ,FIELD2 NVARCHAR(100)
   ,FIELD3 BIT
   );

EXEC('
  INSERT INTO #results(field1,field2,field3)
  SELECT FIELD1,FIELD2,FIELD3 FROM ' + @my_tablename
);

select * from #results  --you will have intellisense on #results


回答2:

No. Just as you cannot specify the function name in your c# program as a string. T-SQL compilation is supposed to come up with a precise access plan, meaning what indexes to open and use to satisfy the query. It would be impossible to come up with a plan for a 'string', just as it would be impossible in C# to generate the code to invoke a 'string' as a method.

The solution is dynamic SQL:

declare @sql NVARCHAR(MAX) = N'SELECT ... FROM ' +
   quotename(@dbname) + N'.' + quotename(@schema) + N'.' + quotename(@table) + 
   N' WHERE ...';
exec sp_executesql @sql;

... just as in C# you would use reflection to do dynamic runtime invocation.

For more info see The Curse and Blessings of Dynamic SQL.

PS. the split of @tablename into components and the use of QUOTENAME is an absolute must, it guards agaisnt SQL Injection. Use PARSENAME to do the split for you.



回答3:

You use dynamic SQL. Not sure why you need so many nested SELECTs but it would be something like:

DECLARE @sql NVARCHAR(MAX) = N'SELECT ... FROM ' + @tablename + '...';
EXEC sp_executeSQL @sql;

However please be aware of SQL injection. And no, IntelliSense has no ability to parse a string for object names (or to even know while editing what object name is going to be there).